X-Git-Url: https://git.madduck.net/etc/awesome.git/blobdiff_plain/81eaf243f3f28403292343af73e178f79ab5c0b3..6db06b57e851ec08d5355f015d6df1ebe58b4982:/src/timetable.lua diff --git a/src/timetable.lua b/src/timetable.lua index 9af7994..d1cd534 100644 --- a/src/timetable.lua +++ b/src/timetable.lua @@ -1,3 +1,4 @@ +local strformat = string.format local floor = math.floor local function idiv ( n , d ) return floor ( n / d ) @@ -20,6 +21,7 @@ local function year_length ( y ) end local function month_length ( m , y ) + m = ( m - 1 ) % 12 + 1 if m == 2 then return is_leap ( y ) and 29 or 28 else @@ -27,9 +29,13 @@ local function month_length ( m , y ) end end +local function leap_years_since ( year ) + return idiv ( year , 4 ) - idiv ( year , 100 ) + idiv ( year , 400 ) +end + local function doomsday ( year ) return ( 3 -- Tuesday - - 1 + year + idiv ( year , 4 ) - idiv ( year , 100 ) + idiv ( year , 400 ) ) + - 1 + year + leap_years_since ( year ) ) % 7 + 1 end local doomsday_cache = setmetatable ( { } , { @@ -63,14 +69,19 @@ local function increment ( tens , units , base ) 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" ) +local function unpack_tm ( tm ) + return assert ( tm.year , "year required" ) , + assert ( tm.month , "month required" ) , + assert ( tm.day , "day required" ) , + tm.hour or 12 , + tm.min or 0 , + tm.sec or 0 , + tm.yday , + tm.wday +end +-- Modify parameters so they all fit within the "normal" range +local function normalise ( year , month , day , hour , min , sec ) min , sec = increment ( min , sec , 60 ) -- TODO: consider leap seconds? hour , min = increment ( hour , min , 60 ) day , hour = increment ( day , hour , 24 ) @@ -92,23 +103,106 @@ local function normalise ( tm ) 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 + return year , month , day , hour , min , sec +end + +local leap_years_since_1970 = leap_years_since ( 1970 ) +local function timestamp ( year , month , day , hour , min , sec ) + year , month , day , hour , min , sec = normalise ( year , month , day , hour , min , sec ) + + local days_since_epoch = day_of_year ( day , month , year ) + + 365 * ( year - 1970 ) + -- Each leap year adds one day + + ( leap_years_since ( year - 1 ) - leap_years_since_1970 ) - 1 + + return days_since_epoch * (60*60*24) + + hour * (60*60) + + min * 60 + + sec +end + + +local timetable_methods = { } + +function timetable_methods:normalise ( ) + local year , month , day + year , month , day , self.hour , self.min , self.sec = normalise ( unpack_tm ( self ) ) + + self.day = day + self.month = month + self.year = year local yday = day_of_year ( day , month , year ) local wday = day_of_week ( yday , year ) - tm.yday = yday - tm.wday = wday + self.yday = yday + self.wday = wday + + return self +end +timetable_methods.normalize = timetable_methods.normalise -- American English + +function timetable_methods:timestamp ( ) + return timestamp ( unpack_tm ( self ) ) +end - return tm +function timetable_methods:rfc_3339 ( ) + -- %06.4g gives 3 (=6-4+1) digits after decimal + return strformat ( "%04u-%02u-%02uT%02u:%02u:%06.4g" , unpack_tm ( self ) ) +end + +local timetable_mt + +local function coerce_arg ( t ) + if getmetatable ( t ) == timetable_mt then + return t:timestamp ( ) + end + return t +end + +timetable_mt = { + __index = timetable_methods ; + __tostring = timetable_methods.rfc_3339 ; + __eq = function ( a , b ) + return coerce_arg ( a ) == coerce_arg ( b ) + end ; + __lt = function ( a , b ) + return coerce_arg ( a ) < coerce_arg ( b ) + end ; +} + +local function cast_timetable ( tm ) + return setmetatable ( tm , timetable_mt ) +end + +local function new_timetable ( year , month , day , hour , min , sec , yday , wday ) + return cast_timetable { + year = year ; + month = month ; + day = day ; + hour = hour ; + min = min ; + sec = sec ; + yday = yday ; + wday = wday ; + } +end + +function timetable_methods:clone ( ) + return new_timetable ( unpack_tm ( self ) ) +end + +local function new_from_timestamp ( ts ) + return new_timetable ( 1970 , 1 , 1 , 0 , 0 , ts ) end return { doomsday = doomsday ; normalise = normalise ; + timestamp = timestamp ; + + new = new_timetable ; + new_from_timestamp = new_from_timestamp ; + cast = cast_timetable ; + timetable_mt = timetable_mt ; }