1#!/usr/local/bin/python3.8 2# vim:fileencoding=utf-8 3# License: GPLv3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> 4 5 6from datetime import datetime 7 8from dateutil.tz import tzlocal, tzutc, tzoffset 9from calibre_extensions import speedup 10 11 12class SafeLocalTimeZone(tzlocal): 13 14 def _isdst(self, dt): 15 # This method in tzlocal raises ValueError if dt is out of range (in 16 # older versions of dateutil) 17 # In such cases, just assume that dt is not DST. 18 try: 19 return super()._isdst(dt) 20 except Exception: 21 pass 22 return False 23 24 def _naive_is_dst(self, dt): 25 # This method in tzlocal raises ValueError if dt is out of range (in 26 # newer versions of dateutil) 27 # In such cases, just assume that dt is not DST. 28 try: 29 return super()._naive_is_dst(dt) 30 except Exception: 31 pass 32 return False 33 34 35utc_tz = tzutc() 36local_tz = SafeLocalTimeZone() 37del tzutc, tzlocal 38UNDEFINED_DATE = datetime(101,1,1, tzinfo=utc_tz) 39 40 41def parse_iso8601(date_string, assume_utc=False, as_utc=True, require_aware=False): 42 if not date_string: 43 return UNDEFINED_DATE 44 dt, aware, tzseconds = speedup.parse_iso8601(date_string) 45 tz = utc_tz if assume_utc else local_tz 46 if aware: # timezone was specified 47 if tzseconds == 0: 48 tz = utc_tz 49 else: 50 sign = '-' if tzseconds < 0 else '+' 51 description = "%s%02d:%02d" % (sign, abs(tzseconds) // 3600, (abs(tzseconds) % 3600) // 60) 52 tz = tzoffset(description, tzseconds) 53 elif require_aware: 54 raise ValueError('{} does not specify a time zone'.format(date_string)) 55 dt = dt.replace(tzinfo=tz) 56 if as_utc and tz is utc_tz: 57 return dt 58 return dt.astimezone(utc_tz if as_utc else local_tz) 59 60 61if __name__ == '__main__': 62 import sys 63 print(parse_iso8601(sys.argv[-1])) 64