438
edits
(replace kludge using global set_current_for_test so Module:No globals can be used by a module requiring Module:Date) |
m (3 revisions imported from wikipedia:Module:Date: see Topic:Vtixlm0q28eo6jtf) |
||
(13 intermediate revisions by 4 users not shown) | |||
Line 897: | Line 897: | ||
end | end | ||
return date, options | return date, options | ||
end | |||
local function autofill(date1, date2) | |||
-- Fill any missing month or day in each date using the | |||
-- corresponding component from the other date, if present, | |||
-- or with 1 if both dates are missing the month or day. | |||
-- This gives a good result for calculating the difference | |||
-- between two partial dates when no range is wanted. | |||
-- Return filled date1, date2 (two full dates). | |||
local function filled(a, b) | |||
-- Return date a filled, if necessary, with month and/or day from date b. | |||
-- The filled day is truncated to fit the number of days in the month. | |||
local fillmonth, fillday | |||
if not a.month then | |||
fillmonth = b.month or 1 | |||
end | |||
if not a.day then | |||
fillday = b.day or 1 | |||
end | |||
if fillmonth or fillday then -- need to create a new date | |||
a = Date(a, { | |||
month = fillmonth, | |||
day = math.min(fillday or a.day, days_in_month(a.year, fillmonth or a.month, a.calendar)) | |||
}) | |||
end | |||
return a | |||
end | |||
return filled(date1, date2), filled(date2, date1) | |||
end | end | ||
Line 1,102: | Line 1,130: | ||
-- Return true if dates identify same date/time where, for example, | -- Return true if dates identify same date/time where, for example, | ||
-- Date(-4712, 1, 1, 'Julian') == Date(-4713, 11, 24, 'Gregorian') is true. | -- Date(-4712, 1, 1, 'Julian') == Date(-4713, 11, 24, 'Gregorian') is true. | ||
-- This is only | -- This is called only if lhs and rhs have the same type and the same metamethod. | ||
if lhs.partial or rhs.partial then | if lhs.partial or rhs.partial then | ||
-- One date is partial; the other is a partial or a full date. | -- One date is partial; the other is a partial or a full date. | ||
Line 1,114: | Line 1,142: | ||
-- Return true if lhs < rhs, for example, | -- Return true if lhs < rhs, for example, | ||
-- Date('1 Jan 2016') < Date('06:00 1 Jan 2016') is true. | -- Date('1 Jan 2016') < Date('06:00 1 Jan 2016') is true. | ||
-- This is only | -- This is called only if lhs and rhs have the same type and the same metamethod. | ||
if lhs.partial or rhs.partial then | if lhs.partial or rhs.partial then | ||
-- One date is partial; the other is a partial or a full date. | -- One date is partial; the other is a partial or a full date. | ||
Line 1,160: | Line 1,188: | ||
options = {}, | options = {}, | ||
list = _date_list, | list = _date_list, | ||
subtract = function (self, rhs, options) | |||
return DateDiff(self, rhs, options) | |||
end, | |||
text = _date_text, | text = _date_text, | ||
} | } | ||
Line 1,309: | Line 1,340: | ||
local function _diff_age(diff, code, options) | local function _diff_age(diff, code, options) | ||
-- Return a tuple of integer values from diff as specified by code, except that | -- Return a tuple of integer values from diff as specified by code, except that | ||
-- each integer may be a list of two integers for a diff with a partial date. | -- each integer may be a list of two integers for a diff with a partial date, or | ||
-- return nil if the code is not supported. | |||
-- If want round, the least significant unit is rounded to nearest whole unit. | -- If want round, the least significant unit is rounded to nearest whole unit. | ||
-- For a duration, an extra day is added. | -- For a duration, an extra day is added. | ||
Line 1,328: | Line 1,360: | ||
local function choose(v) | local function choose(v) | ||
if type(v) == 'table' then | if type(v) == 'table' then | ||
if not wantrange then | if not wantrange or v[1] == v[2] then | ||
-- Example: Date('partial', 2005) - Date('partial', 2001) gives | -- Example: Date('partial', 2005) - Date('partial', 2001) gives | ||
-- diff.years = { 3, 4 } to show the range of possible results. | -- diff.years = { 3, 4 } to show the range of possible results. | ||
Line 1,347: | Line 1,379: | ||
return choose(diff.partial.years), choose(diff.partial.months) | return choose(diff.partial.years), choose(diff.partial.months) | ||
end | end | ||
if code == 'y' then | |||
return choose(diff.partial. | return choose(diff.partial.years) | ||
end | |||
if code == 'm' or code == 'w' or code == 'd' then | |||
return choose({ diff.partial.mindiff:age(code), diff.partial.maxdiff:age(code) }) | |||
end | |||
return nil | |||
end | end | ||
local extra_days = wantduration and 1 or 0 | local extra_days = wantduration and 1 or 0 | ||
Line 1,363: | Line 1,400: | ||
return floor(days/7 + offset) | return floor(days/7 + offset) | ||
end | end | ||
local H, M = diff.hours, diff.minutes | local H, M, S = diff.hours, diff.minutes, diff.seconds | ||
if code == 'dh' or code == 'dhm' then | if code == 'dh' or code == 'dhm' or code == 'dhms' or code == 'h' or code == 'hm' or code == 'hms' or code == 'M' or code == 's' then | ||
local days = floor(diff.age_days + extra_days) | local days = floor(diff.age_days + extra_days) | ||
local inc_hour | local inc_hour | ||
if wantround then | if wantround then | ||
if code == 'dh' then | if code == 'dh' or code == 'h' then | ||
if M >= 30 then | if M >= 30 then | ||
inc_hour = true | inc_hour = true | ||
end | end | ||
elseif | elseif code == 'dhm' or code == 'hm' then | ||
if S >= 30 then | |||
M = M + 1 | |||
if M >= 60 then | |||
M = 0 | |||
inc_hour = true | |||
end | |||
end | |||
elseif code == 'M' then | |||
if S >= 30 then | |||
M = M + 1 | |||
end | end | ||
else | |||
-- Nothing needed because S is an integer. | |||
end | end | ||
if inc_hour then | if inc_hour then | ||
Line 1,387: | Line 1,432: | ||
end | end | ||
end | end | ||
if code == 'dh' then | if code == 'dh' or code == 'dhm' or code == 'dhms' then | ||
return days, H | if code == 'dh' then | ||
return days, H | |||
elseif code == 'dhm' then | |||
return days, H, M | |||
else | |||
return days, H, M, S | |||
end | |||
end | |||
local hours = days * 24 + H | |||
if code == 'h' then | |||
return hours | |||
elseif code == 'hm' then | |||
return hours, M | |||
elseif code == 'M' or code == 's' then | |||
M = hours * 60 + M | |||
if code == 'M' then | |||
return M | |||
end | |||
return M * 60 + S | |||
end | end | ||
return | return hours, M, S | ||
end | end | ||
if wantround then | if wantround then | ||
Line 1,399: | Line 1,462: | ||
end | end | ||
elseif code == 'ymdhm' or code == 'ymwdhm' then | elseif code == 'ymdhm' or code == 'ymwdhm' then | ||
if | if S >= 30 then | ||
M = M + 1 | M = M + 1 | ||
if M >= 60 then | if M >= 60 then | ||
Line 1,472: | Line 1,535: | ||
return y, m, floor(d/7), d % 7, H, M | return y, m, floor(d/7), d % 7, H, M | ||
end | end | ||
if code == 'y' then | |||
if wantround and m >= 6 then | |||
y = y + 1 | |||
end | |||
return y | |||
end | end | ||
return | return nil | ||
end | end | ||
Line 1,514: | Line 1,579: | ||
} | } | ||
function DateDiff(date1, date2) -- for forward declaration above | function DateDiff(date1, date2, options) -- for forward declaration above | ||
-- Return a table with the difference between two dates (date1 - date2). | -- Return a table with the difference between two dates (date1 - date2). | ||
-- The difference is negative if date1 is older than date2. | -- The difference is negative if date1 is older than date2. | ||
Line 1,529: | Line 1,594: | ||
if not (is_date(date1) and is_date(date2) and date1.calendar == date2.calendar) then | if not (is_date(date1) and is_date(date2) and date1.calendar == date2.calendar) then | ||
return | return | ||
end | |||
local wantfill | |||
if type(options) == 'table' then | |||
wantfill = options.fill | |||
end | end | ||
local isnegative = false | local isnegative = false | ||
Line 1,547: | Line 1,616: | ||
-------------A===B------------------------- A=2001-04-01 B=2001-04-30 | -------------A===B------------------------- A=2001-04-01 B=2001-04-30 | ||
--------C=====================D------------ C=2001-01-01 D=2001-12-31 | --------C=====================D------------ C=2001-01-01 D=2001-12-31 | ||
local function zdiff(date1, date2) | if wantfill then | ||
date1, date2 = autofill(date1, date2) | |||
else | |||
return | local function zdiff(date1, date2) | ||
local diff = date1 - date2 | |||
if diff.isnegative then | |||
return date1 - date1 -- a valid diff in case we call its methods | |||
end | |||
return diff | |||
end | |||
local function getdate(date, which) | |||
return date.partial and date.partial[which] or date | |||
end | end | ||
local maxdiff = zdiff(getdate(date1, 'last'), getdate(date2, 'first')) | |||
local mindiff = zdiff(getdate(date1, 'first'), getdate(date2, 'last')) | |||
local years, months | |||
if maxdiff.years == mindiff.years then | |||
years = maxdiff.years | |||
if maxdiff.months == mindiff.months then | |||
months = maxdiff.months | |||
else | |||
months = { mindiff.months, maxdiff.months } | |||
end | |||
months = maxdiff.months | |||
else | else | ||
years = { mindiff.years, maxdiff.years } | |||
end | end | ||
return setmetatable({ | |||
date1 = date1, | |||
date2 = date2, | |||
partial = { | |||
years = years, | |||
months = months, | |||
maxdiff = maxdiff, | |||
mindiff = mindiff, | |||
}, | |||
isnegative = isnegative, | |||
iszero = iszero, | |||
age = _diff_age, | |||
duration = _diff_duration, | |||
}, diffmt) | |||
end | end | ||
end | end | ||
local y1, m1 = date1.year, date1.month | local y1, m1 = date1.year, date1.month |