]> git.madduck.net Git - etc/awesome.git/blob - src/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:

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