-
-
Save samuelcolvin/a85b79113bda2026089f to your computer and use it in GitHub Desktop.
datetime util
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/python | |
import argparse | |
import subprocess | |
import os | |
import datetime | |
import re | |
from dateutil.parser import parse as du_parse | |
from dateutil import tz | |
from termcolor import colored | |
import pytz | |
arg_parser = argparse.ArgumentParser(description="""dt | |
Date time and timezone tools. | |
""", formatter_class=argparse.RawTextHelpFormatter) | |
STAMP = 'stamp' | |
COMPARE = 'compare' | |
TO_ZONE = 'tozone' | |
arg_parser.add_argument('action', | |
choices=(STAMP, COMPARE, TO_ZONE), | |
help='directory to search') | |
arg_parser.add_argument('primary_value', | |
nargs=1, | |
help='unix time stamp if stamp, else times to compare') | |
arg_parser.add_argument('values', | |
nargs='*', | |
help='times to compare or timezone') | |
arg_parser.add_argument('--verbose', | |
action='store_true', | |
default=False) | |
def strfdelta(tdelta, fmt): | |
d = {"d": tdelta.days} | |
d["H"], rem = divmod(tdelta.seconds, 3600) | |
d["M"], d["S"] = divmod(rem, 60) | |
for L in ('d', 'H', 'M'): | |
fmt = fmt.replace('%' + L, '%(' + L + ')d') | |
fmt = fmt.replace('%S', '%(S)0.1f') | |
return fmt % d | |
def get_tzd(): | |
tz_str = """-12 Y | |
-11 X NUT SST | |
-10 W CKT HAST HST TAHT TKT | |
-9 V AKST GAMT GIT HADT HNY | |
-8 U AKDT CIST HAY HNP PST PT | |
-7 T HAP HNR MST PDT | |
-6 S CST EAST GALT HAR HNC MDT | |
-5 R CDT COT EASST ECT EST ET HAC HNE PET | |
-4 Q AST BOT CLT COST EDT FKT GYT HAE HNA PYT | |
-3 P ADT ART BRT CLST FKST GFT HAA PMST PYST SRT UYT WGT | |
-2 O BRST FNT PMDT UYST WGST | |
-1 N AZOT CVT EGT | |
0 Z EGST GMT UTC WET WT | |
1 A CET DFT WAT WEDT WEST BST | |
2 B CAT CEDT CEST EET SAST WAST | |
3 C EAT EEDT EEST IDT MSK | |
4 D AMT AZT GET GST KUYT MSD MUT RET SAMT SCT | |
5 E AMST AQTT AZST HMT MAWT MVT PKT TFT TJT TMT UZT YEKT | |
6 F ALMT BIOT BTT IOT KGT NOVT OMST YEKST | |
7 G CXT DAVT HOVT ICT KRAT NOVST OMSST THA WIB | |
8 H ACT AWST BDT BNT CAST HKT IRKT KRAST MYT PHT SGT ULAT WITA WST | |
9 I AWDT IRKST JST KST PWT TLT WDT WIT YAKT | |
10 K AEST ChST PGT VLAT YAKST YAPT | |
11 L AEDT LHDT MAGT NCT PONT SBT VLAST VUT | |
12 M ANAST ANAT FJT GILT MAGST MHT NZST PETST PETT TVT WFT | |
13 FJST NZDT | |
11.5 NFT | |
10.5 ACDT LHST | |
9.5 ACST | |
6.5 CCT MMT | |
5.75 NPT | |
5.5 SLT | |
4.5 AFT IRDT | |
3.5 IRST | |
-2.5 HAT NDT | |
-3.5 HNT NST NT | |
-4.5 HLV VET | |
-9.5 MART MIT""" | |
tzd = [] | |
for tz_descr in map(str.split, tz_str.split('\n')): | |
tz_offset = int(float(tz_descr[0]) * 3600) | |
for tz_code in tz_descr[1:]: | |
tzd.append((tz_code, tz_offset)) | |
tzd.sort(key=lambda x: -len(x[0])) | |
return tzd | |
def extend_format_list(formats, extend_with): | |
for find, rep in extend_with: | |
for i, ft in enumerate(formats): | |
if find in ft: | |
formats.insert(i + 1, ft.replace(find, rep)) | |
def get_zone(zone_str, all_tz, verbose=False): | |
tzinfo = None | |
orig_zone_str = zone_str | |
# upper case is required for finding tz and replacing and lowercase is never required. | |
zone_str = zone_str.upper() | |
tz_name = '?' | |
m = re.search('UTC([0-9+-\.]+)', zone_str) | |
if m: | |
offset_str = m.groups()[0] | |
offset = float(offset_str) | |
tz_name = 'UTC%+.1f' % offset | |
offset = int(offset*3600) | |
tzinfo = tz.tzoffset(tz_name, offset) | |
zone_str = zone_str.replace('UTC' + offset_str, '') | |
if tzinfo is None and len(zone_str) > 4: | |
for tz_name in pytz.all_timezones: | |
if tz_name.upper() in zone_str or zone_str in tz_name.upper(): | |
tzinfo = tz.gettz(tz_name) | |
zone_str = zone_str.replace(tz_name.upper(), '') | |
break | |
if tzinfo is None and not (all_tz and len(zone_str) > 4): | |
for tz_name, offset in get_tzd(): | |
if tz_name in zone_str: | |
tzinfo = tz.tzoffset(tz_name, offset) | |
zone_str = zone_str.replace(tz_name, '') | |
break | |
if verbose and tzinfo: | |
print '"%s" resolved to %s' % (colored(orig_zone_str, 'blue'), colored(tz_name, 'green')) | |
return tzinfo, zone_str | |
def parse_datetime(dt_str, verbose=False): | |
try: | |
return datetime.datetime.fromtimestamp(float(dt_str)) | |
except ValueError: | |
pass | |
tzinfo, dt_str = get_zone(dt_str, all_tz=False, verbose=verbose) | |
dt_str = dt_str.strip(' _\t-') | |
if verbose: | |
print 'trying to parse "%s"' % dt_str | |
today = datetime.date.today() | |
date_str = today.strftime('%d/%m/%Y ') | |
year_str = today.strftime('%Y ') | |
prepends = ['', date_str, year_str] | |
if len(dt_str) < 12: | |
prepends = [date_str, '', year_str] | |
date_formats = ['%d/%m/%Y', '%Y/%m/%d', '%m/%d/%Y', '%B/%d/%Y', '%d/%B/%Y'] | |
extend_format_list(date_formats, [('%B', '%b'), ('%Y', '%y'), ('/', ' '), ('/', '-')]) | |
time_formats = ['%H', '%H:%M', '%I:%M%p', '%I:%M:p', '%H:%M:%S', '%H %z', '%H:%M %z', '%H:%M:%S %z'] | |
extend_format_list(time_formats, [('%H', '%I%p'), (':', ' '), (':', '.')]) | |
dt_spliters = [' ', '', 'T'] | |
try_count = 0 | |
for prepend in prepends: | |
for dt_spliter in dt_spliters: | |
for date_format in date_formats: | |
for time_format in time_formats: | |
format_string = date_format + dt_spliter + time_format | |
dt_try = prepend + dt_str | |
try_count += 1 | |
try: | |
dt = datetime.datetime.strptime(dt_try, format_string) | |
except ValueError: | |
pass | |
else: | |
dt = dt.replace(tzinfo=tzinfo) | |
if verbose: | |
print 'found datetime with format %s after %d tries' % (format_string, try_count) | |
return dt | |
if verbose: | |
print 'no datetime found after trying %d possible formats' % try_count | |
DT_FMT = '%Y-%m-%d %H:%M:%S %Z %z' | |
def switch_zone(from_dt, to_dt=None, zone=None): | |
to_dt = to_dt or from_dt.replace(tzinfo=zone or tz.tzlocal()) | |
in_new_zone = from_dt.astimezone(to_dt.tzinfo) | |
print colored(from_dt.strftime(DT_FMT), 'green'), 'in', colored(to_dt.tzname(), 'red'), 'is', | |
print colored(in_new_zone.strftime(DT_FMT), 'green') | |
return in_new_zone | |
def process(args): | |
primary_value = args.primary_value[0] | |
if args.action == STAMP: | |
time_stamp = primary_value | |
print 'Time stamp: %s' % time_stamp | |
time_stamp = int(time_stamp) | |
print 'Time: %s' % colored(datetime.datetime.fromtimestamp(time_stamp).strftime(DT_FMT), 'green') | |
return time_stamp | |
main_dt = parse_datetime(primary_value, args.verbose) | |
if main_dt is None: | |
print 'Error parsing time "%s"' % main_dt | |
return | |
else: | |
if args.verbose: | |
print '"%s" > %s' % (primary_value, main_dt.strftime(DT_FMT)) | |
if args.action == COMPARE: | |
dts = [] | |
for dt_str in args.values: | |
dt = parse_datetime(dt_str, args.verbose) | |
if dt is None: | |
print 'Error parsing time "%s"' % dt | |
return | |
else: | |
if args.verbose: | |
print '"%s" > %s' % (dt_str, dt.strftime(DT_FMT)) | |
dts.append(dt) | |
if len(dts) > 0: | |
for dt in dts: | |
diff = abs(main_dt - dt) | |
before_after = 'after' if main_dt > dt else 'before' | |
print colored(main_dt.strftime(DT_FMT), 'green'), before_after, colored(dt.strftime(DT_FMT), 'green'), | |
print 'by', colored(strfdelta(diff, '%d days, %Hh %Mm'), 'blue') | |
switch_zone(dt, main_dt) | |
else: | |
switch_zone(main_dt) | |
elif args.action == TO_ZONE: | |
zone_str = '_'.join(args.values) | |
zone = None | |
if zone_str != '': | |
zone, _ = get_zone(zone_str, all_tz=True, verbose=True) | |
if zone is None: | |
print 'Error parsing zone "%s"' % zone_str | |
return | |
switch_zone(main_dt, zone=zone) | |
else: | |
raise Exception('Unknown action %r' % args.action) | |
if __name__ == '__main__': | |
process(arg_parser.parse_args()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment