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