Module:Date: Difference between revisions
rework date differences for more consistent years/months/days; differences include hours/minutes/seconds; can add 'date + diff'; tweaks
(major refactor with fixes; force Date to be read-only (error on write); list of dates in a month on a particular day of week) |
(rework date differences for more consistent years/months/days; differences include hours/minutes/seconds; can add 'date + diff'; tweaks) |
||
| Line 3: | Line 3: | ||
local MINUS = '−' -- Unicode U+2212 MINUS SIGN | local MINUS = '−' -- Unicode U+2212 MINUS SIGN | ||
local floor = math.floor | |||
local Date, DateDiff, diffmt -- forward declarations | local Date, DateDiff, diffmt -- forward declarations | ||
| Line 34: | Line 35: | ||
local function strip_to_nil(text) | local function strip_to_nil(text) | ||
-- If text is a string, return its trimmed content, or nil. | -- If text is a string, return its trimmed content, or nil if empty. | ||
-- Otherwise return text (convenient when Date fields are provided from | -- Otherwise return text (convenient when Date fields are provided from | ||
-- another module which | -- another module which may pass a string, a number, or another type). | ||
if type(text) == 'string' then | if type(text) == 'string' then | ||
text = text:match('(%S.-)%s*$') | text = text:match('(%S.-)%s*$') | ||
end | end | ||
return text | return text | ||
end | end | ||
| Line 65: | Line 58: | ||
end | end | ||
return ({ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 })[month] | 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 (0 <= fraction < 1) from date's time. | |||
return (date.hour + (date.minute + date.second / 60) / 60) / 24 | |||
end | end | ||
| Line 76: | Line 82: | ||
-- 1 January 4713 BC = (-4712, 1, 1) Julian calendar | -- 1 January 4713 BC = (-4712, 1, 1) Julian calendar | ||
-- 24 November 4714 BC = (-4713, 11, 24) Gregorian calendar | -- 24 November 4714 BC = (-4713, 11, 24) Gregorian calendar | ||
local offset | local offset | ||
local a = floor((14 - date.month)/12) | local a = floor((14 - date.month)/12) | ||
| Line 88: | Line 93: | ||
local jd = date.day + floor((153*m + 2)/5) + 365*y + offset | local jd = date.day + floor((153*m + 2)/5) + 365*y + offset | ||
if date.hastime then | if date.hastime then | ||
jd = jd + (date | jd = jd + hms(date) - 0.5 | ||
return jd, jd | return jd, jd | ||
end | end | ||
| Line 100: | Line 105: | ||
-- This handles the proleptic Julian and Gregorian calendars. | -- This handles the proleptic Julian and Gregorian calendars. | ||
-- Negative Julian dates are not defined but they work. | -- Negative Julian dates are not defined but they work. | ||
local calname = date.calname | local calname = date.calname | ||
local | local low, high -- min/max limits for date ranges −9999-01-01 to 9999-12-31 | ||
if calname == ' | if calname == 'Gregorian' then | ||
low, high = -1930999.5, 5373484.49999 | |||
elseif calname == ' | elseif calname == 'Julian' then | ||
low, high = -1931076.5, 5373557.49999 | |||
else | else | ||
return | return | ||
end | end | ||
local jd = date.jd | local jd = date.jd | ||
if not (type(jd) == 'number' and | if not (type(jd) == 'number' and low <= jd and jd <= high) then | ||
return | return | ||
end | end | ||
local jdn = floor(jd) | local jdn = floor(jd) | ||
if date.hastime then | if date.hastime then | ||
local time = jd - jdn | local time = jd - jdn -- 0 <= time < 1 | ||
if time >= 0.5 then -- if at or after midnight of next day | |||
if time >= 0.5 then | |||
jdn = jdn + 1 | jdn = jdn + 1 | ||
time = time - 0.5 | time = time - 0.5 | ||
else | else | ||