Skip to content

Instantly share code, notes, and snippets.

@samuelcolvin
Created April 11, 2015 17:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samuelcolvin/a85b79113bda2026089f to your computer and use it in GitHub Desktop.
Save samuelcolvin/a85b79113bda2026089f to your computer and use it in GitHub Desktop.
datetime util
#! /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