]> 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:

spec/strftime_spec: Use the same timestamp every test run.
[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
5 local function read_int32be ( fd )
6         local data , err = fd:read ( 4 )
7         if data == nil then return nil , err end
8         local o1 , o2 , o3 , o4 = data:byte ( 1 , 4 )
9
10         local unsigned = o4 + o3*2^8 + o2*2^16 + o1*2^24
11         if unsigned >= 2^31 then
12                 return unsigned - 2^32
13         else
14                 return unsigned
15         end
16 end
17
18 local function read_int64be ( fd )
19         local data , err = fd:read ( 8 )
20         if data == nil then return nil , err end
21         local o1 , o2 , o3 , o4 , o5 , o6 , o7 , o8 = data:byte ( 1 , 8 )
22
23         local unsigned = o8 + o7*2^8 + o6*2^16 + o5*2^24 + o4*2^32 + o3*2^40 + o2*2^48 + o1*2^56
24         if unsigned >= 2^63 then
25                 return unsigned - 2^64
26         else
27                 return unsigned
28         end
29 end
30
31 local function read_flags ( fd , n )
32         local data , err = fd:read ( n )
33         if data == nil then return nil , err end
34
35         local res = { }
36         for i=1, n do
37                 res[i] = data:byte(i,i) ~= 0
38         end
39         return res
40 end
41
42 local fifteen_nulls = ("\0"):rep(15)
43 local function read_tz ( fd )
44         assert ( fd:read(4) == "TZif" , "Invalid TZ file" )
45         local version = assert ( fd:read(1) )
46         if version == "\0" or version == "2" then
47                 local MIN_TIME = -2^32+1
48
49                 assert ( assert ( fd:read(15) ) == fifteen_nulls , "Expected 15 nulls" )
50
51                 -- The number of UTC/local indicators stored in the file.
52                 local tzh_ttisgmtcnt = assert ( read_int32be ( fd ) )
53
54                 -- The number of standard/wall indicators stored in the file.
55                 local tzh_ttisstdcnt = assert ( read_int32be ( fd ) )
56
57                 -- The number of leap seconds for which data is stored in the file.
58                 local tzh_leapcnt = assert ( read_int32be ( fd ) )
59
60                 -- The number of "transition times" for which data is stored in the file.
61                 local tzh_timecnt = assert ( read_int32be ( fd ) )
62
63                 -- The number of "local time types" for which data is stored in the file (must not be zero).
64                 local tzh_typecnt = assert ( read_int32be ( fd ) )
65
66                 -- The number of characters of "timezone abbreviation strings" stored in the file.
67                 local tzh_charcnt = assert ( read_int32be ( fd ) )
68
69                 local transition_times = { }
70                 for i=1, tzh_timecnt do
71                         transition_times [ i ] = assert ( read_int32be ( fd ) )
72                 end
73                 local transition_time_ind = { assert ( fd:read ( tzh_timecnt ) ):byte ( 1 , -1 ) }
74
75                 local ttinfos = { }
76                 for i=1, tzh_typecnt do
77                         ttinfos [ i ] = {
78                                 gmtoff = assert ( read_int32be ( fd ) ) ;
79                                 isdst  = assert ( fd:read ( 1 ) ) ~= "\0" ;
80                                 abbrind = assert ( fd:read ( 1 ) ):byte ( ) ;
81                         }
82                 end
83
84                 local abbreviations = assert ( fd:read ( tzh_charcnt ) )
85
86                 local leap_seconds = { }
87                 for i=1, tzh_leapcnt do
88                         leap_seconds [ i ] = {
89                                 offset = assert ( read_int32be ( fd ) ) ;
90                                 n = assert ( read_int32be ( fd ) ) ;
91                         }
92                 end
93
94                 local isstd = assert ( read_flags ( fd , tzh_ttisstdcnt ) )
95
96                 local isgmt = assert ( read_flags ( fd , tzh_ttisgmtcnt ) )
97
98                 if version == "2" then
99                         --[[
100                         For version-2-format timezone files, the above header and data is followed by a second header and data,
101                         identical in format except that eight bytes are used for each transition time or leap-second time.
102                         ]]
103                         assert ( fd:read(5) == "TZif2" )
104                         assert ( assert ( fd:read(15) ) == fifteen_nulls , "Expected 15 nulls" )
105
106                         MIN_TIME = -2^64+1
107
108                         -- The number of UTC/local indicators stored in the file.
109                         tzh_ttisgmtcnt = assert ( read_int32be ( fd ) )
110
111                         -- The number of standard/wall indicators stored in the file.
112                         tzh_ttisstdcnt = assert ( read_int32be ( fd ) )
113
114                         -- The number of leap seconds for which data is stored in the file.
115                         tzh_leapcnt = assert ( read_int32be ( fd ) )
116
117                         -- The number of "transition times" for which data is stored in the file.
118                         tzh_timecnt = assert ( read_int32be ( fd ) )
119
120                         -- The number of "local time types" for which data is stored in the file (must not be zero).
121                         tzh_typecnt = assert ( read_int32be ( fd ) )
122
123                         -- The number of characters of "timezone abbreviation strings" stored in the file.
124                         tzh_charcnt = assert ( read_int32be ( fd ) )
125
126                         transition_times = { }
127                         for i=1, tzh_timecnt do
128                                 transition_times [ i ] = assert ( read_int64be ( fd ) )
129                         end
130                         transition_time_ind = { assert ( fd:read ( tzh_timecnt ) ):byte ( 1 , -1 ) }
131
132                         ttinfos = { }
133                         for i=1, tzh_typecnt do
134                                 ttinfos [ i ] = {
135                                         gmtoff = assert ( read_int32be ( fd ) ) ;
136                                         isdst  = assert ( fd:read ( 1 ) ) ~= "\0" ;
137                                         abbrind = assert ( fd:read ( 1 ) ):byte ( ) ;
138                                 }
139                         end
140
141                         abbreviations = assert ( fd:read ( tzh_charcnt ) )
142
143                         leap_seconds = { }
144                         for i=1, tzh_leapcnt do
145                                 leap_seconds [ i ] = {
146                                         offset = assert ( read_int64be ( fd ) ) ;
147                                         n = assert ( read_int32be ( fd ) ) ;
148                                 }
149                         end
150
151                         isstd = assert ( read_flags ( fd , tzh_ttisstdcnt ) )
152
153                         isgmt = assert ( read_flags ( fd , tzh_ttisgmtcnt ) )
154
155                         --[[
156                         After the second header and data comes a newline-enclosed, POSIX-TZ-environment-variable-style string
157                         for use in handling instants after the last transition time stored in the file
158                         (with nothing between the newlines if there is no POSIX representation for such instants).
159                         ]]
160                 end
161
162                 for i=1, tzh_typecnt do
163                         local v = ttinfos [ i ]
164                         v.abbr = abbreviations:sub ( v.abbrind+1 , v.abbrind+3 )
165                         v.isstd = isstd [ i ] or false
166                         v.isgmt = isgmt [ i ] or false
167                         setmetatable ( v , tt_info_mt )
168                 end
169
170                 --[[
171                 Use the first standard-time ttinfo structure in the file
172                 (or simply the first ttinfo structure in the absence of a standard-time structure)
173                 if either tzh_timecnt is zero or the time argument is less than the first transition time recorded in the file.
174                 ]]
175                 local first = 1
176                 do
177                         for i=1, tzh_ttisstdcnt do
178                                 if isstd[i] then
179                                         first = i
180                                         break
181                                 end
182                         end
183                 end
184
185                 local res = {
186                         [0] = {
187                                 transition_time = MIN_TIME ;
188                                 info = ttinfos [ first ] ;
189                         }
190                 }
191                 for i=1, tzh_timecnt do
192                         res [ i ] = {
193                                 transition_time = transition_times [ i ] ;
194                                 info = ttinfos [ transition_time_ind [ i ]+1 ] ;
195                         }
196                 end
197                 return setmetatable ( res , tz_info_mt )
198         else
199                 error ( "Unsupported version" )
200         end
201 end
202
203 local function read_tzfile ( path )
204         local fd = assert ( io.open ( path , "rb" ) )
205         local tzinfo = read_tz ( fd )
206         fd:close ( )
207         return tzinfo
208 end
209
210 return {
211         read_tz = read_tz ;
212         read_tzfile = read_tzfile ;
213 }