]> git.madduck.net Git - etc/awesome.git/commitdiff

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:

Add src/timetable module (with tests)
authordaurnimator <quae@daurnimator.com>
Tue, 23 Jul 2013 19:39:52 +0000 (15:39 -0400)
committerdaurnimator <quae@daurnimator.com>
Tue, 23 Jul 2013 19:39:52 +0000 (15:39 -0400)
luatz-scm-0.rockspec
spec/timetable_spec.lua [new file with mode: 0644]
src/timetable.lua [new file with mode: 0644]

index 73b4dff9f99b9a996d376b06ae43abf6cefec238..25b0f62548818e9548f74b9f8934269b1436c806 100644 (file)
@@ -22,10 +22,11 @@ source = {
 build = {
        type = "builtin" ;
        modules = {
-               ["luatz.init"]    = "src/init.lua" ;
-               ["luatz.gettime"] = "src/gettime.lua" ;
-               ["luatz.tzcache"] = "src/tzcache.lua" ;
-               ["luatz.tzfile"]  = "src/tzfile.lua" ;
-               ["luatz.tzinfo"]  = "src/tzinfo.lua" ;
+               ["luatz.init"]      = "src/init.lua" ;
+               ["luatz.gettime"]   = "src/gettime.lua" ;
+               ["luatz.timetable"] = "src/timetable.lua" ;
+               ["luatz.tzcache"]   = "src/tzcache.lua" ;
+               ["luatz.tzfile"]    = "src/tzfile.lua" ;
+               ["luatz.tzinfo"]    = "src/tzinfo.lua" ;
        } ;
 }
diff --git a/spec/timetable_spec.lua b/spec/timetable_spec.lua
new file mode 100644 (file)
index 0000000..6536459
--- /dev/null
@@ -0,0 +1,37 @@
+describe ( "Time table library" , function ( )
+       local timetable = require "timetable"
+
+       it ( "Doomsday calculation" , function ( )
+               local doomsday = timetable.doomsday
+
+               -- Doomsday in Gregorian calendar for 2013 is Thursday.
+               assert.are.same ( 5 , doomsday(2013) )
+
+               assert.are.same ( 3 , doomsday(1967) )
+               assert.are.same ( 5 , doomsday(1968) )
+       end )
+
+       it ( "Get day of week correct" , function ( )
+               local function native_normalise ( tbl )
+                       return os.date("*t",os.time(tbl))
+               end
+               local tbl = {
+                       year = 2013 ;
+                       month = 7 ;
+                       day = 23 ;
+               }
+               assert.are.same ( native_normalise ( tbl ).wday  , timetable.normalise ( tbl ).wday )
+               tbl.day=24
+               assert.are.same ( native_normalise ( tbl ).wday  , timetable.normalise ( tbl ).wday )
+               tbl.day=25
+               assert.are.same ( native_normalise ( tbl ).wday  , timetable.normalise ( tbl ).wday )
+               tbl.day=26
+               assert.are.same ( native_normalise ( tbl ).wday  , timetable.normalise ( tbl ).wday )
+               tbl.day=27
+               assert.are.same ( native_normalise ( tbl ).wday  , timetable.normalise ( tbl ).wday )
+               tbl.day=28
+               assert.are.same ( native_normalise ( tbl ).wday  , timetable.normalise ( tbl ).wday )
+               tbl.day=29
+               assert.are.same ( native_normalise ( tbl ).wday  , timetable.normalise ( tbl ).wday )
+       end )
+end )
diff --git a/src/timetable.lua b/src/timetable.lua
new file mode 100644 (file)
index 0000000..9af7994
--- /dev/null
@@ -0,0 +1,114 @@
+local floor = math.floor
+local function idiv ( n , d )
+       return floor ( n / d )
+end
+
+
+local mon_lengths = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+-- Number of days in year until start of month; not corrected for leap years
+local months_to_days_cumulative = { 0 }
+for i = 2, 12 do
+       months_to_days_cumulative [ i ] = months_to_days_cumulative [ i-1 ] + mon_lengths [ i-1 ]
+end
+
+local function is_leap ( y )
+       return (y % 4) == 0 and (y % 100) ~= 0 or (y % 400) == 0
+end
+
+local function year_length ( y )
+       return is_leap ( y ) and 366 or 365
+end
+
+local function month_length ( m , y )
+       if m == 2 then
+               return is_leap ( y ) and 29 or 28
+       else
+               return mon_lengths [ m ]
+       end
+end
+
+local function doomsday ( year )
+       return ( 3 -- Tuesday
+               - 1 + year + idiv ( year , 4 ) - idiv ( year , 100 ) + idiv ( year , 400 ) )
+               % 7 + 1
+end
+local doomsday_cache = setmetatable ( { } , {
+       __index = function ( cache , year )
+               local d = doomsday ( year )
+               cache [ year ] = d
+               return d
+       end ;
+} )
+
+local function day_of_year ( day , month , year )
+       local yday = months_to_days_cumulative [ month ]
+       if month > 2 and is_leap ( year ) then
+               yday = yday + 1
+       end
+       return yday + day
+end
+
+local function day_of_week ( yday , year )
+       return ( yday - doomsday_cache [ year ] - 1 ) % 7 + 1
+end
+
+local function increment ( tens , units , base )
+       if units >= base then
+               tens  = tens + idiv ( units , base )
+               units = units % base
+       elseif units < 0 then
+               tens  = tens - 1 + idiv ( -units , base )
+               units = base - ( -units % base )
+       end
+       return tens , units
+end
+
+local function normalise ( tm )
+       local sec   = tm.sec  or 0
+       local min   = tm.min  or 0
+       local hour  = tm.hour or 12
+       local day   = assert ( tm.day   , "day required" )
+       local month = assert ( tm.month , "month required" )
+       local year  = assert ( tm.year  , "year required" )
+
+       min  , sec  = increment ( min  , sec  , 60 ) -- TODO: consider leap seconds?
+       hour , min  = increment ( hour , min  , 60 )
+       day  , hour = increment ( day  , hour , 24 )
+
+       while day <= 0 do
+               year = year - 1
+               day  = day + year_length ( year )
+       end
+
+       -- This could potentially be slow if `day` is very large
+       while true do
+               local i = month_length ( month , year )
+               if day <= i then break end
+               day = day - i
+               month = month + 1
+       end
+
+       -- Lua months start from 1, need -1 and +1 around this increment
+       year , month = increment ( year , month - 1 , 12 )
+       month = month + 1
+
+       tm.sec   = sec
+       tm.min   = min
+       tm.hour  = hour
+       tm.day   = day
+       tm.month = month
+       tm.year  = year
+
+       local yday = day_of_year ( day , month , year )
+       local wday = day_of_week ( yday , year )
+
+       tm.yday = yday
+       tm.wday = wday
+
+       return tm
+end
+
+return {
+       doomsday  = doomsday ;
+       normalise = normalise ;
+}