|
|
| Line 1: |
Line 1: |
| -- Date functions for use by other modules.
| |
| -- I18N and time zones are not supported.
| |
|
| |
|
| local MINUS = '−' -- Unicode U+2212 MINUS SIGN
| |
| local floor = math.floor
| |
|
| |
| local Date, DateDiff, diffmt -- forward declarations
| |
| local uniq = { 'unique identifier' }
| |
|
| |
| local function is_date(t)
| |
| -- The system used to make a date read-only means there is no unique
| |
| -- metatable that is conveniently accessible to check.
| |
| return type(t) == 'table' and t._id == uniq
| |
| end
| |
|
| |
| local function is_diff(t)
| |
| return type(t) == 'table' and getmetatable(t) == diffmt
| |
| end
| |
|
| |
| local function _list_join(list, sep)
| |
| return table.concat(list, sep)
| |
| end
| |
|
| |
| local function collection()
| |
| -- Return a table to hold items.
| |
| return {
| |
| n = 0,
| |
| add = function (self, item)
| |
| self.n = self.n + 1
| |
| self[self.n] = item
| |
| end,
| |
| join = _list_join,
| |
| }
| |
| end
| |
|
| |
| local function strip_to_nil(text)
| |
| -- If text is a string, return its trimmed content, or nil if empty.
| |
| -- Otherwise return text (convenient when Date fields are provided from
| |
| -- another module which may pass a string, a number, or another type).
| |
| if type(text) == 'string' then
| |
| text = text:match('(%S.-)%s*$')
| |
| end
| |
| return text
| |
| end
| |
|
| |
| local function is_leap_year(year, calname)
| |
| -- Return true if year is a leap year.
| |
| if calname == 'Julian' then
| |
| return year % 4 == 0
| |
| end
| |
| return (year % 4 == 0 and year % 100 ~= 0) or year % 400 == 0
| |
| end
| |
|
| |
| local function days_in_month(year, month, calname)
| |
| -- Return number of days (1..31) in given month (1..12).
| |
| if month == 2 and is_leap_year(year, calname) then
| |
| return 29
| |
| end
| |
| return ({ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 })[month]
| |
| end
| |
|
| |
| local function h_m_s(time)
| |
| -- Return hour, minute, second extracted from fraction of a day.
| |
| time = floor(time * 24 * 3600 + 0.5) -- number of seconds
| |
| local second = time % 60
| |
| time = floor(time / 60)
| |
| return floor(time / 60), time % 60, second
| |
| end
| |
|
| |
| local function hms(date)
| |
| -- Return fraction of a day from date's time, where (0 <= fraction < 1)
| |
| -- if the values are valid, but could be anything if outside range.
| |
| return (date.hour + (date.minute + date.second / 60) / 60) / 24
| |
| end
| |
|
| |
| local function julian_date(date)
| |
| -- Return jd, jdz from a Julian or Gregorian calendar date where
| |
| -- jd = Julian date and its fractional part is zero at noon
| |
| -- jdz = same, but assume time is 00:00:00 if no time given
| |
| -- http://www.tondering.dk/claus/cal/julperiod.php#formula
| |
| -- Testing shows this works for all dates from year -9999 to 9999!
| |
| -- JDN 0 is the 24-hour period starting at noon UTC on Monday
| |
| -- 1 January 4713 BC = (-4712, 1, 1) Julian calendar
| |
| -- 24 November 4714 BC = (-4713, 11, 24) Gregorian calendar
| |
| local offset
| |
| local a = floor((14 - date.month)/12)
| |
| local y = date.year + 4800 - a
| |
| if date.calendar == 'Julian' then
| |
| offset = floor(y/4) - 32083
| |
| else
| |
| offset = floor(y/4) - floor(y/100) + floor(y/400) - 32045
| |
| end
| |
| local m = date.month + 12*a - 3
| |
| local jd = date
|