require "Амодуль:No globals"
local p = {}
local lib = require 'Амодуль:Wikidata/lib'
local i18n = mw.loadData('Амодуль:Wikidata/i18n')
local function shouldConvertGregorian(timevalue, precision)
return (
(precision or timevalue.precision) > timevalue.PRECISION.MONTH
and timevalue.calendar == timevalue.CALENDAR.JULIAN
and timevalue > timevalue.newFromIso8601('1582-10-04')
)
end
-- 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, year)
-- удалить ведущие нули (год < 1000 и > -1000)
return mw.ustring.gsub(
date,
lang:formatNum(0) .. '+(' .. lang:formatNum(year, { noCommafy = true }) .. ')',
'%1'
)
end
function p.getRawValue(value, options)
local Time = require 'Амодуль:Time'
local timevalue = Time.newFromWikidataValue(value)
if (
lib.IsOptionTrue(options, 'gregorian')
and shouldConvertGregorian(timevalue)
) then
timevalue = p.julianToGregorian(timevalue)
end
return timevalue
end
function p.formatRawValue(timevalue, options)
local lang = mw.getContentLanguage()
local precision = math.min(timevalue.precision, options.precision or timevalue.precision)
local linked_app = ''
if not lib.IsOptionTrue(options, 'nolink') then
linked_app = '-linked'
end
local year, BCE, BCE_app
if timevalue.year < 0 then
year = -timevalue.year - 1
BCE = true
if tostring(options.showera) ~= 'false' then
BCE_app = '-BCE'
else
BCE_app = ''
end
else
year = timevalue.year
BCE = false
BCE_app = ''
end
local newstring
-- follows Wikibase/lib/includes/Formatters/MwTimeIsoFormatter.php
local map = {
[timevalue.PRECISION.GY] = { 'Gannum', 1e9, 1 },
[timevalue.PRECISION.MY100] = { 'Mannum', 1e8, 100 },
[timevalue.PRECISION.MY10] = { 'Mannum', 1e7, 10 },
[timevalue.PRECISION.MY] = { 'Mannum', 1e6, 1 },
[timevalue.PRECISION.KY100] = { 'annum', 1e5, 1e5 },
[timevalue.PRECISION.KY10] = { 'annum', 1e4, 1e4 },
[timevalue.PRECISION.KY] = { 'millenium', 1e3, 1, math.ceil },
[timevalue.PRECISION.YEAR100] = { 'century', 100, 1, math.ceil },
[timevalue.PRECISION.YEAR10] = { 'decade', 10, 10, math.floor },
[timevalue.PRECISION.YEAR] = 'year',
[timevalue.PRECISION.MONTH] = 'year-month',
[timevalue.PRECISION.DAY] = 'year-month-day',
-- TODO: hours, minutes and seconds (not on Wikidata yet)
}
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 shouldConvertGregorian(timevalue, precision) 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 timevalue.month == 2 and timevalue.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',
lang:formatDate(jul_pattern, tostring(timevalue)),
i18n.date.julian,
lang:formatDate(pattern, tostring(newvalue)),
i18n.date.gregorian)
else
local timestring
if BCE then
timestring = mw.ustring.sub(tostring(timevalue), 2)
else
timestring = tostring(timevalue)
end
newstring = stripZero(lang:formatDate(pattern, timestring), lang, year)
end
if lib.IsOptionTrue(options, 'showera') and not BCE then
newstring = mw.getCurrentFrame():preprocess(
mw.message.newRawMessage(i18n.date.CE, newstring):plain())
end
elseif precision >= timevalue.PRECISION.KY then -- uses custom messages from Wikidata/i18n
-- follows Wikibase/lib/includes/Formatters/MwTimeIsoFormatter.php
local key = map[precision][1] .. BCE_app
local shift = map[precision][2]
local unshift = map[precision][3]
local func = map[precision][4]
local number = func(year / shift) * unshift
newstring = mw.getCurrentFrame():preprocess(
mw.message.newRawMessage(i18n.date[key .. linked_app] or i18n.date[key])
:params(lang:formatNum(number, { noCommafy = true }))
:plain()
)
if lib.IsOptionTrue(options, 'showera') and not BCE then
newstring = mw.getCurrentFrame():preprocess(
mw.message.newRawMessage(i18n.date.CE, newstring):plain())
end
else -- uses messages from Wikibase extension
local round = function (x)
local int, fract = math.modf(x)
if fract < .5 then
return int
else
return int + 1
end
end
-- follows Wikibase/lib/includes/Formatters/MwTimeIsoFormatter.php
local key = 'wikibase-time-precision' .. BCE_app .. '-' .. map[precision][1]
local shift = map[precision][2]
local unshift = map[precision][3]
local number = round(year / shift) * unshift
newstring = mw.getCurrentFrame():preprocess(
mw.message.new(key)
:numParams(number)
:plain()
)
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
local statements = mw.wikibase.getBestStatements(options.id, 'P570')
local Filterers = require 'Амодуль:Wikidata/Filterers'
Filterers.filterStatements(statements, { somevalue = true })
if #statements == 0 then
local Time = require 'Амодуль:Time'
local osDate = os.date('*t')
local currentTime = Time.new{
year = osDate.year,
month = osDate.month,
day = osDate.day,
precision = Time.PRECISION.DAY,
calendar = Time.CALENDAR.GREGORIAN,
}
local age, age_text = p.yearDifference(timevalue, currentTime)
if age >= 0 and age < 150 then
formatted = mw.ustring.format('%s (%s)', formatted,
mw.getCurrentFrame():preprocess(
mw.message.newRawMessage(i18n.date.age)
:params(age_text)
:numParams(age)
:plain()
)
)
end
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 statements = mw.wikibase.getBestStatements(options.id, 'P569')
local Filterers = require 'Амодуль:Wikidata/Filterers'
Filterers.filterStatements(statements, { somevalue = false, novalue = false })
if #statements > 0 then
local Formatters = require 'Амодуль:Wikidata/Formatters'
local TableTools = require 'Амодуль:TableTools'
local ages = {}
local invalid = 'INVALID'
local age, age_text
local min_age = math.huge
for _, statement in ipairs(statements) do
local birthvalue = Formatters.getRawValue(statement.mainsnak)
if birthvalue.precision >= birthvalue.PRECISION.YEAR then
age, age_text = p.yearDifference(birthvalue, timevalue)
ages[age_text] = true
if age < min_age then
min_age = age
end
else
ages[invalid] = true
end
end
if TableTools.size(ages) == 1 and not ages[invalid] then
formatted = mw.ustring.format('%s (%s)', formatted,
mw.getCurrentFrame():preprocess(
mw.message.newRawMessage(i18n.date['in-the-age'])
:params(age_text)
:numParams(min_age)
:plain()
)
)
end
if min_age >= 100 and min_age < math.huge then
formatted = formatted .. lib.category('centenarians')
elseif min_age < 0 then
formatted = formatted .. lib.category('failed-age-computing')
end
end
end
return formatted
end
return p