Module:Wikidata/Formatters/time

Από τη Βικιπαίδεια, την ελεύθερη εγκυκλοπαίδεια
Documentation icon Τεκμηρίωση module[δημιουργία]
--require "Module:No globals"

local p = {}

local lib = require 'Module:Wikidata/lib'
local i18n = mw.loadData('Module:Wikidata/i18n')

-- todo: move to lib
function p.yearDifference(sooner, later)
	local age = later.year - sooner.year
	if sooner.precision > 10 then
		if later.precision > 10 then
			if sooner.month > later.month then
				age = age - 1
			elseif sooner.month == later.month and sooner.day > later.day then
				age = age - 1
			end
			return age, age
		elseif later.precision == 10 then
			if sooner.month == later.month then
				return age - 1, age - 1 .. '–' .. age
			end
			if sooner.month > later.month then
				age = age - 1
			end
			return age, age
		end
	elseif sooner.precision == 10 then
		if later.precision > 9 then
			if sooner.month == later.month then
				return age - 1, age - 1 .. '–' .. age
			end
			if sooner.month > later.month then
				age = age - 1
			end
			return age, age
		end
	end
	return age - 1, age - 1 .. '–' .. age
end

-- todo: move to lib
function p.julianToGregorian(timevalue)
	-- https://en.wikipedia.org/wiki/Gregorian_calendar#Difference_between_Gregorian_and_Julian_calendar_dates
	local year = timevalue.year
	local diff = math.floor(year / 100) - math.floor(year / 400) - 2
	if year % 100 == 0 and year % 400 ~= 0 then
		if timevalue.month == 2 and 30 - timevalue.day > diff then
			diff = diff - 1
		end
	end
	local feb_days
	if year % 4 == 0 then
		feb_days = 29
	else
		feb_days = 28
	end
	local days = { 31, feb_days, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
	local newvalue = mw.clone(timevalue)
	newvalue.calendar = newvalue.CALENDAR.GREGORIAN
	newvalue.day = newvalue.day + diff
	if newvalue.day > days[newvalue.month] then
		newvalue.day = newvalue.day - days[newvalue.month]
		newvalue.month = newvalue.month + 1
		if newvalue.month > 12 then
			newvalue.month = 1
			newvalue.year = newvalue.year + 1
		end
	end
	return newvalue
end

local function stripZero(date, lang, timevalue)
	-- odstraň nuly na začátku (rok < 1000 a > -1000)
	local year
	if timevalue.year > 0 then
		year = math.abs(timevalue.year)
	else
		year = math.abs(timevalue.year) - 1
	end
	return mw.ustring.gsub(
		date,
		lang:formatNum(0) .. '+(' .. lang:formatNum(year, { noCommafy = true }) .. ')',
		'%1'
	)
end

function p.getRawValue(value, options)
	local Time = require 'Module:Time'
	return Time.newFromWikidataValue(value)
end

function p.formatRawValue(timevalue, options)
	local lang = mw.getContentLanguage()
	local precision = math.min(timevalue.precision, options.precision or timevalue.precision)
	local timestring = tostring(timevalue)
	local linked_app = ''
	if not lib.IsOptionTrue(options, 'nolink') then
		linked_app = '-linked'
	end
	local BCE_app = ''
	if timevalue.year < 0 then
		timestring = mw.ustring.sub(timestring, 2)
		BCE_app = '-BCE'
	end

	local newstring
	local map = {
		[timevalue.PRECISION.KY] = 'millenium',
		[timevalue.PRECISION.YEAR100] = 'century',
		[timevalue.PRECISION.YEAR10] = 'decade',
		[timevalue.PRECISION.YEAR] = 'year',
		[timevalue.PRECISION.MONTH] = 'year-month',
		[timevalue.PRECISION.DAY] = 'year-month-day',
	}
	if precision >= timevalue.PRECISION.YEAR then
		local key = map[math.min(precision, timevalue.PRECISION.DAY)] .. BCE_app
		local pattern = i18n.date[key .. linked_app] or i18n.date[key]
		if precision > timevalue.PRECISION.MONTH
			and timevalue.calendar == timevalue.CALENDAR.JULIAN
			and timevalue > timevalue.newFromIso8601('1582-10-04')
		then
			local newvalue = p.julianToGregorian(timevalue)
			local jul_pattern
			if newvalue.year ~= timevalue.year then
				jul_pattern = i18n.date['year-month-day']
			elseif newvalue.month ~= timevalue.month then
				jul_pattern = i18n.date['month-day']
			else
				jul_pattern = i18n.date.day
			end
			if newvalue.month == 2 and newvalue.day == 29 then
				if year % 100 == 0 and year % 400 ~= 0 then
					timevalue.year = 2000 -- hack to force formatting 29th February
				end
			end
			newstring = mw.ustring.format('%s%s / %s%s',
				stripZero(lang:formatDate(jul_pattern, timestring), lang, timevalue),
				i18n.date.julian,
				stripZero(lang:formatDate(pattern, tostring(newvalue)), lang, newvalue),
				i18n.date.gregorian)
		else
			newstring = stripZero(lang:formatDate(pattern, timestring), lang, timevalue)
		end
	elseif precision >= timevalue.PRECISION.KY then
		local key = map[precision] .. BCE_app
		local coef = 10 ^ (9 - precision)
		local number = math.floor(math.abs(timevalue.year) / coef)
		if precision == timevalue.PRECISION.YEAR100 then
			if timevalue.year % 100 > 0 and BCE_app == '' then
				number = number + 1
			end
		elseif precision == timevalue.PRECISION.YEAR10 then
			number = number * coef
		end
		newstring = mw.getCurrentFrame():preprocess(
			mw.message.newRawMessage(i18n.date[key .. linked_app] or i18n.date[key])
				:params(lang:formatNum(number, { noCommafy = true }))
				:plain()
			)
	else
		-- TODO
		newstring = timestring
	end
	return newstring
end

function p.formatValue(value, options)
	local timevalue = p.getRawValue(value, options)
	local formatted = p.formatRawValue(timevalue, options)
	if lib.IsOptionTrue(options, 'birthdate') and timevalue.precision >= timevalue.PRECISION.YEAR then
		if #mw.wikibase.getBestStatements(options.id, 'P570') == 0 then
			local Time = require 'Module:Time'
			local age, age_text = p.yearDifference(timevalue, Time.new(os.date('!*t')))
			formatted = mw.ustring.format('%s (%s)', formatted,
				mw.getCurrentFrame():preprocess(
					mw.message.newRawMessage(i18n.date.age)
						:params(age_text)
						:numParams(age)
						:plain()
				)
			)
			if age >= 100 then
				formatted = formatted .. lib.category('centenarians-living')
			elseif age < 0 then
				formatted = formatted .. lib.category('failed-age-computing')
			end
		end
	elseif lib.IsOptionTrue(options, 'deathdate') and timevalue.precision >= timevalue.PRECISION.YEAR then
		local birthvalue
		for _, statement in ipairs(mw.wikibase.getBestStatements(options.id, 'P569')) do
			if lib.IsSnakValue(statement.mainsnak) then
				local Formatters = require 'Module:Wikidata/Formatters'
				birthvalue = Formatters.getRawValue(statement.mainsnak)
				break
			end
		end
		if birthvalue and birthvalue.precision >= birthvalue.PRECISION.YEAR then
			local age, age_text = p.yearDifference(birthvalue, timevalue)
			formatted = mw.ustring.format('%s (%s)', formatted,
				mw.getCurrentFrame():preprocess(
					mw.message.newRawMessage(i18n.date['in-the-age'])
						:params(age_text)
						:numParams(age)
						:plain()
				)
			)
			if age >= 100 then
				formatted = formatted .. lib.category('centenarians')
			end
		end
	end
	return formatted
end

return p