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

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