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

doc/index.md: Fix internal link
[etc/awesome.git] / luatz / tzinfo.lua
1 local gettime = require "luatz.gettime".gettime
2 local timetable_mt = require "luatz.timetable".timetable_mt
3
4 local function to_timestamp(o)
5         if type(o) == "number" then
6                 return o
7         elseif getmetatable(o) == timetable_mt then
8                 return o:timestamp()
9         end
10 end
11
12 local tz_info_methods = { }
13 local tz_info_mt = {
14         __name = "luatz.tz_info";
15         __index = tz_info_methods;
16 }
17 local tt_info_mt = {
18         __name = "luatz.tt_info";
19         __tostring = function(self)
20                 return string.format("tt_info:%s=%d", self.abbr, self.gmtoff)
21         end;
22 }
23
24 -- Binary search
25 local function find_current(tzinfo, target, i, j)
26         if i >= j then return j end
27
28         local half = math.ceil((j+i) / 2)
29
30         if target >= tzinfo[half].transition_time then
31                 return find_current(tzinfo, target, half, j)
32         else
33                 return find_current(tzinfo, target, i, half-1)
34         end
35 end
36
37 local function find_current_local(tzinfo, ts_local)
38         -- Find two best possibilities by searching back and forward a day (assumes transition is never by more than 24 hours)
39         local tz_first = find_current(tzinfo, ts_local-86400, 0, #tzinfo)
40         local tz_last  = find_current(tzinfo, ts_local+86400, 0, #tzinfo)
41
42         local n_candidates = tz_last - tz_first + 1
43
44         if n_candidates == 1 then
45                 return tz_first
46         elseif n_candidates == 2 then
47                 local tz_first_ob = tzinfo[tz_first]
48                 local tz_last_ob  = tzinfo[tz_last]
49
50                 local first_gmtoffset = tz_first_ob.info.gmtoff
51                 local last_gmtoffset  = tz_last_ob .info.gmtoff
52
53                 local t_start = tz_last_ob.transition_time + first_gmtoffset
54                 local t_end   = tz_last_ob.transition_time + last_gmtoffset
55
56                 -- If timestamp is before start or after end
57                 if ts_local < t_start then
58                         return tz_first
59                 elseif ts_local > t_end then
60                         return tz_last
61                 end
62
63                 -- If we get this far, the local time is ambiguous
64                 return tz_first, tz_last
65         else
66                 error("Too many transitions in a 2 day period")
67         end
68 end
69
70 function tz_info_methods:find_current(current)
71         current = assert(to_timestamp(current), "invalid timestamp to :find_current")
72         return self[find_current(self, current, 0, #self)].info
73 end
74
75 function tz_info_methods:localise(utc_ts)
76         utc_ts = utc_ts or gettime()
77         return utc_ts + self:find_current(utc_ts).gmtoff
78 end
79 tz_info_methods.localize = tz_info_methods.localise
80
81 function tz_info_methods:utctime(ts_local)
82         ts_local = assert(to_timestamp(ts_local), "invalid timestamp to :utctime")
83         local tz1, tz2 = find_current_local(self, ts_local)
84         tz1 = self[tz1].info
85         if tz2 == nil then
86                 return ts_local - tz1.gmtoff
87         else -- Local time is ambiguous
88                 tz2 = self[tz2].info
89
90                 return ts_local - tz2.gmtoff, ts_local - tz2.gmtoff
91         end
92 end
93
94 return {
95         tz_info_mt = tz_info_mt;
96         tt_info_mt = tt_info_mt;
97 }