You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
2.8 KiB
Lua
100 lines
2.8 KiB
Lua
local gettime = require"luatz.gettime".gettime
|
|
local timetable_mt = require"luatz.timetable".timetable_mt
|
|
|
|
local function to_timestamp(o)
|
|
if type(o) == "number" then
|
|
return o
|
|
elseif getmetatable(o) == timetable_mt then
|
|
return o:timestamp()
|
|
end
|
|
end
|
|
|
|
local tz_info_methods = {}
|
|
local tz_info_mt = {
|
|
__name = "luatz.tz_info",
|
|
__index = tz_info_methods,
|
|
}
|
|
local tt_info_mt = {
|
|
__name = "luatz.tt_info",
|
|
__tostring = function(self)
|
|
return string.format("tt_info:%s=%d", self.abbr, self.gmtoff)
|
|
end,
|
|
}
|
|
|
|
-- Binary search
|
|
local function find_current(tzinfo, target, i, j)
|
|
if i >= j then
|
|
return j
|
|
end
|
|
|
|
local half = math.ceil((j + i) / 2)
|
|
|
|
if target >= tzinfo[half].transition_time then
|
|
return find_current(tzinfo, target, half, j)
|
|
else
|
|
return find_current(tzinfo, target, i, half - 1)
|
|
end
|
|
end
|
|
|
|
local function find_current_local(tzinfo, ts_local)
|
|
-- Find two best possibilities by searching back and forward a day (assumes transition is never by more than 24 hours)
|
|
local tz_first = find_current(tzinfo, ts_local - 86400, 0, #tzinfo)
|
|
local tz_last = find_current(tzinfo, ts_local + 86400, 0, #tzinfo)
|
|
|
|
local n_candidates = tz_last - tz_first + 1
|
|
|
|
if n_candidates == 1 then
|
|
return tz_first
|
|
elseif n_candidates == 2 then
|
|
local tz_first_ob = tzinfo[tz_first]
|
|
local tz_last_ob = tzinfo[tz_last]
|
|
|
|
local first_gmtoffset = tz_first_ob.info.gmtoff
|
|
local last_gmtoffset = tz_last_ob.info.gmtoff
|
|
|
|
local t_start = tz_last_ob.transition_time + first_gmtoffset
|
|
local t_end = tz_last_ob.transition_time + last_gmtoffset
|
|
|
|
-- If timestamp is before start or after end
|
|
if ts_local < t_start then
|
|
return tz_first
|
|
elseif ts_local > t_end then
|
|
return tz_last
|
|
end
|
|
|
|
-- If we get this far, the local time is ambiguous
|
|
return tz_first, tz_last
|
|
else
|
|
error("Too many transitions in a 2 day period")
|
|
end
|
|
end
|
|
|
|
function tz_info_methods:find_current(current)
|
|
current = assert(to_timestamp(current), "invalid timestamp to :find_current")
|
|
return self[find_current(self, current, 0, #self)].info
|
|
end
|
|
|
|
function tz_info_methods:localise(utc_ts)
|
|
utc_ts = utc_ts or gettime()
|
|
return utc_ts + self:find_current(utc_ts).gmtoff
|
|
end
|
|
tz_info_methods.localize = tz_info_methods.localise
|
|
|
|
function tz_info_methods:utctime(ts_local)
|
|
ts_local = assert(to_timestamp(ts_local), "invalid timestamp to :utctime")
|
|
local tz1, tz2 = find_current_local(self, ts_local)
|
|
tz1 = self[tz1].info
|
|
if tz2 == nil then
|
|
return ts_local - tz1.gmtoff
|
|
else -- Local time is ambiguous
|
|
tz2 = self[tz2].info
|
|
|
|
return ts_local - tz2.gmtoff, ts_local - tz2.gmtoff
|
|
end
|
|
end
|
|
|
|
return {
|
|
tz_info_mt = tz_info_mt,
|
|
tt_info_mt = tt_info_mt,
|
|
}
|