local luatz = require "luatz"
-- We do this a few times ==> Convert a timestamp to timetable and normalise
-local function ts2tt ( ts )
- return luatz.timetable.new_from_timestamp ( ts )
+local function ts2tt(ts)
+ return luatz.timetable.new_from_timestamp(ts)
end
-- Get the current time in UTC
-local utcnow = luatz.time ( )
-local now = ts2tt ( utcnow )
-print ( now , "now (UTC)" )
+local utcnow = luatz.time()
+local now = ts2tt(utcnow)
+print(now, "now (UTC)")
-- Get a new time object 6 months from now
-local x = now:clone ( )
+local x = now:clone()
x.month = x.month + 6
-x:normalise ( )
-print ( x , "6 months from now" )
+x:normalise()
+print(x, "6 months from now")
-- Find out what time it is in Melbourne at the moment
-local melbourne = luatz.get_tz ( "Australia/Melbourne" )
-local now_in_melbourne = ts2tt ( melbourne:localise ( utcnow ) )
-print ( now_in_melbourne , "Melbourne" )
+local melbourne = luatz.get_tz("Australia/Melbourne")
+local now_in_melbourne = ts2tt(melbourne:localise(utcnow))
+print(now_in_melbourne, "Melbourne")
-- Six months from now in melbourne (so month is incremented; but still the same time)
-local m = now_in_melbourne:clone ( )
+local m = now_in_melbourne:clone()
m.month = m.month + 6
-m:normalise ( )
-print ( m , "6 months from now in melbourne" )
+m:normalise()
+print(m, "6 months from now in melbourne")
-- Convert time back to utc; a daylight savings transition may have taken place!
-- There may be 2 results, but for we'll ignore the second possibility
-local c , _ = melbourne:utctime ( m:timestamp( ) )
-print ( ts2tt ( c ) , "6 months from now in melbourne converted to utc" )
+local c, _ = melbourne:utctime(m:timestamp())
+print(ts2tt(c), "6 months from now in melbourne converted to utc")
local new_from_timestamp = require "luatz.timetable".new_from_timestamp
local get_tz = require "luatz.tzcache".get_tz
-local function os_date ( format_string , timestamp )
+local function os_date(format_string, timestamp)
format_string = format_string or "%c"
- timestamp = timestamp or gettime ( )
- if format_string:sub ( 1 , 1 ) == "!" then -- UTC
- format_string = format_string:sub ( 2 )
+ timestamp = timestamp or gettime()
+ if format_string:sub(1, 1) == "!" then -- UTC
+ format_string = format_string:sub(2)
else -- Localtime
- timestamp = get_tz ( ):localise ( timestamp )
+ timestamp = get_tz():localise(timestamp)
end
- local tt = new_from_timestamp ( timestamp )
+ local tt = new_from_timestamp(timestamp)
if format_string == "*t" then
return tt
else
- return tt:strftime ( format_string )
+ return tt:strftime(format_string)
end
end
-local _M = { }
+local _M = {}
-_M.source , _M.resolution , _M.gettime = (function()
- local has_syscall , syscall = pcall ( require , "syscall" )
+_M.source, _M.resolution, _M.gettime = (function()
+ local has_syscall, syscall = pcall(require, "syscall")
if has_syscall and syscall.clock_gettime and syscall.c.CLOCK then
local clock_id = syscall.c.CLOCK.REALTIME
- local function timespec_to_number ( timespec )
- return tonumber ( timespec.tv_sec ) + tonumber ( timespec.tv_nsec ) * 1e-9
+ local function timespec_to_number(timespec)
+ return tonumber(timespec.tv_sec) + tonumber(timespec.tv_nsec) * 1e-9
end
- return "syscall.clock_gettime(CLOCK_REALTIME)" ,
- syscall.clock_getres and timespec_to_number ( syscall.clock_getres ( clock_id ) ) or 1e-9 ,
- function ( )
- return timespec_to_number ( syscall.clock_gettime ( clock_id ) )
+ return "syscall.clock_gettime(CLOCK_REALTIME)",
+ syscall.clock_getres and timespec_to_number(syscall.clock_getres(clock_id)) or 1e-9,
+ function()
+ return timespec_to_number(syscall.clock_gettime(clock_id))
end
end
- local has_unix , unix = pcall ( require , "unix" )
+ local has_unix, unix = pcall(require, "unix")
-- On Apple devices lunix only uses gettimeofday()
if has_unix and unix.clock_gettime and unix.uname and unix.uname().sysname ~= "Darwin" then
- return "unix.clock_gettime(CLOCK_REALTIME)" , 1e-9 , function ( )
- return unix.clock_gettime ( )
+ return "unix.clock_gettime(CLOCK_REALTIME)", 1e-9, function()
+ return unix.clock_gettime()
end
end
if has_syscall and syscall.gettimeofday then
- local function timeval_to_number ( timeval )
- return tonumber ( timeval.tv_sec ) + tonumber ( timeval.tv_nsec ) * 1e-6
+ local function timeval_to_number(timeval)
+ return tonumber(timeval.tv_sec) + tonumber(timeval.tv_nsec) * 1e-6
end
- return "syscall.gettimeofday()" , 1e-6 ,
- function ( )
- return timeval_to_number ( syscall.gettimeofday ( ) )
+ return "syscall.gettimeofday()", 1e-6,
+ function()
+ return timeval_to_number(syscall.gettimeofday())
end
end
return "unix.gettimeofday()", 1e-6, unix.gettimeofday
end
- local has_socket , socket = pcall ( require , "socket" )
+ local has_socket, socket = pcall(require, "socket")
if has_socket and socket.gettime then
-- on windows, this uses GetSystemTimeAsFileTime, which has resolution of 1e-7
-- on linux, this uses gettimeofday, which has resolution of 1e-6
- return "socket.gettime()" , 1e-6 , socket.gettime
+ return "socket.gettime()", 1e-6, socket.gettime
end
if ngx and ngx.now then -- luacheck: ignore 113
- return "ngx.now()" , 1e-3 , ngx.now -- luacheck: ignore 113
+ return "ngx.now()", 1e-3, ngx.now -- luacheck: ignore 113
end
- return "os.time()" , 1 , os.time
+ return "os.time()", 1, os.time
end)()
return _M
local _M = {
- gettime = require "luatz.gettime" ;
- parse = require "luatz.parse" ;
- strftime = require "luatz.strftime" ;
- timetable = require "luatz.timetable" ;
- tzcache = require "luatz.tzcache" ;
+ gettime = require "luatz.gettime";
+ parse = require "luatz.parse";
+ strftime = require "luatz.strftime";
+ timetable = require "luatz.timetable";
+ tzcache = require "luatz.tzcache";
}
--- Top-level aliases for common functions
--- Handy functions
-_M.time_in = function ( tz , now )
- return _M.get_tz ( tz ):localize ( now )
+_M.time_in = function(tz, now)
+ return _M.get_tz(tz):localize(now)
end
-_M.now = function ( )
- local ts = _M.gettime.gettime ( )
- return _M.timetable.new_from_timestamp ( ts )
+_M.now = function()
+ local ts = _M.gettime.gettime()
+ return _M.timetable.new_from_timestamp(ts)
end
--- C-like functions
-_M.gmtime = function ( ts )
- return _M.timetable.new_from_timestamp ( ts )
+_M.gmtime = function(ts)
+ return _M.timetable.new_from_timestamp(ts)
end
-_M.localtime = function ( ts )
- ts = _M.time_in ( nil , ts )
- return _M.gmtime ( ts )
+_M.localtime = function(ts)
+ ts = _M.time_in(nil, ts)
+ return _M.gmtime(ts)
end
-_M.ctime = function ( ts )
- return _M.strftime.asctime ( _M.localtime ( ts ) )
+_M.ctime = function(ts)
+ return _M.strftime.asctime(_M.localtime(ts))
end
return _M
-- Return value is not normalised (this preserves a leap second)
-- If the timestamp is only partial (i.e. missing "Z" or time offset) then `tz_offset` will be nil
-- TODO: Validate components are within their boundarys (e.g. 1 <= month <= 12)
-local function rfc_3339 ( str , init )
- local year , month , day , hour , min , sec , patt_end = str:match ( "^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)()" , init ) -- luacheck: ignore 631
+local function rfc_3339(str, init)
+ local year, month, day, hour, min, sec, patt_end = str:match("^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)()", init) -- luacheck: ignore 631
if not year then
return nil, "Invalid RFC 3339 timestamp"
end
- year = tonumber ( year )
- month = tonumber ( month )
- day = tonumber ( day )
- hour = tonumber ( hour )
- min = tonumber ( min )
- sec = tonumber ( sec )
+ year = tonumber(year)
+ month = tonumber(month)
+ day = tonumber(day)
+ hour = tonumber(hour)
+ min = tonumber(min)
+ sec = tonumber(sec)
- local tt = new_timetable ( year , month , day , hour , min , sec )
+ local tt = new_timetable(year, month, day, hour, min, sec)
local tz_offset
- if str:match ("^[Zz]" , patt_end ) then
+ if str:match("^[Zz]", patt_end) then
tz_offset = 0
else
- local hour_offset , min_offset = str:match ( "^([+-]%d%d):(%d%d)" , patt_end )
+ local hour_offset, min_offset = str:match("^([+-]%d%d):(%d%d)", patt_end)
if hour_offset then
- tz_offset = tonumber ( hour_offset ) * 3600 + tonumber ( min_offset ) * 60
+ tz_offset = tonumber(hour_offset) * 3600 + tonumber(min_offset) * 60
else -- luacheck: ignore 542
-- Invalid RFC 3339 timestamp offset (should be Z or (+/-)hour:min)
-- tz_offset will be nil
end
end
- return tt , tz_offset
+ return tt, tz_offset
end
return {
- rfc_3339 = rfc_3339 ;
+ rfc_3339 = rfc_3339;
}
local strformat = string.format
local floor = math.floor
-local function idiv ( n , d )
- return floor ( n / d )
+local function idiv(n, d)
+ return floor(n / d)
end
local c_locale = {
- abday = { "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" } ;
- day = { "Sunday" , "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" } ;
- abmon = { "Jan" , "Feb" , "Mar" , "Apr" , "May" , "Jun" , "Jul" , "Aug" , "Sep" , "Oct" , "Nov" , "Dec" } ;
- mon = { "January" , "February" , "March" , "April" , "May" , "June" ,
- "July" , "August" , "September" , "October" , "November" , "December" } ;
- am_pm = { "AM" , "PM" } ;
+ abday = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+ day = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
+ abmon = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ mon = {"January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"};
+ am_pm = {"AM", "PM"};
}
--- ISO-8601 week logic
-- ISO 8601 weekday as number with Monday as 1 (1-7)
-local function iso_8601_weekday ( wday )
+local function iso_8601_weekday(wday)
if wday == 1 then
return 7
else
end
local iso_8601_week do
-- Years that have 53 weeks according to ISO-8601
- local long_years = { }
+ local long_years = {}
for _, v in ipairs {
4, 9, 15, 20, 26, 32, 37, 43, 48, 54, 60, 65, 71, 76, 82,
88, 93, 99, 105, 111, 116, 122, 128, 133, 139, 144, 150, 156, 161, 167,
257, 263, 268, 274, 280, 285, 291, 296, 303, 308, 314, 320, 325, 331, 336,
342, 348, 353, 359, 364, 370, 376, 381, 387, 392, 398
} do
- long_years [ v ] = true
+ long_years[v] = true
end
- local function is_long_year ( year )
- return long_years [ year % 400 ]
+ local function is_long_year(year)
+ return long_years[year % 400]
end
- function iso_8601_week ( self )
- local wday = iso_8601_weekday ( self.wday )
+ function iso_8601_week(self)
+ local wday = iso_8601_weekday(self.wday)
local n = self.yday - wday
local year = self.year
if n < -3 then
year = year - 1
- if is_long_year ( year ) then
- return year , 53 , wday
+ if is_long_year(year) then
+ return year, 53, wday
else
- return year , 52 , wday
+ return year, 52, wday
end
- elseif n >= 361 and not is_long_year ( year ) then
- return year + 1 , 1 , wday
+ elseif n >= 361 and not is_long_year(year) then
+ return year + 1, 1, wday
else
- return year , idiv ( n + 10 , 7 ) , wday
+ return year, idiv(n + 10, 7), wday
end
end
end
--- Specifiers
-local t = { }
-function t:a ( locale )
- return "%s" , locale.abday [ self.wday ]
+local t = {}
+function t:a(locale)
+ return "%s", locale.abday[self.wday]
end
-function t:A ( locale )
- return "%s" , locale.day [ self.wday ]
+function t:A(locale)
+ return "%s", locale.day[self.wday]
end
-function t:b ( locale )
- return "%s" , locale.abmon [ self.month ]
+function t:b(locale)
+ return "%s", locale.abmon[self.month]
end
-function t:B ( locale )
- return "%s" , locale.mon [ self.month ]
+function t:B(locale)
+ return "%s", locale.mon[self.month]
end
-function t:c ( locale )
- return "%.3s %.3s%3d %.2d:%.2d:%.2d %d" ,
- locale.abday [ self.wday ] , locale.abmon [ self.month ] ,
- self.day , self.hour , self.min , self.sec , self.year
+function t:c(locale)
+ return "%.3s %.3s%3d %.2d:%.2d:%.2d %d",
+ locale.abday[self.wday], locale.abmon[self.month],
+ self.day, self.hour, self.min, self.sec, self.year
end
-- Century
-function t:C ( )
- return "%02d" , idiv ( self.year , 100 )
+function t:C()
+ return "%02d", idiv(self.year, 100)
end
-function t:d ( )
- return "%02d" , self.day
+function t:d()
+ return "%02d", self.day
end
-- Short MM/DD/YY date, equivalent to %m/%d/%y
-function t:D ( )
- return "%02d/%02d/%02d" , self.month , self.day , self.year % 100
+function t:D()
+ return "%02d/%02d/%02d", self.month, self.day, self.year % 100
end
-function t:e ( )
- return "%2d" , self.day
+function t:e()
+ return "%2d", self.day
end
-- Short YYYY-MM-DD date, equivalent to %Y-%m-%d
-function t:F ( )
- return "%d-%02d-%02d" , self.year , self.month , self.day
+function t:F()
+ return "%d-%02d-%02d", self.year, self.month, self.day
end
-- Week-based year, last two digits (00-99)
-function t:g ( )
- return "%02d" , iso_8601_week ( self ) % 100
+function t:g()
+ return "%02d", iso_8601_week(self) % 100
end
-- Week-based year
-function t:G ( )
- return "%d" , iso_8601_week ( self )
+function t:G()
+ return "%d", iso_8601_week(self)
end
t.h = t.b
-function t:H ( )
- return "%02d" , self.hour
+function t:H()
+ return "%02d", self.hour
end
-function t:I ( )
- return "%02d" , (self.hour-1) % 12 + 1
+function t:I()
+ return "%02d", (self.hour-1) % 12 + 1
end
-function t:j ( )
- return "%03d" , self.yday
+function t:j()
+ return "%03d", self.yday
end
-function t:m ( )
- return "%02d" , self.month
+function t:m()
+ return "%02d", self.month
end
-function t:M ( )
- return "%02d" , self.min
+function t:M()
+ return "%02d", self.min
end
-- New-line character ('\n')
-function t:n ( ) -- luacheck: ignore 212
+function t:n() -- luacheck: ignore 212
return "\n"
end
-function t:p ( locale )
+function t:p(locale)
return self.hour < 12 and locale.am_pm[1] or locale.am_pm[2]
end
-- TODO: should respect locale
-function t:r ( locale )
- return "%02d:%02d:%02d %s" ,
- (self.hour-1) % 12 + 1 , self.min , self.sec ,
+function t:r(locale)
+ return "%02d:%02d:%02d %s",
+ (self.hour-1) % 12 + 1, self.min, self.sec,
self.hour < 12 and locale.am_pm[1] or locale.am_pm[2]
end
-- 24-hour HH:MM time, equivalent to %H:%M
-function t:R ( )
- return "%02d:%02d" , self.hour , self.min
+function t:R()
+ return "%02d:%02d", self.hour, self.min
end
-function t:s ( )
- return "%d" , self:timestamp ( )
+function t:s()
+ return "%d", self:timestamp()
end
-function t:S ( )
- return "%02d" , self.sec
+function t:S()
+ return "%02d", self.sec
end
-- Horizontal-tab character ('\t')
-function t:t ( ) -- luacheck: ignore 212
+function t:t() -- luacheck: ignore 212
return "\t"
end
-- ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
-function t:T ( )
- return "%02d:%02d:%02d" , self.hour , self.min , self.sec
+function t:T()
+ return "%02d:%02d:%02d", self.hour, self.min, self.sec
end
-function t:u ( )
- return "%d" , iso_8601_weekday ( self.wday )
+function t:u()
+ return "%d", iso_8601_weekday(self.wday)
end
-- Week number with the first Sunday as the first day of week one (00-53)
-function t:U ( )
- return "%02d" , idiv ( self.yday - self.wday + 7 , 7 )
+function t:U()
+ return "%02d", idiv(self.yday - self.wday + 7, 7)
end
-- ISO 8601 week number (00-53)
-function t:V ( )
- return "%02d" , select ( 2 , iso_8601_week ( self ) )
+function t:V()
+ return "%02d", select(2, iso_8601_week(self))
end
-- Weekday as a decimal number with Sunday as 0 (0-6)
-function t:w ( )
- return "%d" , self.wday - 1
+function t:w()
+ return "%d", self.wday - 1
end
-- Week number with the first Monday as the first day of week one (00-53)
-function t:W ( )
- return "%02d" , idiv ( self.yday - iso_8601_weekday ( self.wday ) + 7 , 7 )
+function t:W()
+ return "%02d", idiv(self.yday - iso_8601_weekday(self.wday) + 7, 7)
end
-- TODO make t.x and t.X respect locale
t.x = t.D
t.X = t.T
-function t:y ( )
- return "%02d" , self.year % 100
+function t:y()
+ return "%02d", self.year % 100
end
-function t:Y ( )
- return "%d" , self.year
+function t:Y()
+ return "%d", self.year
end
-- TODO timezones
-function t:z ( ) -- luacheck: ignore 212
+function t:z() -- luacheck: ignore 212
return "+0000"
end
-function t:Z ( ) -- luacheck: ignore 212
+function t:Z() -- luacheck: ignore 212
return "GMT"
end
-- A literal '%' character.
-t["%"] = function ( self ) -- luacheck: ignore 212
+t["%"] = function(self) -- luacheck: ignore 212
return "%%"
end
-local function strftime ( format_string , timetable )
- return ( string.gsub ( format_string , "%%([EO]?)(.)" , function ( locale_modifier , specifier )
- local func = t [ specifier ]
+local function strftime(format_string, timetable)
+ return (string.gsub(format_string, "%%([EO]?)(.)", function(locale_modifier, specifier)
+ local func = t[specifier]
if func then
- return strformat ( func ( timetable , c_locale ) )
+ return strformat(func(timetable, c_locale))
else
- error ( "invalid conversation specifier '%"..locale_modifier..specifier.."'" , 3 )
+ error("invalid conversation specifier '%"..locale_modifier..specifier.."'", 3)
end
- end ) )
+ end))
end
-local function asctime ( timetable )
+local function asctime(timetable)
-- Equivalent to the format string "%c\n"
- return strformat ( t.c ( timetable , c_locale ) ) .. "\n"
+ return strformat(t.c(timetable, c_locale)) .. "\n"
end
return {
- strftime = strftime ;
- asctime = asctime ;
+ strftime = strftime;
+ asctime = asctime;
}
end
-local mon_lengths = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+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 }
+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 ]
+ months_to_days_cumulative[i] = months_to_days_cumulative[i-1] + mon_lengths[i-1]
end
-- For Sakamoto's Algorithm (day of week)
local sakamoto = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
-local function is_leap ( y )
+local function is_leap(y)
if (y % 4) ~= 0 then
return false
elseif (y % 100) ~= 0 then
end
end
-local function month_length ( m , y )
+local function month_length(m, y)
if m == 2 then
- return is_leap ( y ) and 29 or 28
+ return is_leap(y) and 29 or 28
else
- return mon_lengths [ m ]
+ return mon_lengths[m]
end
end
-local function leap_years_since ( year )
- return idiv ( year , 4 ) - idiv ( year , 100 ) + idiv ( year , 400 )
+local function leap_years_since(year)
+ return idiv(year, 4) - idiv(year, 100) + idiv(year, 400)
end
-local function day_of_year ( day , month , year )
- local yday = months_to_days_cumulative [ month ]
- if month > 2 and is_leap ( year ) then
+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 ( day , month , year )
+local function day_of_week(day, month, year)
if month < 3 then
year = year - 1
end
- return ( year + leap_years_since ( year ) + sakamoto[month] + day ) % 7 + 1
+ return(year + leap_years_since(year) + sakamoto[month] + day) % 7 + 1
end
-local function borrow ( tens , units , base )
+local function borrow(tens, units, base)
local frac = tens % 1
units = units + frac * base
tens = tens - frac
- return tens , units
+ return tens, units
end
-local function carry ( tens , units , base )
+local function carry(tens, units, base)
if units >= base then
- tens = tens + idiv ( units , base )
+ tens = tens + idiv(units, base)
units = units % base
elseif units < 0 then
- tens = tens + idiv ( units , base )
- units = ( base + units ) % base
+ tens = tens + idiv(units, base)
+ units = (base + units) % base
end
- return tens , units
+ return tens, units
end
-- Modify parameters so they all fit within the "normal" range
-local function normalise ( year , month , day , hour , min , sec )
+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
+ month, day = month - 1, day - 1
-- Convert everything (except seconds) to an integer
-- by propagating fractional components down.
- year , month = borrow ( year , month , 12 )
+ year , month = borrow(year , month, 12)
-- Carry from month to year first, so we get month length correct in next line around leap years
- year , month = carry ( year , month , 12 )
- month , day = borrow ( month , day , month_length ( floor ( month + 1 ) , year ) )
- day , hour = borrow ( day , hour , 24 )
- hour , min = borrow ( hour , min , 60 )
- min , sec = borrow ( min , sec , 60 )
+ year , month = carry(year, month, 12)
+ month, day = borrow(month, day , month_length(floor(month + 1), year))
+ day , hour = borrow(day , hour , 24)
+ hour , min = borrow(hour , min , 60)
+ min , sec = borrow(min , sec , 60)
-- Propagate out of range values up
-- e.g. if `min` is 70, `hour` increments by 1 and `min` becomes 10
-- This has to happen for all columns after borrowing, as lower radixes may be pushed out of range
- min , sec = carry ( min , sec , 60 ) -- TODO: consider leap seconds?
- hour , min = carry ( hour , min , 60 )
- day , hour = carry ( day , hour , 24 )
+ min , sec = carry(min , sec , 60) -- TODO: consider leap seconds?
+ hour , min = carry(hour, min , 60)
+ day , hour = carry(day , hour, 24)
-- Ensure `day` is not underflowed
-- Add a whole year of days at a time, this is later resolved by adding months
-- TODO[OPTIMIZE]: This could be slow if `day` is far out of range
year = year - 1
month = 11
end
- day = day + month_length ( month + 1 , year )
+ day = day + month_length(month + 1, year)
end
- year , month = carry ( year , month , 12 )
+ year, month = carry(year, month, 12)
-- TODO[OPTIMIZE]: This could potentially be slow if `day` is very large
while true do
- local i = month_length ( month + 1 , year )
+ local i = month_length(month + 1, year)
if day < i then break end
day = day - i
month = month + 1
-- Now we can place `day` and `month` back in their normal ranges
-- e.g. month as 1-12 instead of 0-11
- month , day = month + 1 , day + 1
+ month, day = month + 1, day + 1
- return year , month , day , hour , min , sec
+ 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 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 )
+ 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
+ + (leap_years_since(year - 1) - leap_years_since_1970) - 1
return days_since_epoch * (60*60*24)
- + hour * (60*60)
- + min * 60
+ + hour * (60*60)
+ + min * 60
+ sec
end
-local timetable_methods = { }
+local timetable_methods = {}
-function timetable_methods:unpack ( )
- return assert ( self.year , "year required" ) ,
- assert ( self.month , "month required" ) ,
- assert ( self.day , "day required" ) ,
- self.hour or 12 ,
- self.min or 0 ,
- self.sec or 0 ,
- self.yday ,
+function timetable_methods:unpack()
+ return assert(self.year , "year required"),
+ assert(self.month, "month required"),
+ assert(self.day , "day required"),
+ self.hour or 12,
+ self.min or 0,
+ self.sec or 0,
+ self.yday,
self.wday
end
-function timetable_methods:normalise ( )
- local year , month , day
- year , month , day , self.hour , self.min , self.sec = normalise ( self:unpack ( ) )
+function timetable_methods:normalise()
+ local year, month, day
+ year, month, day, self.hour, self.min, self.sec = normalise(self:unpack())
self.day = day
self.month = month
self.year = year
- self.yday = day_of_year ( day , month , year )
- self.wday = day_of_week ( day , month , year )
+ self.yday = day_of_year(day, month, year)
+ self.wday = day_of_week(day, month, year)
return self
end
timetable_methods.normalize = timetable_methods.normalise -- American English
-function timetable_methods:timestamp ( )
- return timestamp ( self:unpack ( ) )
+function timetable_methods:timestamp()
+ return timestamp(self:unpack())
end
-function timetable_methods:rfc_3339 ( )
+function timetable_methods:rfc_3339()
local year, month, day, hour, min, fsec = self:unpack()
local sec, msec = borrow(fsec, 0, 1000)
msec = math.floor(msec)
- return strformat ( "%04u-%02u-%02uT%02u:%02u:%02d.%03d" , year , month , day , hour , min , sec , msec )
+ return strformat("%04u-%02u-%02uT%02u:%02u:%02d.%03d", year, month, day, hour, min, sec, msec)
end
-function timetable_methods:strftime ( format_string )
- return strftime ( format_string , self )
+function timetable_methods:strftime(format_string)
+ return strftime(format_string, self)
end
local timetable_mt
-local function coerce_arg ( t )
- if getmetatable ( t ) == timetable_mt then
- return t:timestamp ( )
+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 a:timestamp ( ) == b:timestamp ( )
- end ;
- __lt = function ( a , b )
- return a:timestamp ( ) < b:timestamp ( )
- end ;
- __sub = function ( a , b )
- return coerce_arg ( a ) - coerce_arg ( b )
- end ;
+ __index = timetable_methods;
+ __tostring = timetable_methods.rfc_3339;
+ __eq = function(a, b)
+ return a:timestamp() == b:timestamp()
+ end;
+ __lt = function(a, b)
+ return a:timestamp() < b:timestamp()
+ end;
+ __sub = function(a, b)
+ return coerce_arg(a) - coerce_arg(b)
+ end;
}
-local function cast_timetable ( tm )
- return setmetatable ( tm , timetable_mt )
+local function cast_timetable(tm)
+ return setmetatable(tm, timetable_mt)
end
-local function new_timetable ( year , month , day , hour , min , sec , yday , wday )
+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 ;
+ year = year;
+ month = month;
+ day = day;
+ hour = hour;
+ min = min;
+ sec = sec;
+ yday = yday;
+ wday = wday;
}
end
-function timetable_methods:clone ( )
- return new_timetable ( self:unpack ( ) )
+function timetable_methods:clone()
+ return new_timetable(self:unpack())
end
-local function new_from_timestamp ( ts )
- if type ( ts ) ~= "number" then
- error ( "bad argument #1 to 'new_from_timestamp' (number expected, got " .. type ( ts ) .. ")" , 2 )
+local function new_from_timestamp(ts)
+ if type(ts) ~= "number" then
+ error("bad argument #1 to 'new_from_timestamp' (number expected, got " .. type(ts) .. ")", 2)
end
- return new_timetable ( 1970 , 1 , 1 , 0 , 0 , ts ):normalise ( )
+ return new_timetable(1970, 1, 1, 0, 0, ts):normalise()
end
return {
- is_leap = is_leap ;
- day_of_year = day_of_year ;
- day_of_week = day_of_week ;
- normalise = normalise ;
- timestamp = timestamp ;
-
- new = new_timetable ;
- new_from_timestamp = new_from_timestamp ;
- cast = cast_timetable ;
- timetable_mt = timetable_mt ;
+ is_leap = is_leap;
+ day_of_year = day_of_year;
+ day_of_week = day_of_week;
+ normalise = normalise;
+ timestamp = timestamp;
+
+ new = new_timetable;
+ new_from_timestamp = new_from_timestamp;
+ cast = cast_timetable;
+ timetable_mt = timetable_mt;
}
local base_zoneinfo_path = "/usr/share/zoneinfo/"
local local_zoneinfo_path = "/etc/localtime"
-local tz_cache = { }
+local tz_cache = {}
-local function name_to_zoneinfo_path ( name )
+local function name_to_zoneinfo_path(name)
if name == nil then
return local_zoneinfo_path
- elseif name:sub ( 1 , 1 ) == "/" then
+ elseif name:sub(1, 1) == "/" then
return name
else
return base_zoneinfo_path .. name
end
end
-local function clear_tz_cache ( name )
- tz_cache [ name_to_zoneinfo_path ( name ) ] = nil
+local function clear_tz_cache(name)
+ tz_cache[name_to_zoneinfo_path(name)] = nil
end
-local function get_tz ( name )
- local path = name_to_zoneinfo_path ( name )
+local function get_tz(name)
+ local path = name_to_zoneinfo_path(name)
-- TODO: stat path
- local tzinfo = tz_cache [ path ]
+ local tzinfo = tz_cache[path]
if tzinfo == nil then
- tzinfo = read_tzfile ( path )
- tz_cache [ path ] = tzinfo
+ tzinfo = read_tzfile(path)
+ tz_cache[path] = tzinfo
end
return tzinfo
end
return {
- get_tz = get_tz ;
- clear_tz_cache = clear_tz_cache ;
+ get_tz = get_tz;
+ clear_tz_cache = clear_tz_cache;
}
end
end
-local function read_flags ( fd , n )
- local data , err = fd:read ( n )
- if data == nil then return nil , err end
+local function read_flags(fd, n)
+ local data, err = fd:read(n)
+ if data == nil then return nil, err end
- local res = { }
+ local res = {}
for i=1, n do
res[i] = data:byte(i,i) ~= 0
end
end
local fifteen_nulls = ("\0"):rep(15)
-local function read_tz ( fd )
- assert ( fd:read(4) == "TZif" , "Invalid TZ file" )
- local version = assert ( fd:read(1) )
+local function read_tz(fd)
+ assert(fd:read(4) == "TZif", "Invalid TZ file")
+ local version = assert(fd:read(1))
if version == "\0" or version == "2" or version == "3" then
local MIN_TIME = -2^32+1
- assert ( assert ( fd:read(15) ) == fifteen_nulls , "Expected 15 nulls" )
+ assert(assert(fd:read(15)) == fifteen_nulls, "Expected 15 nulls")
-- The number of UTC/local indicators stored in the file.
- local tzh_ttisgmtcnt = assert ( read_int32be ( fd ) )
+ local tzh_ttisgmtcnt = assert(read_int32be(fd))
-- The number of standard/wall indicators stored in the file.
- local tzh_ttisstdcnt = assert ( read_int32be ( fd ) )
+ local tzh_ttisstdcnt = assert(read_int32be(fd))
-- The number of leap seconds for which data is stored in the file.
- local tzh_leapcnt = assert ( read_int32be ( fd ) )
+ local tzh_leapcnt = assert(read_int32be(fd))
-- The number of "transition times" for which data is stored in the file.
- local tzh_timecnt = assert ( read_int32be ( fd ) )
+ local tzh_timecnt = assert(read_int32be(fd))
-- The number of "local time types" for which data is stored in the file (must not be zero).
- local tzh_typecnt = assert ( read_int32be ( fd ) )
+ local tzh_typecnt = assert(read_int32be(fd))
-- The number of characters of "timezone abbreviation strings" stored in the file.
- local tzh_charcnt = assert ( read_int32be ( fd ) )
+ local tzh_charcnt = assert(read_int32be(fd))
- local transition_times = { }
+ local transition_times = {}
for i=1, tzh_timecnt do
- transition_times [ i ] = assert ( read_int32be ( fd ) )
+ transition_times[i] = assert(read_int32be(fd))
end
- local transition_time_ind = { assert ( fd:read ( tzh_timecnt ) ):byte ( 1 , -1 ) }
+ local transition_time_ind = {assert(fd:read(tzh_timecnt)):byte(1, -1)}
- local ttinfos = { }
+ local ttinfos = {}
for i=1, tzh_typecnt do
- ttinfos [ i ] = {
- gmtoff = assert ( read_int32be ( fd ) ) ;
- isdst = assert ( fd:read ( 1 ) ) ~= "\0" ;
- abbrind = assert ( fd:read ( 1 ) ):byte ( ) ;
+ ttinfos[i] = {
+ gmtoff = assert(read_int32be(fd));
+ isdst = assert(fd:read(1)) ~= "\0";
+ abbrind = assert(fd:read(1)):byte();
}
end
- local abbreviations = assert ( fd:read ( tzh_charcnt ) )
+ local abbreviations = assert(fd:read(tzh_charcnt))
- local leap_seconds = { } -- luacheck: ignore 241
+ local leap_seconds = {} -- luacheck: ignore 241
for i=1, tzh_leapcnt do
- leap_seconds [ i ] = {
- offset = assert ( read_int32be ( fd ) ) ;
- n = assert ( read_int32be ( fd ) ) ;
+ leap_seconds[i] = {
+ offset = assert(read_int32be(fd));
+ n = assert(read_int32be(fd));
}
end
- local isstd = assert ( read_flags ( fd , tzh_ttisstdcnt ) )
+ local isstd = assert(read_flags(fd, tzh_ttisstdcnt))
- local isgmt = assert ( read_flags ( fd , tzh_ttisgmtcnt ) )
+ local isgmt = assert(read_flags(fd, tzh_ttisgmtcnt))
local TZ
]]
assert(fd:read(4) == "TZif")
assert(fd:read(1) == version)
- assert ( assert ( fd:read(15) ) == fifteen_nulls , "Expected 15 nulls" )
+ assert(assert(fd:read(15)) == fifteen_nulls, "Expected 15 nulls")
MIN_TIME = -2^64+1
-- The number of UTC/local indicators stored in the file.
- tzh_ttisgmtcnt = assert ( read_int32be ( fd ) )
+ tzh_ttisgmtcnt = assert(read_int32be(fd))
-- The number of standard/wall indicators stored in the file.
- tzh_ttisstdcnt = assert ( read_int32be ( fd ) )
+ tzh_ttisstdcnt = assert(read_int32be(fd))
-- The number of leap seconds for which data is stored in the file.
- tzh_leapcnt = assert ( read_int32be ( fd ) )
+ tzh_leapcnt = assert(read_int32be(fd))
-- The number of "transition times" for which data is stored in the file.
- tzh_timecnt = assert ( read_int32be ( fd ) )
+ tzh_timecnt = assert(read_int32be(fd))
-- The number of "local time types" for which data is stored in the file (must not be zero).
- tzh_typecnt = assert ( read_int32be ( fd ) )
+ tzh_typecnt = assert(read_int32be(fd))
-- The number of characters of "timezone abbreviation strings" stored in the file.
- tzh_charcnt = assert ( read_int32be ( fd ) )
+ tzh_charcnt = assert(read_int32be(fd))
- transition_times = { }
+ transition_times = {}
for i=1, tzh_timecnt do
- transition_times [ i ] = assert ( read_int64be ( fd ) )
+ transition_times[i] = assert(read_int64be(fd))
end
- transition_time_ind = { assert ( fd:read ( tzh_timecnt ) ):byte ( 1 , -1 ) }
+ transition_time_ind = {assert(fd:read(tzh_timecnt)):byte(1, -1)}
- ttinfos = { }
+ ttinfos = {}
for i=1, tzh_typecnt do
- ttinfos [ i ] = {
- gmtoff = assert ( read_int32be ( fd ) ) ;
- isdst = assert ( fd:read ( 1 ) ) ~= "\0" ;
- abbrind = assert ( fd:read ( 1 ) ):byte ( ) ;
+ ttinfos[i] = {
+ gmtoff = assert(read_int32be(fd));
+ isdst = assert(fd:read(1)) ~= "\0";
+ abbrind = assert(fd:read(1)):byte();
}
end
- abbreviations = assert ( fd:read ( tzh_charcnt ) )
+ abbreviations = assert(fd:read(tzh_charcnt))
- leap_seconds = { }
+ leap_seconds = {}
for i=1, tzh_leapcnt do
- leap_seconds [ i ] = {
- offset = assert ( read_int64be ( fd ) ) ;
- n = assert ( read_int32be ( fd ) ) ;
+ leap_seconds[i] = {
+ offset = assert(read_int64be(fd));
+ n = assert(read_int32be(fd));
}
end
- isstd = assert ( read_flags ( fd , tzh_ttisstdcnt ) )
+ isstd = assert(read_flags(fd, tzh_ttisstdcnt))
- isgmt = assert ( read_flags ( fd , tzh_ttisgmtcnt ) )
+ isgmt = assert(read_flags(fd, tzh_ttisgmtcnt))
--[[
After the second header and data comes a newline-enclosed, POSIX-TZ-environment-variable-style string
between daylight saving and standard time.
]]
- assert ( assert ( fd:read ( 1 ) ) == "\n" , "Expected newline at end of version 2 header" )
+ assert(assert(fd:read(1)) == "\n", "Expected newline at end of version 2 header")
- TZ = assert ( fd:read ( "*l" ) )
+ TZ = assert(fd:read("*l"))
if #TZ == 0 then
TZ = nil
end
end
for i=1, tzh_typecnt do
- local v = ttinfos [ i ]
- v.abbr = abbreviations:sub ( v.abbrind+1 , v.abbrind+3 )
- v.isstd = isstd [ i ] or false
- v.isgmt = isgmt [ i ] or false
- setmetatable ( v , tt_info_mt )
+ local v = ttinfos[i]
+ v.abbr = abbreviations:sub(v.abbrind+1, v.abbrind+3)
+ v.isstd = isstd[i] or false
+ v.isgmt = isgmt[i] or false
+ setmetatable(v, tt_info_mt)
end
--[[
local res = {
future = TZ;
[0] = {
- transition_time = MIN_TIME ;
- info = ttinfos [ first ] ;
+ transition_time = MIN_TIME;
+ info = ttinfos[first];
}
}
for i=1, tzh_timecnt do
- res [ i ] = {
- transition_time = transition_times [ i ] ;
- info = ttinfos [ transition_time_ind [ i ]+1 ] ;
+ res[i] = {
+ transition_time = transition_times[i];
+ info = ttinfos[transition_time_ind[i]+1];
}
end
- return setmetatable ( res , tz_info_mt )
+ return setmetatable(res, tz_info_mt)
else
- error ( "Unsupported version" )
+ error("Unsupported version")
end
end
-local function read_tzfile ( path )
- local fd = assert ( io.open ( path , "rb" ) )
- local tzinfo = read_tz ( fd )
- fd:close ( )
+local function read_tzfile(path)
+ local fd = assert(io.open(path, "rb"))
+ local tzinfo = read_tz(fd)
+ fd:close()
return tzinfo
end
return {
- read_tz = read_tz ;
- read_tzfile = read_tzfile ;
+ read_tz = read_tz;
+ read_tzfile = read_tzfile;
}
local gettime = require "luatz.gettime".gettime
local timetable_mt = require "luatz.timetable".timetable_mt
-local function to_timestamp ( o )
- if type ( o ) == "number" then
+local function to_timestamp(o)
+ if type(o) == "number" then
return o
- elseif getmetatable ( o ) == timetable_mt then
- return o:timestamp ( )
+ elseif getmetatable(o) == timetable_mt then
+ return o:timestamp()
end
end
local tz_info_methods = { }
local tz_info_mt = {
__name = "luatz.tz_info";
- __index = tz_info_methods ;
+ __index = tz_info_methods;
}
local tt_info_mt = {
__name = "luatz.tt_info";
- __tostring = function ( self )
- return string.format ( "tt_info:%s=%d" , self.abbr , self.gmtoff )
- end ;
+ __tostring = function(self)
+ return string.format("tt_info:%s=%d", self.abbr, self.gmtoff)
+ end;
}
-- Binary search
-local function _find_current ( tzinfo , target , i , j )
+local function find_current(tzinfo, target, i, j)
if i >= j then return j end
- local half = math.ceil ( (j+i) / 2 )
+ local half = math.ceil((j+i) / 2)
- if target >= tzinfo [ half ].transition_time then
- return _find_current ( tzinfo , target , half , j )
+ if target >= tzinfo[half].transition_time then
+ return find_current(tzinfo, target, half, j)
else
- return _find_current ( tzinfo , target , i , half-1 )
+ return find_current(tzinfo, target, i, half-1)
end
end
-local function find_current_local ( tzinfo , ts_local )
+local function find_current_local(tzinfo, ts_local)
-- Find two best possibilities by searching back and forward a day (assumes transition is never by more than 24 hours)
- local tz_first = _find_current ( tzinfo , ts_local-86400 , 0 , #tzinfo )
- local tz_last = _find_current ( tzinfo , ts_local+86400 , 0 , #tzinfo )
+ local tz_first = find_current(tzinfo, ts_local-86400, 0, #tzinfo)
+ local tz_last = find_current(tzinfo, ts_local+86400, 0, #tzinfo)
local n_candidates = tz_last - tz_first + 1
if n_candidates == 1 then
return tz_first
elseif n_candidates == 2 then
- local tz_first_ob = tzinfo [ tz_first ]
- local tz_last_ob = tzinfo [ tz_last ]
+ local tz_first_ob = tzinfo[tz_first]
+ local tz_last_ob = tzinfo[tz_last]
local first_gmtoffset = tz_first_ob.info.gmtoff
local last_gmtoffset = tz_last_ob .info.gmtoff
end
-- If we get this far, the local time is ambiguous
- return tz_first , tz_last
+ return tz_first, tz_last
else
- error ( "Too many transitions in a 2 day period" )
+ error("Too many transitions in a 2 day period")
end
end
-function tz_info_methods:find_current ( current )
- current = assert ( to_timestamp ( current ) , "invalid timestamp to :find_current" )
- return self [ _find_current ( self , current , 0 , #self ) ].info
+function tz_info_methods:find_current(current)
+ current = assert(to_timestamp(current), "invalid timestamp to :find_current")
+ return self[find_current(self, current, 0, #self)].info
end
-function tz_info_methods:localise ( utc_ts )
- utc_ts = utc_ts or gettime ( )
- return utc_ts + self:find_current ( utc_ts ).gmtoff
+function tz_info_methods:localise(utc_ts)
+ utc_ts = utc_ts or gettime()
+ return utc_ts + self:find_current(utc_ts).gmtoff
end
tz_info_methods.localize = tz_info_methods.localise
-function tz_info_methods:utctime ( ts_local )
- ts_local = assert ( to_timestamp ( ts_local ) , "invalid timestamp to :utctime" )
- local tz1 , tz2 = find_current_local ( self , ts_local )
- tz1 = self [ tz1 ].info
+function tz_info_methods:utctime(ts_local)
+ ts_local = assert(to_timestamp(ts_local), "invalid timestamp to :utctime")
+ local tz1, tz2 = find_current_local(self, ts_local)
+ tz1 = self[tz1].info
if tz2 == nil then
return ts_local - tz1.gmtoff
else -- Local time is ambiguous
- tz2 = self [ tz2 ].info
+ tz2 = self[tz2].info
- return ts_local - tz2.gmtoff , ts_local - tz2.gmtoff
+ return ts_local - tz2.gmtoff, ts_local - tz2.gmtoff
end
end
return {
- tz_info_mt = tz_info_mt ;
- tt_info_mt = tt_info_mt ;
+ tz_info_mt = tz_info_mt;
+ tt_info_mt = tt_info_mt;
}
-describe ( "Time parsing library" , function ( )
+describe("Time parsing library", function()
local timetable = require "luatz.timetable"
local parse = require "luatz.parse"
- it ( "#RFC3339 parsing" , function ( )
- assert.same ( timetable.new(2013,10,22,14,17,02) , (parse.rfc_3339 "2013-10-22T14:17:02Z") )
+ it("#RFC3339 parsing", function()
+ assert.same(timetable.new(2013,10,22,14,17,02), (parse.rfc_3339 "2013-10-22T14:17:02Z"))
-- Numeric offsets accepted
- assert.same ( { timetable.new(2013,10,22,14,17,02) , 10*3600 } , { parse.rfc_3339 "2013-10-22T14:17:02+10:00" } )
+ assert.same({timetable.new(2013,10,22,14,17,02), 10*3600 }, {parse.rfc_3339 "2013-10-22T14:17:02+10:00" })
-- Missing offsets parse
- assert.same ( timetable.new(2013,10,22,14,17,02) , (parse.rfc_3339 "2013-10-22T14:17:02") )
+ assert.same(timetable.new(2013,10,22,14,17,02), (parse.rfc_3339 "2013-10-22T14:17:02"))
-- Invalid
assert.same(nil, (parse.rfc_3339 "an invalid timestamp"))
- end )
-end )
+ end)
+end)
-describe ( "Timetable library" , function ( )
+describe("Timetable library", function()
local timetable = require "luatz.timetable"
- local function native_normalise ( year , month , day )
- return os.date("*t",os.time{
- year = year ;
- month = month ;
- day = day ;
+ local function native_normalise(year, month, day)
+ return os.date("*t",os.time {
+ year = year;
+ month = month;
+ day = day;
})
end
- it ( "#is_leap is correct" , function ( )
- assert.same ( false , timetable.is_leap ( 1 ) )
- assert.same ( false , timetable.is_leap ( 3 ) )
- assert.same ( true , timetable.is_leap ( 4 ) )
- assert.same ( true , timetable.is_leap ( 2000 ) )
- assert.same ( true , timetable.is_leap ( 2004 ) )
- assert.same ( true , timetable.is_leap ( 2012 ) )
- assert.same ( false , timetable.is_leap ( 2013 ) )
- assert.same ( false , timetable.is_leap ( 2014 ) )
- assert.same ( false , timetable.is_leap ( 2100 ) )
- assert.same ( true , timetable.is_leap ( 2400 ) )
- end )
-
- it ( "#normalise gets #wday (day of week) correct" , function ( )
-
- local function assert_same_wday ( year , month , day )
- return assert.are.same (
- native_normalise ( year , month , day ).wday ,
- timetable.new ( year , month , day ):normalise().wday
+ it("#is_leap is correct", function()
+ assert.same(false, timetable.is_leap(1))
+ assert.same(false, timetable.is_leap(3))
+ assert.same(true , timetable.is_leap(4))
+ assert.same(true , timetable.is_leap(2000))
+ assert.same(true , timetable.is_leap(2004))
+ assert.same(true , timetable.is_leap(2012))
+ assert.same(false, timetable.is_leap(2013))
+ assert.same(false, timetable.is_leap(2014))
+ assert.same(false, timetable.is_leap(2100))
+ assert.same(true , timetable.is_leap(2400))
+ end)
+
+ it("#normalise gets #wday (day of week) correct", function()
+
+ local function assert_same_wday(year, month, day)
+ return assert.are.same(
+ native_normalise(year, month, day).wday,
+ timetable.new(year, month, day):normalise().wday
)
end
- assert_same_wday ( 2013 , 7 , 23 )
- assert_same_wday ( 2013 , 7 , 24 )
- assert_same_wday ( 2013 , 7 , 25 )
- assert_same_wday ( 2013 , 7 , 26 )
- assert_same_wday ( 2013 , 7 , 27 )
- assert_same_wday ( 2013 , 7 , 28 )
- assert_same_wday ( 2013 , 7 , 29 )
- assert_same_wday ( 2014 , 1 , 1 )
- assert_same_wday ( 2014 , 1 , 6 )
- assert_same_wday ( 2016 , 2 , 28 )
- assert_same_wday ( 2016 , 2 , 29 )
- assert_same_wday ( 2016 , 3 , 1 )
- end )
-
- local function native_timestamp ( year , month , day )
- return assert ( tonumber ( assert ( io.popen (
- string.format('date -u -d "%d-%d-%d" +%%s', year , month , day )
- ) ):read "*l" ) )
+ assert_same_wday(2013, 7, 23)
+ assert_same_wday(2013, 7, 24)
+ assert_same_wday(2013, 7, 25)
+ assert_same_wday(2013, 7, 26)
+ assert_same_wday(2013, 7, 27)
+ assert_same_wday(2013, 7, 28)
+ assert_same_wday(2013, 7, 29)
+ assert_same_wday(2014, 1, 1)
+ assert_same_wday(2014, 1, 6)
+ assert_same_wday(2016, 2, 28)
+ assert_same_wday(2016, 2, 29)
+ assert_same_wday(2016, 3, 1)
+ end)
+
+ local function native_timestamp(year, month, day)
+ return assert(tonumber(assert(io.popen(
+ string.format('date -u -d "%d-%d-%d" +%%s', year, month, day)
+ )):read "*l"))
end
- it ( "#timestamp creation is valid" , function ( )
+ it("#timestamp creation is valid", function()
for y=1950,2013 do
for m=1,12 do
- assert.same ( native_timestamp ( y,m,1 ) , timetable.timestamp(y,m,1,0,0,0) )
+ assert.same(native_timestamp(y,m,1), timetable.timestamp(y,m,1,0,0,0))
end
end
- end )
-
- it ( "#normalise handles out of range days in a year" , function ( )
- assert.same({ 2014,1,1,0,0,0 }, { timetable.normalise(2013,1,366,0,0,0) })
- assert.same({ 2014,2,4,0,0,0 }, { timetable.normalise(2013,1,400,0,0,0) })
- assert.same({ 2017,2,3,0,0,0 }, { timetable.normalise(2016,1,400,0,0,0) })
- assert.same({ 2016,3,5,0,0,0 }, { timetable.normalise(2015,1,430,0,0,0) })
- assert.same({ 2017,3,5,0,0,0 }, { timetable.normalise(2016,1,430,0,0,0) })
- assert.same({ 2027,5,18,0,0,0 }, { timetable.normalise(2000,1,10000,0,0,0) })
- assert.same({ 29379,1,25,0,0,0 }, { timetable.normalise(2000,1,10000000,0,0,0) })
- end )
-
- it ( "#normalise handles out of range days in a #month" , function ( )
- assert.same({ 2012,12,1,0,0,0 }, { timetable.normalise(2013,0,1,0,0,0) })
- assert.same({ 2016,6,1,0,0,0 }, { timetable.normalise(2013,42,1,0,0,0) })
+ end)
+
+ it("#normalise handles out of range days in a year", function()
+ assert.same({2014,1,1,0,0,0}, {timetable.normalise(2013,1,366,0,0,0)})
+ assert.same({2014,2,4,0,0,0}, {timetable.normalise(2013,1,400,0,0,0)})
+ assert.same({2017,2,3,0,0,0}, {timetable.normalise(2016,1,400,0,0,0)})
+ assert.same({2016,3,5,0,0,0}, {timetable.normalise(2015,1,430,0,0,0)})
+ assert.same({2017,3,5,0,0,0}, {timetable.normalise(2016,1,430,0,0,0)})
+ assert.same({2027,5,18,0,0,0}, {timetable.normalise(2000,1,10000,0,0,0)})
+ assert.same({29379,1,25,0,0,0}, {timetable.normalise(2000,1,10000000,0,0,0)})
+ end)
+
+ it("#normalise handles out of range days in a #month", function()
+ assert.same({2012,12,1,0,0,0}, {timetable.normalise(2013,0,1,0,0,0)})
+ assert.same({2016,6,1,0,0,0}, {timetable.normalise(2013,42,1,0,0,0)})
-- Correct behaviour around leap days
- assert.same({ 2012,3,23,0,0,0 }, { timetable.normalise(2012,2,52,0,0,0) })
- assert.same({ 2013,3,24,0,0,0 }, { timetable.normalise(2013,2,52,0,0,0) })
+ assert.same({2012,3,23,0,0,0}, {timetable.normalise(2012,2,52,0,0,0)})
+ assert.same({2013,3,24,0,0,0}, {timetable.normalise(2013,2,52,0,0,0)})
- assert.same({ 2012,2,27,0,0,0 }, { timetable.normalise(2012,3,-2,0,0,0) })
- assert.same({ 2013,2,26,0,0,0 }, { timetable.normalise(2013,3,-2,0,0,0) })
+ assert.same({2012,2,27,0,0,0}, {timetable.normalise(2012,3,-2,0,0,0)})
+ assert.same({2013,2,26,0,0,0}, {timetable.normalise(2013,3,-2,0,0,0)})
-- Also when more fields are out of range
- assert.same({ 2016,7,22,0,0,0 }, { timetable.normalise(2013,42,52,0,0,0) })
- assert.same({ 2016,7,24,2,0,0 }, { timetable.normalise(2013,42,52,50,0,0) })
- end )
-
- it ( "#normalise handles fractional #month" , function ( )
- assert.same({ 2015,2,15,0,0,0 } , { timetable.normalise(2014,14.5,1,0,0,0) })
- assert.same({ 2016,2,15,12,0,0 } , { timetable.normalise(2015,14.5,1,0,0,0) }) -- leap year, so hours is 12
- assert.same({ 2017,2,15,0,0,0 } , { timetable.normalise(2016,14.5,1,0,0,0) })
- end )
-
- it ( "#normalise handles negative carry (issue #10)", function()
- assert.same({ 1970,01,01,00,59,00 }, { timetable.normalise(1970,01,01,01,00,-60) })
- assert.same({ 1970,01,01,00,58,58 }, { timetable.normalise(1970,01,01,01,00,-62) })
- assert.same({ 1969,12,31,23,55,58 }, { timetable.normalise(1970,01,01,01,-63,-62) })
- assert.same({ 2017,02,3,0,0,0 }, { timetable.normalise(2017,02,13,0,-14400,0) })
- end )
-
- it ( "#normalise handles negative day carry (issue #13)", function()
- assert.same({ 2016,11,30,00,00,00 }, { timetable.normalise(2016,12,0,0,0,0) })
- assert.same({ 2017,11,30,00,00,00 }, { timetable.normalise(2017,12,0,0,0,0) })
- assert.same({ 2018,11,30,00,00,00 }, { timetable.normalise(2018,12,0,0,0,0) })
- end )
+ assert.same({2016,7,22,0,0,0}, {timetable.normalise(2013,42,52,0,0,0)})
+ assert.same({2016,7,24,2,0,0}, {timetable.normalise(2013,42,52,50,0,0)})
+ end)
+
+ it("#normalise handles fractional #month", function()
+ assert.same({2015,2,15,0,0,0}, {timetable.normalise(2014,14.5,1,0,0,0)})
+ assert.same({2016,2,15,12,0,0}, {timetable.normalise(2015,14.5,1,0,0,0)}) -- leap year, so hours is 12
+ assert.same({2017,2,15,0,0,0}, {timetable.normalise(2016,14.5,1,0,0,0)})
+ end)
+
+ it("#normalise handles negative carry (issue #10)", function()
+ assert.same({1970,01,01,00,59,00}, {timetable.normalise(1970,01,01,01,00,-60)})
+ assert.same({1970,01,01,00,58,58}, {timetable.normalise(1970,01,01,01,00,-62)})
+ assert.same({1969,12,31,23,55,58}, {timetable.normalise(1970,01,01,01,-63,-62)})
+ assert.same({2017,02,3,0,0,0}, {timetable.normalise(2017,02,13,0,-14400,0)})
+ end)
+
+ it("#normalise handles negative day carry (issue #13)", function()
+ assert.same({2016,11,30,00,00,00}, {timetable.normalise(2016,12,0,0,0,0)})
+ assert.same({2017,11,30,00,00,00}, {timetable.normalise(2017,12,0,0,0,0)})
+ assert.same({2018,11,30,00,00,00}, {timetable.normalise(2018,12,0,0,0,0)})
+ end)
local function round_trip_add(t, field, x)
local before = t:clone()
t:normalise();
assert.same(0, t-before)
end
- it ( "#normalise round trips" , function ( )
+ it("#normalise round trips", function()
round_trip_add(timetable.new(2000,2,28,0,0,0), "month", 0.5)
round_trip_add(timetable.new(2014,8,28,19,23,0), "month", 0.4)
round_trip_add(timetable.new(2014,14.5,28,0,0,0), "month", 0.4)
- end )
+ end)
it("#rfc_3339 works with fractional milliseconds", function()
-- on lua 5.3 this used to throw an error due to milliseconds not being an integer
assert.same("1969-12-31T23:59:59.999", timetable.new_from_timestamp(-0.001):rfc_3339())
assert.same("1969-12-31T23:59:00.000", timetable.new_from_timestamp(-59.9999999):rfc_3339())
end)
-end )
+end)