blob: f94b483101b1a2008dd30577a5775490b38d1806 [file] [log] [blame]
Alexander Belopolsky5d0c5982016-07-22 18:47:04 -04001import sys
2import os
3import struct
4from array import array
5from collections import namedtuple
6from datetime import datetime, timedelta
7
8ttinfo = namedtuple('ttinfo', ['tt_gmtoff', 'tt_isdst', 'tt_abbrind'])
9
10class TZInfo:
11 def __init__(self, transitions, type_indices, ttis, abbrs):
12 self.transitions = transitions
13 self.type_indices = type_indices
14 self.ttis = ttis
15 self.abbrs = abbrs
16
17 @classmethod
18 def fromfile(cls, fileobj):
19 if fileobj.read(4).decode() != "TZif":
20 raise ValueError("not a zoneinfo file")
21 fileobj.seek(20)
22 header = fileobj.read(24)
23 tzh = (tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
24 tzh_timecnt, tzh_typecnt, tzh_charcnt) = struct.unpack(">6l", header)
25 transitions = array('i')
26 transitions.fromfile(fileobj, tzh_timecnt)
27 if sys.byteorder != 'big':
28 transitions.byteswap()
29
30 type_indices = array('B')
31 type_indices.fromfile(fileobj, tzh_timecnt)
32
33 ttis = []
34 for i in range(tzh_typecnt):
35 ttis.append(ttinfo._make(struct.unpack(">lbb", fileobj.read(6))))
36
37 abbrs = fileobj.read(tzh_charcnt)
38
39 self = cls(transitions, type_indices, ttis, abbrs)
40 self.tzh = tzh
41
42 return self
43
44 def dump(self, stream, start=None, end=None):
45 for j, (trans, i) in enumerate(zip(self.transitions, self.type_indices)):
46 utc = datetime.utcfromtimestamp(trans)
47 tti = self.ttis[i]
48 lmt = datetime.utcfromtimestamp(trans + tti.tt_gmtoff)
49 abbrind = tti.tt_abbrind
50 abbr = self.abbrs[abbrind:self.abbrs.find(0, abbrind)].decode()
51 if j > 0:
52 prev_tti = self.ttis[self.type_indices[j - 1]]
53 shift = " %+g" % ((tti.tt_gmtoff - prev_tti.tt_gmtoff) / 3600)
54 else:
55 shift = ''
56 print("%s UTC = %s %-5s isdst=%d" % (utc, lmt, abbr, tti[1]) + shift, file=stream)
57
58 @classmethod
59 def zonelist(cls, zonedir='/usr/share/zoneinfo'):
60 zones = []
61 for root, _, files in os.walk(zonedir):
62 for f in files:
63 p = os.path.join(root, f)
64 with open(p, 'rb') as o:
65 magic = o.read(4)
66 if magic == b'TZif':
67 zones.append(p[len(zonedir) + 1:])
68 return zones
69
70if __name__ == '__main__':
71 if len(sys.argv) < 2:
72 zones = TZInfo.zonelist()
73 for z in zones:
74 print(z)
75 sys.exit()
76 filepath = sys.argv[1]
77 if not filepath.startswith('/'):
78 filepath = os.path.join('/usr/share/zoneinfo', filepath)
79 with open(filepath, 'rb') as fileobj:
80 tzi = TZInfo.fromfile(fileobj)
81 tzi.dump(sys.stdout)