]> git.madduck.net Git - etc/awesome.git/blob - luatz/tzfile.lua

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

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.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

9603cfc8863388c912231d602ce1c8c4298d78b9
[etc/awesome.git] / luatz / tzfile.lua
1 local tz_info_mt = require "luatz.tzinfo".tz_info_mt
2 local tt_info_mt = require "luatz.tzinfo".tt_info_mt
3
4 local read_int32be, read_int64be
5
6 if string.unpack then -- Only available in Lua 5.3+
7         function read_int32be(fd)
8                 local data, err = fd:read(4)
9                 if data == nil then return nil, err end
10                 return string.unpack(">i4", data)
11         end
12
13         function read_int64be(fd)
14                 local data, err = fd:read(8)
15                 if data == nil then return nil, err end
16                 return string.unpack(">i8", data)
17         end
18 else
19         function read_int32be(fd)
20                 local data, err = fd:read(4)
21                 if data == nil then return nil, err end
22                 local o1, o2, o3, o4 = data:byte(1, 4)
23
24                 local unsigned = o4 + o3*2^8 + o2*2^16 + o1*2^24
25                 if unsigned >= 2^31 then
26                         return unsigned - 2^32
27                 else
28                         return unsigned
29                 end
30         end
31
32         function read_int64be(fd)
33                 local data, err = fd:read(8)
34                 if data == nil then return nil, err end
35                 local o1, o2, o3, o4, o5, o6, o7, o8 = data:byte(1, 8)
36
37                 local unsigned = o8 + o7*2^8 + o6*2^16 + o5*2^24 + o4*2^32 + o3*2^40 + o2*2^48 + o1*2^56
38                 if unsigned >= 2^63 then
39                         return unsigned - 2^64
40                 else
41                         return unsigned
42                 end
43         end
44 end
45
46 local function read_flags ( fd , n )
47         local data , err = fd:read ( n )
48         if data == nil then return nil , err end
49
50         local res = { }
51         for i=1, n do
52                 res[i] = data:byte(i,i) ~= 0
53         end
54         return res
55 end
56
57 local fifteen_nulls = ("\0"):rep(15)
58 local function read_tz ( fd )
59         assert ( fd:read(4) == "TZif" , "Invalid TZ file" )
60         local version = assert ( fd:read(1) )
61         if version == "\0" or version == "2" or version == "3" then
62                 local MIN_TIME = -2^32+1
63
64                 assert ( assert ( fd:read(15) ) == fifteen_nulls , "Expected 15 nulls" )
65
66                 -- The number of UTC/local indicators stored in the file.
67                 local tzh_ttisgmtcnt = assert ( read_int32be ( fd ) )
68
69                 -- The number of standard/wall indicators stored in the file.
70                 local tzh_ttisstdcnt = assert ( read_int32be ( fd ) )
71
72                 -- The number of leap seconds for which data is stored in the file.
73                 local tzh_leapcnt = assert ( read_int32be ( fd ) )
74
75                 -- The number of "transition times" for which data is stored in the file.
76                 local tzh_timecnt = assert ( read_int32be ( fd ) )
77
78                 -- The number of "local time types" for which data is stored in the file (must not be zero).
79                 local tzh_typecnt = assert ( read_int32be ( fd ) )
80
81                 -- The number of characters of "timezone abbreviation strings" stored in the file.
82                 local tzh_charcnt = assert ( read_int32be ( fd ) )
83
84                 local transition_times = { }
85                 for i=1, tzh_timecnt do
86                         transition_times [ i ] = assert ( read_int32be ( fd ) )
87                 end
88                 local transition_time_ind = { assert ( fd:read ( tzh_timecnt ) ):byte ( 1 , -1 ) }
89
90                 local ttinfos = { }
91                 for i=1, tzh_typecnt do
92                         ttinfos [ i ] = {
93                                 gmtoff = assert ( read_int32be ( fd ) ) ;
94                                 isdst  = assert ( fd:read ( 1 ) ) ~= "\0" ;
95                                 abbrind = assert ( fd:read ( 1 ) ):byte ( ) ;
96                         }
97                 end
98
99                 local abbreviations = assert ( fd:read ( tzh_charcnt ) )
100
101                 local leap_seconds = { } -- luacheck: ignore 241
102                 for i=1, tzh_leapcnt do
103                         leap_seconds [ i ] = {
104                                 offset = assert ( read_int32be ( fd ) ) ;
105                                 n = assert ( read_int32be ( fd ) ) ;
106                         }
107                 end
108
109                 local isstd = assert ( read_flags ( fd , tzh_ttisstdcnt ) )
110
111                 local isgmt = assert ( read_flags ( fd , tzh_ttisgmtcnt ) )
112
113                 local TZ
114
115                 if version == "2" or version == "3" then
116                         --[[
117                         For version-2-format timezone files, the above header and data is followed by a second header and data,
118                         identical in format except that eight bytes are used for each transition time or leap-second time.
119                         ]]
120                         assert(fd:read(4) == "TZif")
121                         assert(fd:read(1) == version)
122                         assert ( assert ( fd:read(15) ) == fifteen_nulls , "Expected 15 nulls" )
123
124                         MIN_TIME = -2^64+1
125
126                         -- The number of UTC/local indicators stored in the file.
127                         tzh_ttisgmtcnt = assert ( read_int32be ( fd ) )
128
129                         -- The number of standard/wall indicators stored in the file.
130                         tzh_ttisstdcnt = assert ( read_int32be ( fd ) )
131
132                         -- The number of leap seconds for which data is stored in the file.
133                         tzh_leapcnt = assert ( read_int32be ( fd ) )
134
135                         -- The number of "transition times" for which data is stored in the file.
136                         tzh_timecnt = assert ( read_int32be ( fd ) )
137
138                         -- The number of "local time types" for which data is stored in the file (must not be zero).
139                         tzh_typecnt = assert ( read_int32be ( fd ) )
140
141                         -- The number of characters of "timezone abbreviation strings" stored in the file.
142                         tzh_charcnt = assert ( read_int32be ( fd ) )
143
144                         transition_times = { }
145                         for i=1, tzh_timecnt do
146                                 transition_times [ i ] = assert ( read_int64be ( fd ) )
147                         end
148                         transition_time_ind = { assert ( fd:read ( tzh_timecnt ) ):byte ( 1 , -1 ) }
149
150                         ttinfos = { }
151                         for i=1, tzh_typecnt do
152                                 ttinfos [ i ] = {
153                                         gmtoff = assert ( read_int32be ( fd ) ) ;
154                                         isdst  = assert ( fd:read ( 1 ) ) ~= "\0" ;
155                                         abbrind = assert ( fd:read ( 1 ) ):byte ( ) ;
156                                 }
157                         end
158
159                         abbreviations = assert ( fd:read ( tzh_charcnt ) )
160
161                         leap_seconds = { }
162                         for i=1, tzh_leapcnt do
163                                 leap_seconds [ i ] = {
164                                         offset = assert ( read_int64be ( fd ) ) ;
165                                         n = assert ( read_int32be ( fd ) ) ;
166                                 }
167                         end
168
169                         isstd = assert ( read_flags ( fd , tzh_ttisstdcnt ) )
170
171                         isgmt = assert ( read_flags ( fd , tzh_ttisgmtcnt ) )
172
173                         --[[
174                         After the second header and data comes a newline-enclosed, POSIX-TZ-environment-variable-style string
175                         for use in handling instants after the last transition time stored in the file
176                         (with nothing between the newlines if there is no POSIX representation for such instants).
177                         ]]
178
179                         --[[
180                         For version-3-format time zone files, the POSIX-TZ-style string may
181                         use two minor extensions to the POSIX TZ format, as described in newtzset (3).
182                         First, the hours part of its transition times may be signed and range from
183                         -167 through 167 instead of the POSIX-required unsigned values
184                         from 0 through 24.  Second, DST is in effect all year if it starts
185                         January 1 at 00:00 and ends December 31 at 24:00 plus the difference
186                         between daylight saving and standard time.
187                         ]]
188
189                         assert ( assert ( fd:read ( 1 ) ) == "\n" , "Expected newline at end of version 2 header" )
190
191                         TZ = assert ( fd:read ( "*l" ) )
192                         if #TZ == 0 then
193                                 TZ = nil
194                         end
195                 end
196
197                 for i=1, tzh_typecnt do
198                         local v = ttinfos [ i ]
199                         v.abbr = abbreviations:sub ( v.abbrind+1 , v.abbrind+3 )
200                         v.isstd = isstd [ i ] or false
201                         v.isgmt = isgmt [ i ] or false
202                         setmetatable ( v , tt_info_mt )
203                 end
204
205                 --[[
206                 Use the first standard-time ttinfo structure in the file
207                 (or simply the first ttinfo structure in the absence of a standard-time structure)
208                 if either tzh_timecnt is zero or the time argument is less than the first transition time recorded in the file.
209                 ]]
210                 local first = 1
211                 do
212                         for i=1, tzh_ttisstdcnt do
213                                 if isstd[i] then
214                                         first = i
215                                         break
216                                 end
217                         end
218                 end
219
220                 local res = {
221                         future = TZ;
222                         [0] = {
223                                 transition_time = MIN_TIME ;
224                                 info = ttinfos [ first ] ;
225                         }
226                 }
227                 for i=1, tzh_timecnt do
228                         res [ i ] = {
229                                 transition_time = transition_times [ i ] ;
230                                 info = ttinfos [ transition_time_ind [ i ]+1 ] ;
231                         }
232                 end
233                 return setmetatable ( res , tz_info_mt )
234         else
235                 error ( "Unsupported version" )
236         end
237 end
238
239 local function read_tzfile ( path )
240         local fd = assert ( io.open ( path , "rb" ) )
241         local tzinfo = read_tz ( fd )
242         fd:close ( )
243         return tzinfo
244 end
245
246 return {
247         read_tz = read_tz ;
248         read_tzfile = read_tzfile ;
249 }