diff --git a/utils/timestamp.lua b/utils/timestamp.lua index 80548b92..3002b1e4 100644 --- a/utils/timestamp.lua +++ b/utils/timestamp.lua @@ -4,6 +4,7 @@ local Public = {} local floor = math.floor +local strformat = string.format local function borrow(tens, units, base) local frac = tens % 1 @@ -35,6 +36,12 @@ 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 month_length(m, y) if m == 2 then return is_leap(y) and 29 or 28 @@ -43,12 +50,21 @@ local function month_length(m, y) end end -local function normalise(sec) - local year = 1970 - local month = 1 - local day = 1 - local hour = 0 - local min = 0 +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 leap_years_since(year) + return floor(year / 4) - floor(year / 100) + floor(year / 400) +end + +local leap_years_since_1970 = leap_years_since(1970) + +local function normalise(year, month, day, hour, min, sec) -- `month` and `day` start from 1, need -1 and +1 so it works modulo month, day = month - 1, day - 1 @@ -105,14 +121,32 @@ end --- Converts unix epoch timestamp into table {year: number, month: number, day: number, hour: number, min: number, sec: number} -- @param sec unix epoch timestamp -- @return {year: number, month: number, day: number, hour: number, min: number, sec: number} -Public.to_timetable = normalise +function Public.to_timetable(secs) + return normalise(1970, 1, 1, 0, 0, secs) +end + +--- Converts timetable into unix epoch timestamp +-- @param timetable {year: number, month: number, day: number, hour: number, min: number, sec: number} +-- @return number +function Public.from_timetable(timetable) + local tt = normalise(timetable.year, timetable.month, timetable.day, timetable.hour, timetable.min, timetable.sec) + + local year, month, day, hour, min, sec = tt.year, tt.month, tt.day, tt.hour, tt.min, tt.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 --- Converts unix epoch timestamp into human readable string. -- @param secs unix epoch timestamp -- @return string function Public.to_string(secs) local tt = normalise(secs) - return table.concat({tt.year, '-', tt.month, '-', tt.day, ' ', tt.hour, ':', tt.min, ':', tt.sec}) + return strformat('%04u-%02u-%02u %02u:%02u:%02d', tt.year, tt.month, tt.day, tt.hour, tt.min, tt.sec) end return Public