Open main menu

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