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

9af79942cdd47883ebc137b2e602a022aaa8f73a
[etc/awesome.git] / src / timetable.lua
1 local floor = math.floor
2 local function idiv ( n , d )
3         return floor ( n / d )
4 end
5
6
7 local mon_lengths = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
8 -- Number of days in year until start of month; not corrected for leap years
9 local months_to_days_cumulative = { 0 }
10 for i = 2, 12 do
11         months_to_days_cumulative [ i ] = months_to_days_cumulative [ i-1 ] + mon_lengths [ i-1 ]
12 end
13
14 local function is_leap ( y )
15         return (y % 4) == 0 and (y % 100) ~= 0 or (y % 400) == 0
16 end
17
18 local function year_length ( y )
19         return is_leap ( y ) and 366 or 365
20 end
21
22 local function month_length ( m , y )
23         if m == 2 then
24                 return is_leap ( y ) and 29 or 28
25         else
26                 return mon_lengths [ m ]
27         end
28 end
29
30 local function doomsday ( year )
31         return ( 3 -- Tuesday
32                 - 1 + year + idiv ( year , 4 ) - idiv ( year , 100 ) + idiv ( year , 400 ) )
33                 % 7 + 1
34 end
35 local doomsday_cache = setmetatable ( { } , {
36         __index = function ( cache , year )
37                 local d = doomsday ( year )
38                 cache [ year ] = d
39                 return d
40         end ;
41 } )
42
43 local function day_of_year ( day , month , year )
44         local yday = months_to_days_cumulative [ month ]
45         if month > 2 and is_leap ( year ) then
46                 yday = yday + 1
47         end
48         return yday + day
49 end
50
51 local function day_of_week ( yday , year )
52         return ( yday - doomsday_cache [ year ] - 1 ) % 7 + 1
53 end
54
55 local function increment ( tens , units , base )
56         if units >= base then
57                 tens  = tens + idiv ( units , base )
58                 units = units % base
59         elseif units < 0 then
60                 tens  = tens - 1 + idiv ( -units , base )
61                 units = base - ( -units % base )
62         end
63         return tens , units
64 end
65
66 local function normalise ( tm )
67         local sec   = tm.sec  or 0
68         local min   = tm.min  or 0
69         local hour  = tm.hour or 12
70         local day   = assert ( tm.day   , "day required" )
71         local month = assert ( tm.month , "month required" )
72         local year  = assert ( tm.year  , "year required" )
73
74         min  , sec  = increment ( min  , sec  , 60 ) -- TODO: consider leap seconds?
75         hour , min  = increment ( hour , min  , 60 )
76         day  , hour = increment ( day  , hour , 24 )
77
78         while day <= 0 do
79                 year = year - 1
80                 day  = day + year_length ( year )
81         end
82
83         -- This could potentially be slow if `day` is very large
84         while true do
85                 local i = month_length ( month , year )
86                 if day <= i then break end
87                 day = day - i
88                 month = month + 1
89         end
90
91         -- Lua months start from 1, need -1 and +1 around this increment
92         year , month = increment ( year , month - 1 , 12 )
93         month = month + 1
94
95         tm.sec   = sec
96         tm.min   = min
97         tm.hour  = hour
98         tm.day   = day
99         tm.month = month
100         tm.year  = year
101
102         local yday = day_of_year ( day , month , year )
103         local wday = day_of_week ( yday , year )
104
105         tm.yday = yday
106         tm.wday = wday
107
108         return tm
109 end
110
111 return {
112         doomsday  = doomsday ;
113         normalise = normalise ;
114 }