All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
1 local tz_info_mt = require "luatz.tzinfo".tz_info_mt
2 local tt_info_mt = require "luatz.tzinfo".tt_info_mt
4 local read_int32be, read_int64be
6 -- luacheck: push std max
8 -- Only available in Lua 5.3+
9 function read_int32be(fd)
10 local data, err = fd:read(4)
11 if data == nil then return nil, err end
12 return string.unpack(">i4", data)
15 function read_int64be(fd)
16 local data, err = fd:read(8)
17 if data == nil then return nil, err end
18 return string.unpack(">i8", data)
21 function read_int32be(fd)
22 local data, err = fd:read(4)
23 if data == nil then return nil, err end
24 local o1, o2, o3, o4 = data:byte(1, 4)
26 local unsigned = o4 + o3*2^8 + o2*2^16 + o1*2^24
27 if unsigned >= 2^31 then
28 return unsigned - 2^32
34 function read_int64be(fd)
35 local data, err = fd:read(8)
36 if data == nil then return nil, err end
37 local o1, o2, o3, o4, o5, o6, o7, o8 = data:byte(1, 8)
39 local unsigned = o8 + o7*2^8 + o6*2^16 + o5*2^24 + o4*2^32 + o3*2^40 + o2*2^48 + o1*2^56
40 if unsigned >= 2^63 then
41 return unsigned - 2^64
48 local function read_flags(fd, n)
49 local data, err = fd:read(n)
50 if data == nil then return nil, err end
54 res[i] = data:byte(i,i) ~= 0
59 local fifteen_nulls = ("\0"):rep(15)
60 local function read_tz(fd)
61 assert(fd:read(4) == "TZif", "Invalid TZ file")
62 local version = assert(fd:read(1))
63 if version == "\0" or version == "2" or version == "3" then
64 local MIN_TIME = -2^32+1
66 assert(assert(fd:read(15)) == fifteen_nulls, "Expected 15 nulls")
68 -- The number of UTC/local indicators stored in the file.
69 local tzh_ttisgmtcnt = assert(read_int32be(fd))
71 -- The number of standard/wall indicators stored in the file.
72 local tzh_ttisstdcnt = assert(read_int32be(fd))
74 -- The number of leap seconds for which data is stored in the file.
75 local tzh_leapcnt = assert(read_int32be(fd))
77 -- The number of "transition times" for which data is stored in the file.
78 local tzh_timecnt = assert(read_int32be(fd))
80 -- The number of "local time types" for which data is stored in the file (must not be zero).
81 local tzh_typecnt = assert(read_int32be(fd))
83 -- The number of characters of "timezone abbreviation strings" stored in the file.
84 local tzh_charcnt = assert(read_int32be(fd))
86 local transition_times = {}
87 for i=1, tzh_timecnt do
88 transition_times[i] = assert(read_int32be(fd))
90 local transition_time_ind = {assert(fd:read(tzh_timecnt)):byte(1, -1)}
93 for i=1, tzh_typecnt do
95 gmtoff = assert(read_int32be(fd));
96 isdst = assert(fd:read(1)) ~= "\0";
97 abbrind = assert(fd:read(1)):byte();
101 local abbreviations = assert(fd:read(tzh_charcnt))
103 local leap_seconds = {} -- luacheck: ignore 241
104 for i=1, tzh_leapcnt do
106 offset = assert(read_int32be(fd));
107 n = assert(read_int32be(fd));
111 local isstd = assert(read_flags(fd, tzh_ttisstdcnt))
113 local isgmt = assert(read_flags(fd, tzh_ttisgmtcnt))
117 if version == "2" or version == "3" then
119 For version-2-format timezone files, the above header and data is followed by a second header and data,
120 identical in format except that eight bytes are used for each transition time or leap-second time.
122 assert(fd:read(4) == "TZif")
123 assert(fd:read(1) == version)
124 assert(assert(fd:read(15)) == fifteen_nulls, "Expected 15 nulls")
128 -- The number of UTC/local indicators stored in the file.
129 tzh_ttisgmtcnt = assert(read_int32be(fd))
131 -- The number of standard/wall indicators stored in the file.
132 tzh_ttisstdcnt = assert(read_int32be(fd))
134 -- The number of leap seconds for which data is stored in the file.
135 tzh_leapcnt = assert(read_int32be(fd))
137 -- The number of "transition times" for which data is stored in the file.
138 tzh_timecnt = assert(read_int32be(fd))
140 -- The number of "local time types" for which data is stored in the file (must not be zero).
141 tzh_typecnt = assert(read_int32be(fd))
143 -- The number of characters of "timezone abbreviation strings" stored in the file.
144 tzh_charcnt = assert(read_int32be(fd))
146 transition_times = {}
147 for i=1, tzh_timecnt do
148 transition_times[i] = assert(read_int64be(fd))
150 transition_time_ind = {assert(fd:read(tzh_timecnt)):byte(1, -1)}
153 for i=1, tzh_typecnt do
155 gmtoff = assert(read_int32be(fd));
156 isdst = assert(fd:read(1)) ~= "\0";
157 abbrind = assert(fd:read(1)):byte();
161 abbreviations = assert(fd:read(tzh_charcnt))
164 for i=1, tzh_leapcnt do
166 offset = assert(read_int64be(fd));
167 n = assert(read_int32be(fd));
171 isstd = assert(read_flags(fd, tzh_ttisstdcnt))
173 isgmt = assert(read_flags(fd, tzh_ttisgmtcnt))
176 After the second header and data comes a newline-enclosed, POSIX-TZ-environment-variable-style string
177 for use in handling instants after the last transition time stored in the file
178 (with nothing between the newlines if there is no POSIX representation for such instants).
182 For version-3-format time zone files, the POSIX-TZ-style string may
183 use two minor extensions to the POSIX TZ format, as described in newtzset (3).
184 First, the hours part of its transition times may be signed and range from
185 -167 through 167 instead of the POSIX-required unsigned values
186 from 0 through 24. Second, DST is in effect all year if it starts
187 January 1 at 00:00 and ends December 31 at 24:00 plus the difference
188 between daylight saving and standard time.
191 assert(assert(fd:read(1)) == "\n", "Expected newline at end of version 2 header")
193 TZ = assert(fd:read("*l"))
199 for i=1, tzh_typecnt do
201 v.abbr = abbreviations:sub(v.abbrind+1, v.abbrind+3)
202 v.isstd = isstd[i] or false
203 v.isgmt = isgmt[i] or false
204 setmetatable(v, tt_info_mt)
208 Use the first standard-time ttinfo structure in the file
209 (or simply the first ttinfo structure in the absence of a standard-time structure)
210 if either tzh_timecnt is zero or the time argument is less than the first transition time recorded in the file.
214 for i=1, tzh_ttisstdcnt do
225 transition_time = MIN_TIME;
226 info = ttinfos[first];
229 for i=1, tzh_timecnt do
231 transition_time = transition_times[i];
232 info = ttinfos[transition_time_ind[i]+1];
235 return setmetatable(res, tz_info_mt)
237 error("Unsupported version")
241 local function read_tzfile(path)
242 local fd = assert(io.open(path, "rb"))
243 local tzinfo = read_tz(fd)
250 read_tzfile = read_tzfile;