X-Git-Url: https://git.madduck.net/etc/awesome.git/blobdiff_plain/e5c87b4489b0881b7b9903733478f0d11ac96ab7..bdbbf89c38126a71b17049469e8f976c571b9392:/luatz/tzfile.lua diff --git a/luatz/tzfile.lua b/luatz/tzfile.lua index eed426e..e57db6d 100644 --- a/luatz/tzfile.lua +++ b/luatz/tzfile.lua @@ -1,36 +1,55 @@ local tz_info_mt = require "luatz.tzinfo".tz_info_mt +local tt_info_mt = require "luatz.tzinfo".tt_info_mt -local function read_int32be ( fd ) - local data , err = fd:read ( 4 ) - if data == nil then return nil , err end - local o1 , o2 , o3 , o4 = data:byte ( 1 , 4 ) +local read_int32be, read_int64be - local unsigned = o4 + o3*2^8 + o2*2^16 + o1*2^24 - if unsigned >= 2^31 then - return unsigned - 2^32 - else - return unsigned +-- luacheck: push std max +if string.unpack then + -- Only available in Lua 5.3+ + function read_int32be(fd) + local data, err = fd:read(4) + if data == nil then return nil, err end + return string.unpack(">i4", data) + end + + function read_int64be(fd) + local data, err = fd:read(8) + if data == nil then return nil, err end + return string.unpack(">i8", data) + end +else -- luacheck: pop + function read_int32be(fd) + local data, err = fd:read(4) + if data == nil then return nil, err end + local o1, o2, o3, o4 = data:byte(1, 4) + + local unsigned = o4 + o3*2^8 + o2*2^16 + o1*2^24 + if unsigned >= 2^31 then + return unsigned - 2^32 + else + return unsigned + end end -end -local function read_int64be ( fd ) - local data , err = fd:read ( 8 ) - if data == nil then return nil , err end - local o1 , o2 , o3 , o4 , o5 , o6 , o7 , o8 = data:byte ( 1 , 8 ) + function read_int64be(fd) + local data, err = fd:read(8) + if data == nil then return nil, err end + local o1, o2, o3, o4, o5, o6, o7, o8 = data:byte(1, 8) - local unsigned = o8 + o7*2^8 + o6*2^16 + o5*2^24 + o4*2^32 + o3*2^40 + o2*2^48 + o1*2^56 - if unsigned >= 2^63 then - return unsigned - 2^64 - else - return unsigned + local unsigned = o8 + o7*2^8 + o6*2^16 + o5*2^24 + o4*2^32 + o3*2^40 + o2*2^48 + o1*2^56 + if unsigned >= 2^63 then + return unsigned - 2^64 + else + return unsigned + end end end -local function read_flags ( fd , n ) - local data , err = fd:read ( n ) - if data == nil then return nil , err end +local function read_flags(fd, n) + local data, err = fd:read(n) + if data == nil then return nil, err end - local res = { } + local res = {} for i=1, n do res[i] = data:byte(i,i) ~= 0 end @@ -38,130 +57,151 @@ local function read_flags ( fd , n ) end local fifteen_nulls = ("\0"):rep(15) -local function read_tz ( fd ) - assert ( fd:read(4) == "TZif" ) - local version = assert ( fd:read(1) ) - if version == "\0" or version == "2" then +local function read_tz(fd) + assert(fd:read(4) == "TZif", "Invalid TZ file") + local version = assert(fd:read(1)) + if version == "\0" or version == "2" or version == "3" then local MIN_TIME = -2^32+1 - assert ( assert ( fd:read(15) ) == fifteen_nulls , "Expected 15 nulls" ) + assert(assert(fd:read(15)) == fifteen_nulls, "Expected 15 nulls") -- The number of UTC/local indicators stored in the file. - local tzh_ttisgmtcnt = assert ( read_int32be ( fd ) ) + local tzh_ttisgmtcnt = assert(read_int32be(fd)) -- The number of standard/wall indicators stored in the file. - local tzh_ttisstdcnt = assert ( read_int32be ( fd ) ) + local tzh_ttisstdcnt = assert(read_int32be(fd)) -- The number of leap seconds for which data is stored in the file. - local tzh_leapcnt = assert ( read_int32be ( fd ) ) + local tzh_leapcnt = assert(read_int32be(fd)) -- The number of "transition times" for which data is stored in the file. - local tzh_timecnt = assert ( read_int32be ( fd ) ) + local tzh_timecnt = assert(read_int32be(fd)) -- The number of "local time types" for which data is stored in the file (must not be zero). - local tzh_typecnt = assert ( read_int32be ( fd ) ) + local tzh_typecnt = assert(read_int32be(fd)) -- The number of characters of "timezone abbreviation strings" stored in the file. - local tzh_charcnt = assert ( read_int32be ( fd ) ) + local tzh_charcnt = assert(read_int32be(fd)) - local transition_times = { } + local transition_times = {} for i=1, tzh_timecnt do - transition_times [ i ] = assert ( read_int32be ( fd ) ) + transition_times[i] = assert(read_int32be(fd)) end - local transition_time_ind = { assert ( fd:read ( tzh_timecnt ) ):byte ( 1 , -1 ) } + local transition_time_ind = {assert(fd:read(tzh_timecnt)):byte(1, -1)} - local ttinfos = { } + local ttinfos = {} for i=1, tzh_typecnt do - ttinfos [ i ] = { - gmtoff = assert ( read_int32be ( fd ) ) ; - isdst = assert ( fd:read ( 1 ) ) ~= "\0" ; - abbrind = assert ( fd:read ( 1 ) ):byte ( ) ; + ttinfos[i] = { + gmtoff = assert(read_int32be(fd)); + isdst = assert(fd:read(1)) ~= "\0"; + abbrind = assert(fd:read(1)):byte(); } end - local abbreviations = assert ( fd:read ( tzh_charcnt ) ) + local abbreviations = assert(fd:read(tzh_charcnt)) - local leap_seconds = { } + local leap_seconds = {} -- luacheck: ignore 241 for i=1, tzh_leapcnt do - leap_seconds [ i ] = { - offset = assert ( read_int32be ( fd ) ) ; - n = assert ( read_int32be ( fd ) ) ; + leap_seconds[i] = { + offset = assert(read_int32be(fd)); + n = assert(read_int32be(fd)); } end - local isstd = assert ( read_flags ( fd , tzh_ttisstdcnt ) ) + local isstd = assert(read_flags(fd, tzh_ttisstdcnt)) - local isgmt = assert ( read_flags ( fd , tzh_ttisgmtcnt ) ) + local isgmt = assert(read_flags(fd, tzh_ttisgmtcnt)) - if version == "2" then + local TZ + + if version == "2" or version == "3" then --[[ For version-2-format timezone files, the above header and data is followed by a second header and data, identical in format except that eight bytes are used for each transition time or leap-second time. ]] - assert ( fd:read(5) == "TZif2" ) - assert ( assert ( fd:read(15) ) == fifteen_nulls , "Expected 15 nulls" ) + assert(fd:read(4) == "TZif") + assert(fd:read(1) == version) + assert(assert(fd:read(15)) == fifteen_nulls, "Expected 15 nulls") MIN_TIME = -2^64+1 -- The number of UTC/local indicators stored in the file. - tzh_ttisgmtcnt = assert ( read_int32be ( fd ) ) + tzh_ttisgmtcnt = assert(read_int32be(fd)) -- The number of standard/wall indicators stored in the file. - tzh_ttisstdcnt = assert ( read_int32be ( fd ) ) + tzh_ttisstdcnt = assert(read_int32be(fd)) -- The number of leap seconds for which data is stored in the file. - tzh_leapcnt = assert ( read_int32be ( fd ) ) + tzh_leapcnt = assert(read_int32be(fd)) -- The number of "transition times" for which data is stored in the file. - tzh_timecnt = assert ( read_int32be ( fd ) ) + tzh_timecnt = assert(read_int32be(fd)) -- The number of "local time types" for which data is stored in the file (must not be zero). - tzh_typecnt = assert ( read_int32be ( fd ) ) + tzh_typecnt = assert(read_int32be(fd)) -- The number of characters of "timezone abbreviation strings" stored in the file. - tzh_charcnt = assert ( read_int32be ( fd ) ) + tzh_charcnt = assert(read_int32be(fd)) - transition_times = { } + transition_times = {} for i=1, tzh_timecnt do - transition_times [ i ] = assert ( read_int64be ( fd ) ) + transition_times[i] = assert(read_int64be(fd)) end - transition_time_ind = { assert ( fd:read ( tzh_timecnt ) ):byte ( 1 , -1 ) } + transition_time_ind = {assert(fd:read(tzh_timecnt)):byte(1, -1)} - ttinfos = { } + ttinfos = {} for i=1, tzh_typecnt do - ttinfos [ i ] = { - gmtoff = assert ( read_int32be ( fd ) ) ; - isdst = assert ( fd:read ( 1 ) ) ~= "\0" ; - abbrind = assert ( fd:read ( 1 ) ):byte ( ) ; + ttinfos[i] = { + gmtoff = assert(read_int32be(fd)); + isdst = assert(fd:read(1)) ~= "\0"; + abbrind = assert(fd:read(1)):byte(); } end - abbreviations = assert ( fd:read ( tzh_charcnt ) ) + abbreviations = assert(fd:read(tzh_charcnt)) - leap_seconds = { } + leap_seconds = {} for i=1, tzh_leapcnt do - leap_seconds [ i ] = { - offset = assert ( read_int64be ( fd ) ) ; - n = assert ( read_int32be ( fd ) ) ; + leap_seconds[i] = { + offset = assert(read_int64be(fd)); + n = assert(read_int32be(fd)); } end - isstd = assert ( read_flags ( fd , tzh_ttisstdcnt ) ) + isstd = assert(read_flags(fd, tzh_ttisstdcnt)) - isgmt = assert ( read_flags ( fd , tzh_ttisgmtcnt ) ) + isgmt = assert(read_flags(fd, tzh_ttisgmtcnt)) --[[ After the second header and data comes a newline-enclosed, POSIX-TZ-environment-variable-style string for use in handling instants after the last transition time stored in the file (with nothing between the newlines if there is no POSIX representation for such instants). ]] + + --[[ + For version-3-format time zone files, the POSIX-TZ-style string may + use two minor extensions to the POSIX TZ format, as described in newtzset (3). + First, the hours part of its transition times may be signed and range from + -167 through 167 instead of the POSIX-required unsigned values + from 0 through 24. Second, DST is in effect all year if it starts + January 1 at 00:00 and ends December 31 at 24:00 plus the difference + between daylight saving and standard time. + ]] + + assert(assert(fd:read(1)) == "\n", "Expected newline at end of version 2 header") + + TZ = assert(fd:read("*l")) + if #TZ == 0 then + TZ = nil + end end for i=1, tzh_typecnt do - local v = ttinfos [ i ] - v.abbr = abbreviations:sub ( v.abbrind+1 , v.abbrind+3 ) - v.isstd = isstd [ i ] or false - v.isgmt = isgmt [ i ] or false + local v = ttinfos[i] + v.abbr = abbreviations:sub(v.abbrind+1, v.abbrind+3) + v.isstd = isstd[i] or false + v.isgmt = isgmt[i] or false + setmetatable(v, tt_info_mt) end --[[ @@ -180,31 +220,32 @@ local function read_tz ( fd ) end local res = { + future = TZ; [0] = { - transition_time = MIN_TIME ; - info = ttinfos [ first ] ; + transition_time = MIN_TIME; + info = ttinfos[first]; } } for i=1, tzh_timecnt do - res [ i ] = { - transition_time = transition_times [ i ] ; - info = ttinfos [ transition_time_ind [ i ]+1 ] ; + res[i] = { + transition_time = transition_times[i]; + info = ttinfos[transition_time_ind[i]+1]; } end - return setmetatable ( res , tz_info_mt ) + return setmetatable(res, tz_info_mt) else - error ( "Unsupported version" ) + error("Unsupported version") end end -local function read_tzfile ( path ) - local fd = assert ( io.open ( path ) ) - local tzinfo = read_tz ( fd ) - fd:close ( ) +local function read_tzfile(path) + local fd = assert(io.open(path, "rb")) + local tzinfo = read_tz(fd) + fd:close() return tzinfo end return { - read_tz = read_tz ; - read_tzfile = read_tzfile ; + read_tz = read_tz; + read_tzfile = read_tzfile; }