Модуль:Wikidata/Даты

Материал из Викитеки — свободной библиотеки

Для документации этого модуля может быть создана страница Модуль:Wikidata/Даты/Документация

local moduleDates = require( "Module:Dates" )
local moduleMath = require( "Module:Math" )
local p = {}

--[[
Функция возвращает значения типа "time" данного свойства из Викиданных.
Значения других типов игнорируются.
Если есть значения ранга "preferred", возвращаются именно они,
иначе возвращаются значения ранга "preferred".
Значения ранга "deprecated" игнорируются.
Даты юлианского календаря переводятся в григорианский
]]
function p.parseProperty(s_property_name)
    local entity = mw.wikibase.getEntity()
    if entity == nil then
        return nil
    end
    if entity.claims == nil then
        return nil
    end
    local claim = entity.claims[s_property_name]
    if claim == nil then
        return nil
    end
    local preferred_values = {}
    local normal_values = {}
    for key, value in pairs(claim) do
      local snak = value.mainsnak
      local item = nil
      if snak.snaktype == "value" and snak.datavalue.type == "time" then
        item = {}
        item.precision = snak.datavalue.value.precision
        if snak.datavalue.value.calendarmodel == "http://www.wikidata.org/entity/Q1985786" then
            item.calendarmodel = "julian"
            item.jul_date = moduleDates.parseISO8601DateTime(snak.datavalue.value.time)
            item.time = moduleDates.JulianDateToUNIXTime(item.jul_date)
            if item.time == nil then
                item.structure = nil
            else
                item.structure = os.date("*t", item.time)
            end
        else
            if snak.datavalue.value.calendarmodel == "http://www.wikidata.org/entity/Q1985727" then
                item.calendarmodel = "gregorian"
            else
                item.calendarmodel = "unknown"
            end
            item.structure = moduleDates.parseISO8601DateTime(snak.datavalue.value.time)
            item.time = tonumber(os.time(item.structure))
            item.jul_date = nil
        end
      elseif snak.snaktype == "somevalue" then
        item = { unknown = "unknown" }
      end
      if item ~= nil then
        if value.rank == "preferred" then
            table.insert(preferred_values, item)
        elseif value.rank == "normal" then
            table.insert(normal_values, item)
        end
      end
    end
    if #preferred_values > 0 then
        return preferred_values
    elseif #normal_values > 0 then
        return normal_values
    else
        return nil
    end
end

--[[
Процедура для тестирования parseProperty()
{{#invoke:Wikidata/Даты|test_parseProperty|P569}}
{{#invoke:Wikidata/Даты|test_parseProperty|P570}}
]] 
function p.test_parseProperty(frame)
    local result = p.parseProperty(tostring(frame.args[1]))
    local s_result = ""
    for key, value in pairs(result) do
        s_result = s_result .. "value = ".. os.date("%d.%m.%Y", value.time) .. " year = " .. tostring(value.structure.year) .. " month = " .. tostring(value.structure.month) .. " day = " .. tostring(value.structure.day) .. " " .. value.calendarmodel  .. " (" .. tostring(value.time) .. ")\n\n"
    end
    return s_result
end

function formatDate( value, infoclass, categoryPrefix, unknownCategory)
    local s_result
    if value.unknown then
        s_result = "''неизвестно''"
        if ( unknownCategory ) then
            s_result = s_result .. "[[" .. unknownCategory.. "]]"
        end
    elseif value.precision == 7 then -- century precision
        if value.structure.year < 0 then
            s_result = moduleMath.rn(math.floor(-value.structure.year/100)) .. ' век до н. э.'
        else
            s_result = moduleMath.rn(math.floor(value.structure.year/100)) .. ' век'
        end
    elseif value.precision == 8 then -- 10-year precision
        if value.structure.year < 0 then
            local s_year = tostring(-value.structure.year)
            s_result = '[[:w:' .. s_year .. '-е годы до н. э.|' .. s_year .. '-е до н. э.]]'
        else
            local s_year = tostring(value.structure.year)
            s_result = '[[:w:' .. s_year .. '-е годы|' .. s_year .. '-е]]'
        end
    elseif value.precision == 9 then -- year precision
        if value.structure.year < 0 then
            local s_year = tostring(-value.structure.year)
            s_result = '[[:w:' .. s_year .. ' год до н. э.|' .. s_year .. ' до н. э.]]'
        else
            local s_year = tostring(value.structure.year)
            s_result = '[[:w:' .. s_year .. ' год|' .. s_year .. ']]'
        end
    elseif value.precision == 10 then -- month precision
        if value.structure.year < 0 then
            local s_year = tostring(-value.structure.year)
            s_result = moduleDates.getMonthNom(value.structure.month) .. ' [[:w:' .. s_year .. ' год до н. э.|' .. s_year .. ' до н. э.]]'
        else
            local s_year = tostring(value.structure.year)
            s_result = moduleDates.getMonthNom(value.structure.month) .. ' [[:w:' .. s_year .. ' год|' .. s_year .. ']]'
        end
    elseif value.jul_date == nil then
        return moduleDates.formatWikiImpl(value.structure, value.structure, infocardClass, categoryPrefix)
    elseif value.structure == nil then -- Даты до ввода грегорианского календаря
        return moduleDates.formatWikiImpl(value.jul_date, value.jul_date, infocardClass, categoryPrefix)
    else
        return moduleDates.formatWikiImpl(value.jul_date, value.structure, infocardClass, categoryPrefix)
    end
    return s_result
end

-- accepts table of time+precision values
function ageCurrent ( bTable )
	local possibleAge = "NYA" -- it meansm "Not Yet Assigned", not what you imagined!

	for bKey, bValue in pairs(bTable) do
		if ( bValue.unknown ) then
			return nil
		end
		local bStructure = bValue.structure
		local bPrecision = bValue.precision

		local dStructure = os.date( "*t" )

		local calculatedAge = ageImpl ( bStructure, bPrecision, dStructure, 11 )
		if ( possibleAge == "NYA" ) then
			possibleAge = calculatedAge
		else
			if ( possibleAge ~= calculatedAge ) then
				possibleAge = nil
			end
		end
	end

	return possibleAge
end

-- accepts tables of time+precision values
function age ( bTable, dTable )
	local possibleAge = "NYA" -- it meansm "Not Yet Assigned", not what you imagined!

	for bKey, bValue in pairs( bTable ) do
		if ( bValue.unknown ) then
			return nil
		end
		local bStructure = bValue.structure
		local bPrecision = bValue.precision

		for dKey, dValue in pairs( dTable ) do
			if ( dValue.unknown ) then
				return nil
			end
			local dStructure = dValue.structure
			local dPrecision = dValue.precision

			local calculatedAge = ageImpl ( bStructure, bPrecision, dStructure, dPrecision )
			if ( possibleAge == "NYA" ) then
				possibleAge = calculatedAge
			else
				if ( possibleAge ~= calculatedAge ) then
					possibleAge = nil
				end
			end
		end
	end

	return possibleAge
end

function ageImpl ( bStructure, bPrecision, dStructure, dPrecision )
	if ( not bStructure or not dStructure or bPrecision < 10 or dPrecision < 10 ) then
		return nil
	end

 	if ( bPrecision == 10 or dPrecision == 10 ) then
 		if ( bStructure.month < dStructure.month ) then
 			return dStructure.year - bStructure.year
 		end
 		if ( bStructure.month == dStructure.month ) then
 			return nil
 		end
 		if ( bStructure.month > dStructure.month ) then
 			return dStructure.year - bStructure.year - 1
 		end
 	end
 
  	if ( bStructure.month < dStructure.month ) then
 		return dStructure.year - bStructure.year
 	end
 	if ( bStructure.month == dStructure.month ) then
	  	if ( bStructure.day <= dStructure.day ) then
	 		return dStructure.year - bStructure.year
	 	else 
	 		return dStructure.year - bStructure.year - 1
 		end
 	end
 	if ( bStructure.month > dStructure.month ) then
 		return dStructure.year - bStructure.year - 1
 	end

	return nil
end

-- проверка на совпадающие даты с разной моделью календаря
function checkDupDates( t )
	if #t > 1 then
		local removed = false;
		local j = 1;
		-- проверка на совпадающие даты с разной моделью календаря
		while (j <= #t)  do
			local i = 1;
			while (i <= #t)  do
				if i ~= j then
					if (os.time(t[j].structure) == os.time(t[i].structure)) then
						if ((t[j].calendarmodel == 'gregorian') and 
							(t[i].calendarmodel == 'julian')) then
							removed = true;
							break;
						else
							table.remove(t, i)
						end
					else
					  i = i + 1;
					end
				else
					i = i + 1;
				end
			end
			if removed then
				removed = false;
				table.remove(t, j);
			else
				j = j+1;
			end
		end
	end
end


--[[
Вызывается из Шаблон:Обавторе, если не задан параметр ДАТАРОЖДЕНИЯ
{{#invoke:Wikidata/Даты|dateOfBirth}}
]]
function p.dateOfBirth()
    local appendToCategory = false
    local bTable = p.parseProperty ( "p569" )
    if not bTable then
        return ''
    end
    local dTable = p.parseProperty ( "p570" )
    checkDupDates(bTable);
    local result = ''
    for key, value in pairs(bTable) do
        if result ~= '' then
            result = result .. ' или '
        end
        if ( appendToCategory ) then
            result = result .. formatDate(value, 'bday', 'Родившиеся ', 'К:Персоналии, чья дата рождения не установлена')
        else
            result = result .. formatDate(value, 'bday', nil, nil )
        end
    end
    if ( not dTable ) then
        local age = ageCurrent( bTable )
        if ( age ) then
            result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'ru' ):plural( age, 'год', 'лет', 'года') .. ')</span>'
            if ( age > 150 and appendToCategory ) then
                result = result .. '[[К:Википедия:Статьи о персоналиях с большим текущим возрастом]]'
            end
        end
    end
    return result
end

--[[
Вызывается из Шаблон:Обавторе, если не задан параметр ДАТАСМЕРТИ
{{#invoke:Wikidata/Даты|dateOfDeath}}
]]
function p.dateOfDeath()
    local appendToCategory = false
    local dTable = p.parseProperty ( "p570" )
    if not dTable then
        return ''
    end
    local bTable = p.parseProperty ( "p569" )
    checkDupDates(dTable);
    local result = ''
    for key, value in pairs( dTable ) do
        if result ~= '' then
            result = result .. ' или '
        end
        if ( appendToCategory ) then
            result = result .. formatDate(value, 'dday', 'Умершие ', 'К:Персоналии, чья дата смерти не установлена')
        else
            result = result .. formatDate(value, 'dday', nil, nil )
        end
    end
    if ( bTable and dTable ) then
        local age = age( bTable, dTable )
        if ( age ) then
            result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'ru' ):plural( age, 'год', 'лет', 'года') .. ')</span>'
            if ( age > 150 and appendToCategory ) then
                result = result .. '[[К:Википедия:Статьи о персоналиях с большим возрастом во время смерти]]'
            end
        end
    end
    return result
end

return p