Documentation for this module may be created at Module:TimedeltaAbstract/doc
local util_args = require("Module:ArgsUtil")
local util_table = require("Module:TableUtil")
local util_time = require("Module:TimeUtil")
local util_vars = require("Module:VarsUtil")
local LCS = require('Module:LuaClassSystem')
local p = LCS.class.abstract()
local h = {}
p.DAYS_PER_MONTH = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
function p:main(s, e, fuzzy)
-- expect string inputs
local sDate = util_time.strToDateFuzzy(s)
local eDate = util_time.strToDateFuzzy(e)
if not sDate.year or not eDate.year then
return self.outputUnknown
end
if not sDate.month or not eDate.month then
return self:approxDurationFuzzyMonth(sDate, eDate)
end
if not sDate.day or not eDate.day then
return self:approxDurationFuzzyDay(sDate, eDate)
end
return self:addFuzzyPrefixIffNeeded(self:approxDurationExact(sDate, eDate), fuzzy)
end
function p:approxDurationFuzzyMonth(sDate, eDate)
if sDate.year >= eDate.year - 1 then
return self:approxDurationFuzzyMonthSameYear(sDate, eDate) .. self.clarifyApprox
end
if not sDate.month then
sDate.month = 7
sDate.day = eDate.day or 1
end
if not eDate.day then
eDate.month = 7
eDate.day = sDate.day or 1
end
return self.fuzzyPrefix .. self:approxDurationExact(sDate, eDate)
end
function p:approxDurationFuzzyMonthSameYear(sDate, eDate)
if (not sDate.month or sDate.month == 1) and (not eDate.month or eDate.month == 12) then
return self.lessThanOneYear
end
sDate.month = sDate.month or 1
eDate.month = eDate.month or 12
sDate.day = sDate.day or eDate.day or 1
eDaysPerMonth = h.getDaysPerMonth(eDate)
eDate.day = eDate.day or sDate.day or eDaysPerMonth[eDate.month]
return self.lessThan .. self:approxDurationExact(sDate, eDate)
end
function p:approxDurationFuzzyDay(sDate, eDate)
if sDate.month == eDate.month then
return self:approxDurationFuzzyDaySameMonth(sDate, eDate)
end
if not sDate.day and not eDate.day then
-- since February rounds differently, and if we don't know either day we want to say just 1 month
-- in this case just "cheat" and assign both as 1 instead of dealing with midpoint differences
sDate.day = 1
eDate.day = 1
elseif not sDate.day then
sDate.day = sDate.month == 2 and 14 or 15
elseif not eDate.day then
eDate.day = eDate.month == 2 and 14 or 15
end
return self.fuzzyPrefix .. self:approxDurationExact(sDate, eDate)
end
function p:addFuzzyPrefixIffNeeded(output, fuzzy)
if not util_args.castAsBool(fuzzy) then return output end
if output:find('≈') then return output end
return '≈' .. output
end
function p:approxDurationFuzzyDaySameMonth(sDate, eDate)
local eDaysPerMonth = h.getDaysPerMonth(eDate)
if (not sDate.day or sDate.day == 1) and (not eDate.day or eDate.day == eDaysPerMonth[eDate.month]) then
return self:lessThanOneMonth(eDaysPerMonth[eDate.month])
end
if not sDate.day then sDate.day = 1 end
if not eDate.day then
eDate.day = eDaysPerMonth[eDate.month]
end
return self.lessThan .. self:approxDurationExact(sDate, eDate)
end
function p:lessThanOneMonth(month) end
function p:approxDurationExact(sDate, eDate)
local numberOfYears = h.getYearDiff(sDate, eDate)
local numberOfMonths = h.getMonthDiff(sDate, eDate)
local numberOfDays = h.getDayDiff(sDate, eDate)
local tbl = {
h.printNumberOfYears(numberOfYears),
h.printNumberOfMonths(numberOfMonths),
h.printNumberOfDays(numberOfYears, numberOfMonths, numberOfDays),
}
util_table.removeFalseEntries(tbl)
return table.concat(tbl, ' ')
end
function h.getYearDiff(sDate, eDate)
local monthOffset = eDate.month >= sDate.month and 0 or 1
return eDate.year - sDate.year - monthOffset
end
function h.getMonthDiff(sDate, eDate)
if not eDate.day then
error(('Missing day input in end: %s'):format(e))
end
if not sDate.day then
error(('Missing day input in start: %s'):format(s))
end
local dayOffset = eDate.day >= sDate.day and 0 or 1
if eDate.month >= sDate.month then
return eDate.month - sDate.month - dayOffset
end
return 12 - (sDate.month - eDate.month) - dayOffset
end
function h.getDayDiff(sDate, eDate)
if eDate.day >= sDate.day then
return eDate.day - sDate.day
end
local sDaysPerMonth = h.getDaysPerMonth(sDate)
return sDaysPerMonth[sDate.month] - (sDate.day - eDate.day)
end
function h.getDaysPerMonth(date)
local ret = mw.clone(p.DAYS_PER_MONTH)
if date.month ~= 2 then return ret end
if math.floor(date.year / 1000) == date.year / 1000 then
ret[2] = 29
return ret
end
if math.floor(date.year / 100) == date.year / 100 then
return ret
end
if math.floor(date.year / 4) ~= date.year / 4 then
return ret
end
ret[2] = 29
return ret
end
function h.printNumberOfYears(numberOfYears)
if numberOfYears <= 0 then return false end
return ('%syr'):format(numberOfYears)
end
function h.printNumberOfMonths(numberOfMonths)
if numberOfMonths <= 0 then return false end
return ('%smo'):format(numberOfMonths)
end
function h.printNumberOfDays(numberOfYears, numberOfMonths, numberOfDays)
if numberOfYears > 0 and numberOfMonths > 0 then return false end
if numberOfDays <= 0 then return '' end
return ('%sd'):format(numberOfDays)
end
return p