Jump to content

Модул:Wikidata/date: Тафовут байни таҳрирҳо

Мавод аз Википедиа — донишномаи озод
Content deleted Content added
No edit summary
No edit summary
 
(11 intermediate revisions by the same user not shown)
Сатри 1: Сатри 1:
--settings
--settings
local categoryUnknownBirthDate = '[[Гурӯҳ:Ашхосе, ки санаи таваллудашон оварда нашудааст]]';
local categoryUnknownBirthDate = '[[Гурӯҳ:Ашхосе, ки таърихи таваллудашон оварда нашудааст]]';
local categoryUnknownDeathDate = '[[Гурӯҳ:Ашхосе, ки санаи маргашон оварда нашудааст]]';
local categoryUnknownDeathDate = '[[Гурӯҳ:Ашхосе, ки таърихи даргузашташон оварда нашудааст]]';
local categoryBigCurrentAge = '[[Гурӯҳ:Википедиа:Статьи о персоналиях с большим текущим возрастом]]'
local categoryBigCurrentAge = '[[Гурӯҳ:Википедия:Мақолаҳо дар бораи ашхоси бо синну соли калони ҷорӣ]]'
local categoryBigDeathAge = '[[Гурӯҳ:Википедиа:Статьи о персоналиях с большим возрастом во время смерти]]'
-- local categoryBigDeathAge = '[[Гурӯҳ:Википедия:Мақолаҳо дар бораи ашхоси солманд дар замони даргузашт]]' //deleted -d.bratchuk 05-07-2016
local categoryBiographiesOfLivingPersons = '[[Гурӯҳ:Википедия:Зиндагиномаи муосирон]]'
local categoryRecentlyDeceased = '[[Гурӯҳ:Википедия:Ашхосе, ки то як соли пеш даргузаштанд]]'
local nowLabel = 'то ҳол'


local moduleDates = require( "Module:Dates" )
local moduleDates = require( "Module:Dates" )
local moduleWikidata = require( "Module:Wikidata" )
local moduleWikidata = require( "Module:Wikidata" )


function deepcopy(orig)
local function deepcopy(orig)
local orig_type = type(orig)
local orig_type = type(orig)
local copy
local copy
Сатри 21: Сатри 24:
end
end
return copy
return copy
end

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

local shift = 0;
if ( bStructure.year < 0 and dStructure.year > 0 ) then
shift = -1;
end

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

return nil
end
end


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


Сатри 50: Сатри 92:


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


Сатри 66: Сатри 108:
local dStructure = dValue.structure
local dStructure = dValue.structure
local dPrecision = dValue.precision
local dPrecision = dValue.precision
if ( bValue.calendar == 'julian' and dValue.calendar == 'gregorian' ) then
-- to calculate age, need to adjust bStructure to gregorian calendar
local shift = math.floor(bStructure.year/100-2) - math.floor(bStructure.year/400)
-- TODO: re-implement this properly
bStructure.day = bStructure.day + shift
end


local calculatedAge = ageImpl ( bStructure, bPrecision, dStructure, dPrecision )
local calculatedAge = ageImpl ( bStructure, bPrecision, dStructure, dPrecision )
Сатри 81: Сатри 129:
end
end


local function parseISO8601Date(str)
function ageImpl ( bStructure, bPrecision, dStructure, dPrecision )
local pattern = "(%-?%d+)%-(%d+)%-(%d+)T"
if ( not bStructure or not dStructure or bPrecision < 10 or dPrecision < 10 ) then
local Y, M, D = mw.ustring.match( str, pattern )
return nil
return tonumber(Y), tonumber(M), tonumber(D)
end
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
local function parseISO8601Time(str)
if ( bStructure.month < dStructure.month ) then
local pattern = "T(%d+):(%d+):(%d+)%Z"
return dStructure.year - bStructure.year
local H, M, S = mw.ustring.match( str, pattern)
end
return tonumber(H), tonumber(M), tonumber(S)
if ( bStructure.month == dStructure.month ) then
end
if ( bStructure.day <= dStructure.day ) then
return dStructure.year - bStructure.year
local function parseISO8601Offset(str)
else
if str:sub(-1)=="Z" then return 0,0 end -- ends with Z, Zulu time
return dStructure.year - bStructure.year - 1
end
-- matches ±hh:mm, ±hhmm or ±hh; else returns nils
end
local pattern = "([-+])(%d%d):?(%d?%d?)$"
if ( bStructure.month > dStructure.month ) then
local sign, oh, om = mw.ustring.match( str, pattern)
return dStructure.year - bStructure.year - 1
sign, oh, om = sign or "+", oh or "00", om or "00"
end

return tonumber(sign .. oh), tonumber(sign .. om)
return nil
end
end


local function parseISO8601(str)
-- returns table of time+precision values for specified property
if 'table' == type(str) then
function parseProperty ( context, options, propertyId )
if str.args and str.args[1] then
if ( not context ) then error( 'context not specified'); end;
str = '' .. str.args[1]
if ( not options ) then error( 'options not specified'); end;
else
if ( not options.entity ) then error( 'options.entity is missing'); end;
return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str )
if ( not propertyId ) then error( 'propertyId not specified'); end;
end
end


local Y,M,D = parseISO8601Date(str)
local claims = context.selectClaims( options, propertyId );
local h,m,s = parseISO8601Time(str)
if not claims then
local oh,om = parseISO8601Offset(str)

if not Y or not M or not D or not h or not m or not s or not oh or not om then
return nil
return nil
end
end


return tonumber(os.time({year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}))
local result = {}
for key, claim in pairs( claims ) do
table.insert ( result, parseClaim( claim ) );
end
return result
end
end


function parseClaim ( claim )
local function parseClaim ( claim )
if ( claim.mainsnak.snaktype == "value" ) then
if ( claim.mainsnak.snaktype == "value" ) then
-- The calendar model used for saving the data is always the proleptic Gregorian calendar according to ISO 8601.
-- The calendar model used for saving the data is always the proleptic Gregorian calendar according to ISO 8601.
Сатри 141: Сатри 179:
local structure = os.date("*t", unixtime)
local structure = os.date("*t", unixtime)
local precision = tonumber( claim.mainsnak.datavalue.value.precision )
local precision = tonumber( claim.mainsnak.datavalue.value.precision )
local calendarmodel = 'gregorian';
local item = { structure=structure, precision=precision }
if (mw.ustring.find(claim.mainsnak.datavalue.value.calendarmodel, 'Q1985786', 1, true)) then
calendarmodel = 'julian';
end
local item = { structure=structure, precision=precision, calendar = calendarmodel }
return item;
return item;
elseif ( claim.mainsnak.snaktype == "novalue" ) then
elseif ( claim.mainsnak.snaktype == "novalue" ) then
Сатри 150: Сатри 192:
return { unknown="unknown" };
return { unknown="unknown" };
end
end
end

-- returns table of time+precision values for specified property
local function parseProperty ( context, options, propertyId )
if ( not context ) then error( 'context not specified'); end;
if ( not options ) then error( 'options not specified'); end;
if ( not options.entity ) then error( 'options.entity is missing'); end;
if ( not propertyId ) then error( 'propertyId not specified'); end;

local claims = context.selectClaims( options, propertyId );
if not claims then
return nil
end

local result = {}
for key, claim in pairs( claims ) do
table.insert ( result, parseClaim( claim ) );
end
return result
end
end


-- проверка на совпадающие даты с разной моделью календаря
-- проверка на совпадающие даты с разной моделью календаря
function checkDupDates( t )
local function checkDupDates( t )
if #t > 1 then
if #t > 1 then
local removed = false;
local removed = false;
Сатри 188: Сатри 249:


-- returns first qualifier of specified propertyId
-- returns first qualifier of specified propertyId
function getQualifierDataValue( statement, qualifierPropertyId )
local function getQualifierWithDataValue( statement, qualifierPropertyId )
if ( statement.qualifiers
if ( statement.qualifiers
and statement.qualifiers[qualifierPropertyId] ) then
and statement.qualifiers[qualifierPropertyId] ) then
Сатри 194: Сатри 255:
for _, qualifier in ipairs( qualifiers ) do
for _, qualifier in ipairs( qualifiers ) do
if (qualifier.datavalue) then
if (qualifier.datavalue) then
return qualifier.datavalue;
return qualifier;
end
end
end
end
Сатри 203: Сатри 264:
local p = {}
local p = {}


local function formatDecade( time, categoryNamePrefix )
function p.formatDateOfBirthClaim( context, options, statement )
if ( time.year < 0 ) then
local year = math.floor( math.abs(time.year) / 10 ) * 10;
if ( categoryNamePrefix ) then
return year .. '-уми то м. [[Category:' .. categoryNamePrefix .. ' солҳои ' .. year .. '-уми то м.]]';
else
return '' .. year .. ' то м.';
end
else
local year = math.floor( time.year / 10 ) * 10;
if ( categoryNamePrefix ) then
return year .. '-ум[[Category:' .. categoryNamePrefix .. ' солҳои ' .. year .. '-ум]]';
else
return '' .. year;
end
end
end

local function formatCentury( time, categoryNamePrefix )
local moduleRoman = require( "Module:RomanNumber" )
if ( time.year < 0 ) then
local century = math.floor( (math.abs( time.year) - 1) / 100 ) + 1;
local infix = ' дар ';
if century == 2 then infix = ' дар '; end;
if ( moduleRoman ) then
century = moduleRoman.toRomanNumber( century );
end
if ( categoryNamePrefix ) then
return '[[асри ' .. century .. ' то м.]][[Category:' .. categoryNamePrefix .. ' асри ' .. century .. ' то м.]]'
else
return '[[асри ' .. century .. ' то м.]]'
end
else
local century = math.floor( (time.year - 1) / 100 ) + 1;
local infix = ' ';
if (century == 2) then infix = ' ' end;
if ( moduleRoman ) then
century = moduleRoman.toRomanNumber( century );
end
if ( categoryNamePrefix ) then
return '[[асри ' .. century .. ']][[Category:' .. categoryNamePrefix .. ' асри ' .. century .. ']]'
else
return '[[асри ' .. century .. ']]'
end
end
end

local function formatMillenium( time, categoryNamePrefix )
if ( time.year < 0 ) then
local millenium = math.floor( (math.abs( time.year) - 1) / 1000 ) + 1;
if ( categoryNamePrefix ) then
if ( millenium == 2 ) then
return '[[ҳазорсолаи ' .. millenium .. ' то м.]][[Category:' .. categoryNamePrefix .. ' ҳазорсолаи ' .. millenium .. ' то м.]]'
else
return '[[ҳазорсолаи ' .. millenium .. ' то м.]][[Category:' .. categoryNamePrefix .. ' ҳазорсолаи ' .. millenium .. ' то м.]]'
end
else
return '[[ҳазорсолаи ' .. millenium .. ' то м.]]'
end
else
local millenium = math.floor( (time.year - 1) / 1000 ) + 1;
if ( categoryNamePrefix ) then
if ( millenium == 2 ) then
return '[[ҳазорсолаи ' .. millenium .. ']][[Category:' .. categoryNamePrefix .. ' ҳазорсолаи ' .. millenium .. ']]'
else
return '[[ҳазорсолаи ' .. millenium .. ']][[Category:' .. categoryNamePrefix .. ' ҳазорсолаи ' .. millenium .. ']]'
end
else
return '[[ҳазорсолаи ' .. millenium .. ']]'
end
end
end

local function formatDateImpl( value, options, microformatClass, categoryPrefix, leftBracket, rightBracket )
if ( not value ) then error( 'value not specified'); end;
if ( not options ) then error( 'options not specified'); end;

-- The calendar model used for saving the data is always the proleptic Gregorian calendar according to ISO 8601.
local timeISO8601 = string.gsub( string.gsub( tostring( value.time ), '-00%-', '-01-' ), '-00T', '-01T' )
local unixtime = parseISO8601( timeISO8601 )
if not unixtime then
return ''
end

local structure = os.date("*t", unixtime)
local precision = tonumber( value.precision )
if precision <= 6 then
return formatMillenium(structure, categoryPrefix)
end
if precision == 7 then
return formatCentury(structure, categoryPrefix)
end

if precision == 8 then
return formatDecade(structure, categoryPrefix)
end

if precision == 9 then
local tCopy = deepcopy( structure )
tCopy.day = nil
tCopy.month = nil
return moduleDates.formatWikiImpl(tCopy, tCopy, infoclass, categoryPrefix, leftBracket, rightBracket )
end

-- year and month only
if precision == 10 then
local tCopy = deepcopy( structure )
tCopy.day = nil
return moduleDates.formatWikiImpl(tCopy, tCopy, infoclass, categoryPrefix, leftBracket, rightBracket )
end

local calendarmodel = 'gregorian';
if (mw.ustring.find(value.calendarmodel, 'Q1985786', 1, true)) then
calendarmodel = 'julian';
end

if (calendarmodel == 'gregorian') then
return moduleDates.formatWikiImpl( structure, structure, microformatClass, categoryPrefix, leftBracket, rightBracket )
else
return p.formatAsJulian( unixtime, infoclass, categoryPrefix, leftBracket, rightBracket )
end
end

local function formatApproximateDateClaim( context, options, statement )
if ( not context ) then error( 'context not specified'); end;
if ( not context ) then error( 'context not specified'); end;
if ( not options ) then error( 'options not specified'); end;
if ( not options ) then error( 'options not specified'); end;
if ( not options.entity ) then error( 'options.entity is missing'); end;
if ( not options.entity ) then error( 'options.entity is missing'); end;
if ( not statement ) then error( 'statement not specified'); end;
if ( not statement ) then error( 'statement not specified'); end;
if options.nocat then categoryUnknownBirthDate = "" end
local qNotSoonerThan = getQualifierWithDataValue( statement, 'P1319' );
local qNotLaterThan = getQualifierWithDataValue( statement, 'P1326' );
if ( qNotSoonerThan or qNotLaterThan ) then
local results = {};
if ( qNotSoonerThan ) then
local formattedDate = formatDateImpl( qNotSoonerThan.datavalue.value, {}, nil, nil, options.leftBracket, options.rightBracket )
local value = 'на&nbsp;пештар&nbsp;аз&nbsp;' .. context.wrapSnak( formattedDate, qNotSoonerThan.hash )
table.insert( results, context.wrapQualifier( value, 'P1319' ) );
end
if ( qNotLaterThan ) then
local formattedDate = formatDateImpl( qNotLaterThan.datavalue.value, {}, nil, nil, options.leftBracket, options.rightBracket )
local value = 'на&nbsp;дертар&nbsp;аз&nbsp;' .. context.wrapSnak( formattedDate, qNotLaterThan.hash )
table.insert( results, context.wrapQualifier( value, 'P1326' ) );
end
return mw.text.listToText( results, ' и ' , ' и ' ) .. categoryUnknownBirthDate;
end
return nil
end


function p.formatDateOfBirthClaim( context, options, statement )
if ( statement.mainsnak.snaktype == 'somevalue' ) then
local value = formatApproximateDateClaim( context, options, statement )
local qNotSoonerThan = getQualifierDataValue( statement, 'P1319' );
if value then
local qNotLaterThan = getQualifierDataValue( statement, 'P1326' );
return value
if ( qNotSoonerThan or qNotLaterThan ) then
local results = {};
if ( qNotSoonerThan ) then
table.insert( results, 'қабл&nbsp;аз&nbsp;' .. formatDateImpl( qNotSoonerThan.value, {}, nil, nil ) );
end
if ( qNotLaterThan ) then
table.insert( results, 'пас&nbsp;аз&nbsp;' .. formatDateImpl( qNotLaterThan.value, {}, nil, nil ) );
end
return mw.text.listToText( results, ' ва ' , ' ва ' ) .. categoryUnknownBirthDate;
end
end
end


Сатри 228: Сатри 426:
options['value-function'] = 'formatBirthDate';
options['value-function'] = 'formatBirthDate';
options.i18n.somevalue = '\'\'номаълум\'\'' .. categoryUnknownBirthDate;
options.i18n.somevalue = '\'\'номаълум\'\'' .. categoryUnknownBirthDate;
options.i18n.approximate = '<span style="border-bottom: 1px dotted; cursor: help;" title="наздики">наз. </span>';
local result = context.formatStatementDefault( context, options, statement );
local result = context.formatStatementDefault( context, options, statement );


Сатри 237: Сатри 434:
local age = ageCurrent( bTable )
local age = ageCurrent( bTable )
if ( age ) then
if ( age ) then
result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'tg' ):plural( age, 'сол', 'сол', 'сола') .. ')</span>'
if ( options.suppressAge == nil or options.suppressAge == '' ) then
result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'tg' ):plural( age, 'сол', 'сол', 'сола') .. ')</span>'
end
if ( age > 150 and not options.nocat ) then
if ( not options.nocat ) then
result = result .. categoryBigCurrentAge;
end
if ( age > 115 ) then
result = result .. categoryBigCurrentAge;
else
result = result .. categoryBiographiesOfLivingPersons;
end
end
end
end
end
end
Сатри 248: Сатри 451:


function p.formatDateOfDeathClaim( context, options, statement )
function p.formatDateOfDeathClaim( context, options, statement )
local value = formatApproximateDateClaim( context, options, statement )
if ( not context ) then error( 'context not specified'); end;
if value then
if ( not options ) then error( 'options not specified'); end;
return value
if ( not options.entity ) then error( 'options.entity is missing'); end;
if ( not statement ) then error( 'statement not specified'); end;

if ( statement.mainsnak.snaktype == 'somevalue' ) then
local qNotSoonerThan = getQualifierDataValue( statement, 'P1319' );
local qNotLaterThan = getQualifierDataValue( statement, 'P1326' );
if ( qNotSoonerThan or qNotLaterThan ) then
local results = {};
if ( qNotSoonerThan ) then
table.insert( results, 'бар&nbsp;вақт&nbsp;' .. formatDateImpl( qNotSoonerThan.value, {}, nil, nil ) );
end
if ( qNotLaterThan ) then
table.insert( results, 'бе&nbsp;вақт&nbsp;' .. formatDateImpl( qNotLaterThan.value, {}, nil, nil ) );
end
return mw.text.listToText( results, ' и ' , ' и ' ) .. categoryUnknownDeathDate;
end
end
end


Сатри 272: Сатри 460:
options['value-function'] = 'formatDeathDate';
options['value-function'] = 'formatDeathDate';
options.i18n.somevalue = '\'\'номаълум\'\'' .. categoryUnknownDeathDate;
options.i18n.somevalue = '\'\'номаълум\'\'' .. categoryUnknownDeathDate;
options.i18n.approximate = '<span style="border-bottom: 1px dotted; cursor: help;" title="около">ок. </span>';
local result = context.formatStatementDefault( context, options, statement );
local result = context.formatStatementDefault( context, options, statement );


Сатри 281: Сатри 468:
local age = age( bTable, dTable )
local age = age( bTable, dTable )
if ( age ) then
if ( age ) then
result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'tg' ):plural( age, 'сол', 'сол', 'сола') .. ')</span>'
if ( options.suppressAge == nil or options.suppressAge == '' ) then
result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'tg' ):plural( age, 'сол', 'сол', 'сола') .. ')</span>'
end
if ( age > 150 and (not context.options or not context.options.nocat) ) then
end
result = result .. categoryBigDeathAge;
end
-- returns category to recently deceased persons
local unixAvailable, unixDateOfDeath = pcall(function()
local r = os.time(dTable[1].structure)
if ( r ~= os.time() ) then
return r
end
error()
end)
if ( unixAvailable and os.time() - unixDateOfDeath < 31536000 and not options.nocat ) then
result = result .. categoryRecentlyDeceased
end
end
end
end
Сатри 297: Сатри 495:
if ( not value ) then error( 'value not specified'); end;
if ( not value ) then error( 'value not specified'); end;
local microformatClass = nil
if options.microformat ~= '-' then
microformatClass = options.microformat or 'bday'
end
if ( options.nocat ) then
if ( options.nocat ) then
return formatDateImpl( value, options, 'bday', nil )
return formatDateImpl( value, options, microformatClass, nil, options.leftBracket, options.rightBracket )
else
else
return formatDateImpl( value, options, 'bday', 'Зодагони' )
return formatDateImpl( value, options, microformatClass, 'Зодагони', options.leftBracket, options.rightBracket )
end
end
end
end
Сатри 310: Сатри 512:
if ( not value ) then error( 'value not specified'); end;
if ( not value ) then error( 'value not specified'); end;


local microformatClass = nil
if ( options.nocat ) then
if options.microformat ~= '-' then
return formatDateImpl( value, options, 'dday', nil )
microformatClass = options.microformat or 'dday'
end
if ( options.nocat and options.nocat ~= '' ) then
return formatDateImpl( value, options, microformatClass, nil, options.leftBracket, options.rightBracket )
else
else
return formatDateImpl( value, options, 'dday', 'Фавтидагони' )
return formatDateImpl( value, options, microformatClass, 'Даргузаштагони', options.leftBracket, options.rightBracket )
end
end
end
end
Сатри 323: Сатри 529:
if ( not value ) then error( 'value not specified'); end;
if ( not value ) then error( 'value not specified'); end;


local microformatClass = options.microfortmat or nil;
local microformatClass = options.microformat or nil;
if ( options.nocat ) then
if ( options.nocat and options.nocat ~= '' ) then
return formatDateImpl( value, options, microformatClass, nil )
return formatDateImpl( value, options, microformatClass, nil, options.leftBracket, options.rightBracket )
else
else
local categoryPrefix = options.categoryPrefix or nil;
local categoryPrefix = options.categoryPrefix or nil;
return formatDateImpl( value, options, microformatClass, categoryPrefix )
return formatDateImpl( value, options, microformatClass, categoryPrefix, options.leftBracket, options.rightBracket )
end
end
end
end


function formatDateImpl( value, options, microformatClass, categoryPrefix )
function p.formatDateIntervalProperty( context, options )
if ( not value ) then error( 'value not specified'); end;
if ( not context ) then error( 'context not specified' ); end;
if ( not options ) then error( 'options not specified'); end;
if ( not options ) then error( 'options not specified' ); end;
if ( not options.entity ) then error( 'options.entity missing' ); end;


-- Получение нужных утверждений
-- The calendar model used for saving the data is always the proleptic Gregorian calendar according to ISO 8601.
local WDS = require( 'Module:WikidataSelectors' );
local timeISO8601 = string.gsub( string.gsub( tostring( value.time ), '-00%-', '-01-' ), '-00T', '-01T' )
local unixtime = parseISO8601( timeISO8601 )
local fromProperty = options.property
if options.from and options.from ~= '' then

fromProperty = options.from
local structure = os.date("*t", unixtime)
local precision = tonumber( value.precision )
if precision == 7 then
return formatCentury(structure, categoryPrefix)
end
end
local fromClaims = WDS.filter( options.entity.claims, fromProperty );
local toClaims = WDS.filter( options.entity.claims, options.to );
local withinClaims = WDS.filter( options.entity.claims, options.within );


if precision == 8 then
if fromClaims == nil and toClaims == nil then
return ''
return formatDecade(structure, categoryPrefix)
end
end


local formattedFromClaims = {}
if precision == 9 then
if fromClaims then
local tCopy = deepcopy( structure )
for i, claim in ipairs( fromClaims ) do
tCopy.day = nil
local formattedStatement = context.formatStatement( options, claim )
tCopy.month = nil
if formattedStatement then
return moduleDates.formatWikiImpl(tCopy, tCopy, infoclass, categoryPrefix)
formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( options.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>'
end
table.insert( formattedFromClaims, formattedStatement )
end
end
end


local formattedToClaims = {}
-- year and month only
local toOptions = deepcopy( options )
if precision == 10 then
toOptions.property = options.to
local tCopy = deepcopy( structure )
toOptions.novalue = nowLabel
tCopy.day = nil
if toClaims then
return moduleDates.formatWikiImpl(tCopy, tCopy, infoclass, categoryPrefix)
for i, claim in ipairs( toClaims ) do
end
local formattedStatement = context.formatStatement( toOptions, claim )
if formattedStatement then
formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( toOptions.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>'
table.insert( formattedToClaims, formattedStatement )
end
end
end


local calendarmodel = 'gregorian';
local out = ''
local fromOut = mw.text.listToText( formattedFromClaims, options.separator, options.conjunction )
if (mw.ustring.find(value.calendarmodel, 'Q1985786', 1, true)) then
local toOut = mw.text.listToText( formattedToClaims, options.separator, options.conjunction )
calendarmodel = 'julian';
end
if fromOut ~= '' or toOut ~= '' then

if (calendarmodel == 'gregorian') then
if fromOut ~= '' then
out = fromOut
return moduleDates.formatWikiImpl( structure, structure, microformatClass, categoryPrefix )
else
return p.formatAsJulian( unixtime, infoclass, categoryPrefix )
end
end

function formatDecade( time, categoryNamePrefix )
local year = math.floor( time.year / 10 ) * 10;
if ( categoryNamePrefix ) then
return year .. '[[Гурӯҳ:' .. categoryNamePrefix .. ' соли ' .. year .. ']]';
else
return '' .. year;
end
end

function formatCentury( time, categoryNamePrefix )
local moduleRoman = require( "Module:RomanNumber" )
if ( time.year < 0 ) then
local century = math.floor( (math.abs( time.year) - 1) / 100 ) + 1;
if ( moduleRoman ) then
century = moduleRoman.toRomanNumber( century );
end
if ( categoryNamePrefix ) then
return '[[асри ' .. century .. ' то милод]][[Гурӯҳ:' .. categoryNamePrefix .. ' соли ' .. century .. ' то милод]]'
else
else
out = '?'
return '[[асри ' .. century .. ' то милод]]'
end
end

else
if toOut ~= '' then
local century = math.floor( (time.year - 1) / 100 ) + 1;
out = out .. ' — ' .. toOut
if ( moduleRoman ) then
century = moduleRoman.toRomanNumber( century );
end
if ( categoryNamePrefix ) then
return '[[асри ' .. century .. ']][[Гурӯҳ:' .. categoryNamePrefix .. ' асри ' .. century .. ']]'
else
else
if withinClaims ~= nil then
return '[[асри ' .. century .. ']]'
out = 'аз ' .. out
else
out = out .. ' — ' .. nowLabel
end
end
end
end
end
end


if out ~= '' then
function parseISO8601Date(str)
if options.before then
local pattern = "(%-?%d+)%-(%d+)%-(%d+)T"
out = options.before .. out
local Y, M, D = mw.ustring.match( str, pattern )
end
return tonumber(Y), tonumber(M), tonumber(D)
if options.after then
end
out = out .. options.after
function parseISO8601Time(str)
local pattern = "T(%d+):(%d+):(%d+)%Z"
local H, M, S = mw.ustring.match( str, pattern)
return tonumber(H), tonumber(M), tonumber(S)
end
function parseISO8601Offset(str)
if str:sub(-1)=="Z" then return 0,0 end -- ends with Z, Zulu time
-- matches ±hh:mm, ±hhmm or ±hh; else returns nils
local pattern = "([-+])(%d%d):?(%d?%d?)$"
local sign, oh, om = mw.ustring.match( str, pattern)
sign, oh, om = sign or "+", oh or "00", om or "00"
return tonumber(sign .. oh), tonumber(sign .. om)
end
function parseISO8601(str)
if 'table'==type(str) then
if str.args and str.args[1] then
str = '' .. str.args[1]
else
return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str )
end
end
end
end

local Y,M,D = parseISO8601Date(str)
return out
local h,m,s = parseISO8601Time(str)
local oh,om = parseISO8601Offset(str)
return tonumber(os.time({year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}))
end
end


local lowestBoundary = parseISO8601('-900-02-20T00:00:00Z')
local lowestBoundary = parseISO8601('-900-02-20T00:00:00Z')
local mainBoundary = parseISO8601('1582-10-15T00:00:00Z')
local mainBoundary = parseISO8601('1582-10-05T00:00:00Z')
local lastBoundary = parseISO8601('1918-01-26T00:00:00Z');
local lastBoundary = parseISO8601('1918-01-31T00:00:00Z');


boundaries = {
local boundaries = {
-- from (G) till next will be diff(G = J + diff), at current
-- from (G) till next will be diff(G = J + diff), at current
{ lowestBoundary, -9 },
{ lowestBoundary, -9 },
{ parseISO8601('-700-02-21T00:00:00Z'), -8 },
{ parseISO8601('-700-02-29T00:00:00Z'), -8 },
{ parseISO8601('-600-02-22T00:00:00Z'), -7 },
{ parseISO8601('-600-02-29T00:00:00Z'), -7 },
{ parseISO8601('-500-02-23T00:00:00Z'), -6 },
{ parseISO8601('-500-02-29T00:00:00Z'), -6 },
{ parseISO8601('-300-02-24T00:00:00Z'), -5 },
{ parseISO8601('-300-02-29T00:00:00Z'), -5 },
{ parseISO8601('-200-02-25T00:00:00Z'), -4 },
{ parseISO8601('-200-02-29T00:00:00Z'), -4 },
{ parseISO8601('-100-02-26T00:00:00Z'), -3 },
{ parseISO8601('-100-02-29T00:00:00Z'), -3 },
{ parseISO8601('0000-00-00T00:00:00Z'), -2 },
{ parseISO8601('0000-00-00T00:00:00Z'), -2 },
{ parseISO8601('0100-02-27T00:00:00Z'), -1 },
{ parseISO8601('0100-02-29T00:00:00Z'), -1 },
{ parseISO8601('0200-02-28T00:00:00Z'), 0 },
{ parseISO8601('0200-02-29T00:00:00Z'), 0 },
{ parseISO8601('0300-03-01T00:00:00Z'), 1 },
{ parseISO8601('0300-02-29T00:00:00Z'), 1 },
{ parseISO8601('0500-03-02T00:00:00Z'), 2 },
{ parseISO8601('0500-02-29T00:00:00Z'), 2 },
{ parseISO8601('0600-03-03T00:00:00Z'), 3 },
{ parseISO8601('0600-02-29T00:00:00Z'), 3 },
{ parseISO8601('0700-03-04T00:00:00Z'), 4 },
{ parseISO8601('0700-02-29T00:00:00Z'), 4 },
{ parseISO8601('0900-03-05T00:00:00Z'), 5 },
{ parseISO8601('0900-02-29T00:00:00Z'), 5 },
{ parseISO8601('1000-03-06T00:00:00Z'), 6 },
{ parseISO8601('1000-02-29T00:00:00Z'), 6 },
{ parseISO8601('1100-03-07T00:00:00Z'), 7 },
{ parseISO8601('1100-02-29T00:00:00Z'), 7 },
{ parseISO8601('1300-03-08T00:00:00Z'), 8 },
{ parseISO8601('1300-02-29T00:00:00Z'), 8 },
{ parseISO8601('1400-03-09T00:00:00Z'), 9 },
{ parseISO8601('1400-02-29T00:00:00Z'), 9 },
{ parseISO8601('1500-03-10T00:00:00Z'), 10 },
{ parseISO8601('1500-02-29T00:00:00Z'), 10 },
{ parseISO8601('1700-03-11T00:00:00Z'), 11 },
{ parseISO8601('1700-02-29T00:00:00Z'), 11 },
{ parseISO8601('1800-03-12T00:00:00Z'), 12 },
{ parseISO8601('1800-02-29T00:00:00Z'), 12 },
{ parseISO8601('1900-03-13T00:00:00Z'), 13 },
{ parseISO8601('1900-02-29T00:00:00Z'), 13 },
{ lastBoundary, '' },
{ lastBoundary, '' },
};
};


-- Передаваемое время обязано быть по Григорианскому календарю (новому стилю)
-- Передаваемое время обязано быть по Юлианскому календарю (старому стилю)
function p.formatAsJulian( time, infocardClass, categoryNamePrefix )
function p.formatAsJulian( julTime, infocardClass, categoryNamePrefix, leftBracket, rightBracket )
if 'table'==type( time ) then
if 'table' == type( julTime ) then
if time.args and time.args[1] then
if julTime.args and julTime.args[1] then
time = tonumber( time.args[1] )
julTime = tonumber( julTime.args[1] )
else
else
return 'unknown argument type: ' .. type( time ) .. ': ' .. table.tostring( time )
return 'unknown argument type: ' .. type( julTime ) .. ': ' .. table.tostring( julTime )
end
end
end
end


local t = os.date("*t", time)
local t = os.date( "*t", julTime )
if ( time <= lowestBoundary ) then
if ( julTime <= lowestBoundary ) then
return "''санаи нодуруст (недостижимая точность)''";
return "''таърихи носаҳеҳ (саҳеҳии дастнорас)''";
end
end
if ( time >= lastBoundary ) then
if ( julTime >= lastBoundary ) then
return "''санаи нодуруст (юлианский не используется после 1918-01-26)''";
return "''таърихи носаҳеҳ (тақвими юлианӣ пас аз 1918-01-26 ба кор бурда мешавад)''";
end
end


Сатри 501: Сатри 669:
local b1 = boundaries[i][1];
local b1 = boundaries[i][1];
local b2 = boundaries[i + 1][1];
local b2 = boundaries[i + 1][1];
if ( b1 <= time and time < b2 ) then
if ( b1 <= julTime and julTime < b2 ) then
local b1s = os.date("*t", b1)
local b1s = os.date( "*t", b1 )
if ( b1s.year == t.year and b1s.month == t.month and b1s.day == t.day) then
if ( b1s.year == t.year and b1s.month == t.month and b1s.day == t.day) then
if ( time < mainBoundary ) then
if ( julTime < mainBoundary ) then
-- only julian
-- only julian
return moduleDates.formatWikiImpl( {year=t.year, month=2, day=29}, {year=t.year, month=2, day=29}, infocardClass, categoryNamePrefix );
return moduleDates.formatWikiImpl( {year=t.year, month=2, day=29}, {year=t.year, month=2, day=29}, infocardClass, categoryNamePrefix, leftBracket, rightBracket );
else
else
--julian and grigorian
--julian and grigorian
return moduleDates.formatWikiImpl( {year=t.year, month=2, day=29}, t, infocardClass, categoryNamePrefix )
return moduleDates.formatWikiImpl( {year=t.year, month=2, day=29}, t, infocardClass, categoryNamePrefix, leftBracket, rightBracket )
end
end
else
else
local julian = os.date("*t", time - boundaries[i][2] * 24 * 60 * 60);
local gregTime = os.date( "*t", julTime + boundaries[i][2] * 24 * 60 * 60 );
if ( time < mainBoundary ) then
if ( julTime < mainBoundary ) then
-- only julian
-- only julian
return moduleDates.formatWikiImpl( julian, julian, infocardClass, categoryNamePrefix );
return moduleDates.formatWikiImpl( t, t, infocardClass, categoryNamePrefix, leftBracket, rightBracket );
else
else
--julian and grigorian
--julian and grigorian
return moduleDates.formatWikiImpl( julian, t, infocardClass, categoryNamePrefix )
return moduleDates.formatWikiImpl( t, gregTime, infocardClass, categoryNamePrefix, leftBracket, rightBracket )
end
end
end
end
Сатри 524: Сатри 692:
end
end


if ( time >= lastBoundary ) then
if ( julTime >= lastBoundary ) then
return "''хато дар модули Module:Wikidata/date (не найден сдвиг конвертации календаря)''";
return "''хато дар модули Модул:Wikidata/date (тағйири табдили тақвим ёфт нашуд)''";
end
end
end
end

Нусхаи кунунӣ то 06:06, 23 феврали 2023

Вычисляет и отображает даты рождения и смерти из Викидода с поддержкой отображения дат по старому стилю, если в Викиданных указана модель пролептического юлианского календаря (d:Q1985786).

Вызов всех функций данного шаблона осуществляется только из промежуточных шаблонов wikidata/pNNN:

Вызов функций шаблонов из других модулей и шаблонов запрещён и делается на страх и риск авторов соответствующих «костылей», вместе с возлаганием на них обязательств по поддержке в случае изменений названий модуля, названий функций или спецификаций вызовов.

Санҷишҳо

[вироиши манбаъ]

12 тестов провалено.

test_formatAsJulian:

Текст Ожидаемое значение Фактическое значение
N {{#invoke:Wikidata/date | formatAsJulian |-14826758400}} 28 феврали 1500 28 феврал 1500
N {{#invoke:Wikidata/date | formatAsJulian |-14826672000}} 29 феврали 1500 29 феврал 1500
N {{#invoke:Wikidata/date | formatAsJulian |-14826585600}} 1 марти 1500 2 март 1500
N {{#invoke:Wikidata/date | formatAsJulian |-11670220800}} 28 феврал (9 марти) 1600 9 (19) март 1600
N {{#invoke:Wikidata/date | formatAsJulian |-11670134400}} 29 феврал (10 марти) 1600 10 (20) март 1600
N {{#invoke:Wikidata/date | formatAsJulian |-11670048000}} 1 (11) марти 1600 11 (21) март 1600
N {{#invoke:Wikidata/date | formatAsJulian | -8514460800}} 28 феврал (10 марти) 1700 10 (21) март 1700
N {{#invoke:Wikidata/date | formatAsJulian | -8514374400}} 29 феврал (11 марти) 1700 11 (22) март 1700
N {{#invoke:Wikidata/date | formatAsJulian | -8514288000}} 1 (12) марти 1700 12 (23) март 1700
N {{#invoke:Wikidata/date | formatAsJulian | -2208988800}} 20 декабри 1899 (1 январи 1900) 1 (13) январ 1900
N {{#invoke:Wikidata/date | formatAsJulian | -1734480000}} 2 (15) январи 1915 15 (28) январ 1915
N {{#invoke:Wikidata/date | formatAsJulian | 946684800}} некорректная дата (юлианский не используется после 1918-01-26) таърихи носаҳеҳ (тақвими юлианӣ пас аз 1918-01-26 ба кор бурда мешавад)

--settings
local categoryUnknownBirthDate = '[[Гурӯҳ:Ашхосе, ки таърихи таваллудашон оварда нашудааст]]';
local categoryUnknownDeathDate = '[[Гурӯҳ:Ашхосе, ки таърихи даргузашташон оварда нашудааст]]';
local categoryBigCurrentAge = '[[Гурӯҳ:Википедия:Мақолаҳо дар бораи ашхоси бо синну соли калони ҷорӣ]]'
-- local categoryBigDeathAge = '[[Гурӯҳ:Википедия:Мақолаҳо дар бораи ашхоси солманд дар замони даргузашт]]' //deleted -d.bratchuk 05-07-2016
local categoryBiographiesOfLivingPersons = '[[Гурӯҳ:Википедия:Зиндагиномаи муосирон]]'
local categoryRecentlyDeceased = '[[Гурӯҳ:Википедия:Ашхосе, ки то як соли пеш даргузаштанд]]'
local nowLabel = 'то&nbsp;ҳол'

local moduleDates = require( "Module:Dates" )
local moduleWikidata = require( "Module:Wikidata" )

local function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
        setmetatable(copy, deepcopy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

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

	local shift = 0;
	if ( bStructure.year < 0 and dStructure.year > 0 ) then
		shift = -1;
	end

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

	return nil
end

-- accepts table of time+precision values
local 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
local 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
			if ( bValue.calendar == 'julian' and dValue.calendar == 'gregorian' ) then
				-- to calculate age, need to adjust bStructure to gregorian calendar
				local shift = math.floor(bStructure.year/100-2) - math.floor(bStructure.year/400)
				-- TODO: re-implement this properly
				bStructure.day = bStructure.day + shift
			end

			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

local function parseISO8601Date(str)
	local pattern = "(%-?%d+)%-(%d+)%-(%d+)T"
	local Y, M, D = mw.ustring.match( str, pattern )
	return tonumber(Y), tonumber(M), tonumber(D)
end
 
local function parseISO8601Time(str)
	local pattern = "T(%d+):(%d+):(%d+)%Z"
	local H, M, S = mw.ustring.match( str, pattern)
	return tonumber(H), tonumber(M), tonumber(S)
end
 
local function parseISO8601Offset(str)
	if str:sub(-1)=="Z" then return 0,0 end -- ends with Z, Zulu time
 
	-- matches ±hh:mm, ±hhmm or ±hh; else returns nils 
	local pattern = "([-+])(%d%d):?(%d?%d?)$"
	local sign, oh, om = mw.ustring.match( str, pattern) 
	sign, oh, om = sign or "+", oh or "00", om or "00"
 
	return tonumber(sign .. oh), tonumber(sign .. om)
end

local function parseISO8601(str)
	if 'table' == type(str) then
		if str.args and str.args[1] then
			str = '' .. str.args[1]
		else
			return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str )
		end
	end

	local Y,M,D = parseISO8601Date(str)
	local h,m,s = parseISO8601Time(str)
	local oh,om = parseISO8601Offset(str)

	if not Y or not M or not D or not h or not m or not s or not oh or not om then
		return nil
	end

	return tonumber(os.time({year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}))
end

local function parseClaim ( claim )
	if ( claim.mainsnak.snaktype == "value" ) then
		-- The calendar model used for saving the data is always the proleptic Gregorian calendar according to ISO 8601.
		local timeISO8601 = string.gsub( string.gsub( tostring( claim.mainsnak.datavalue.value.time ), '-00%-', '-01-' ), '-00T', '-01T' )
		local unixtime = parseISO8601( timeISO8601 )
		local structure = os.date("*t", unixtime)
		local precision = tonumber( claim.mainsnak.datavalue.value.precision )
		local calendarmodel = 'gregorian';
		if (mw.ustring.find(claim.mainsnak.datavalue.value.calendarmodel, 'Q1985786', 1, true)) then
			calendarmodel = 'julian';
		end
		local item = { structure=structure, precision=precision, calendar = calendarmodel }
		return item;
	elseif ( claim.mainsnak.snaktype == "novalue" ) then
		-- novalue
		return { unknown="novalue" };
	else
		--unknown 
		return { unknown="unknown" };
	end
end

-- returns table of time+precision values for specified property
local function parseProperty ( context, options, propertyId )
	if ( not context ) then error( 'context not specified'); end;
	if ( not options ) then error( 'options not specified'); end;
	if ( not options.entity ) then error( 'options.entity is missing'); end;
	if ( not propertyId ) then error( 'propertyId not specified'); end;

	local claims = context.selectClaims( options, propertyId );
	if not claims then
		return nil
	end

	local result = {}
	for key, claim in pairs( claims ) do
		table.insert ( result, parseClaim( claim ) );
	end
	return result
end

-- проверка на совпадающие даты с разной моделью календаря
local 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

-- returns first qualifier of specified propertyId
local function getQualifierWithDataValue( statement, qualifierPropertyId )
	if ( statement.qualifiers
			and statement.qualifiers[qualifierPropertyId] ) then
		local qualifiers = statement.qualifiers[qualifierPropertyId];
		for _, qualifier in ipairs( qualifiers ) do
			if (qualifier.datavalue) then
				return qualifier;
			end
		end
	end
	return nil;
end

local p = {}

local function formatDecade( time, categoryNamePrefix )
	if ( time.year < 0 ) then
		local year = math.floor( math.abs(time.year) / 10 ) * 10;
		if ( categoryNamePrefix ) then
			return year .. '-уми то м. [[Category:' .. categoryNamePrefix .. ' солҳои ' .. year .. '-уми то м.]]';
		else
			return '' .. year .. ' то м.';
		end
	else
		local year = math.floor( time.year / 10 ) * 10;
		if ( categoryNamePrefix ) then
			return year .. '-ум[[Category:' .. categoryNamePrefix .. ' солҳои ' .. year .. '-ум]]';
		else
			return '' .. year;
		end
	end
end

local function formatCentury( time, categoryNamePrefix )
	local moduleRoman = require( "Module:RomanNumber" )
	if ( time.year < 0 ) then
		local century = math.floor( (math.abs( time.year) - 1) / 100 ) + 1;
        local infix = ' дар ';
        if century == 2 then infix = ' дар '; end;
		if ( moduleRoman ) then
			century = moduleRoman.toRomanNumber( century );
		end
		if ( categoryNamePrefix ) then
			return '[[асри ' .. century .. ' то м.]][[Category:' .. categoryNamePrefix .. ' асри ' .. century .. ' то м.]]'
		else
			return '[[асри ' .. century .. ' то м.]]'
		end
	else
		local century = math.floor( (time.year - 1) / 100 ) + 1;
        local infix = ' ';
        if (century == 2) then infix = ' ' end;
		if ( moduleRoman ) then
			century = moduleRoman.toRomanNumber( century );
		end
		if ( categoryNamePrefix ) then
			return '[[асри ' .. century .. ']][[Category:' .. categoryNamePrefix .. ' асри ' .. century .. ']]'
		else
			return '[[асри ' .. century .. ']]'
		end
	end
end

local function formatMillenium( time, categoryNamePrefix )
	if ( time.year < 0 ) then
		local millenium = math.floor( (math.abs( time.year) - 1) / 1000 ) + 1;
		if ( categoryNamePrefix ) then
			if ( millenium == 2 ) then
				return '[[ҳазорсолаи ' .. millenium .. ' то м.]][[Category:' .. categoryNamePrefix .. ' ҳазорсолаи ' .. millenium .. ' то м.]]'
			else	
				return '[[ҳазорсолаи ' .. millenium .. ' то м.]][[Category:' .. categoryNamePrefix .. ' ҳазорсолаи ' .. millenium .. ' то м.]]'
			end	
		else
			return '[[ҳазорсолаи ' .. millenium .. ' то м.]]'
		end
	else
		local millenium = math.floor( (time.year - 1) / 1000 ) + 1;
		if ( categoryNamePrefix ) then
			if ( millenium == 2 ) then
				return '[[ҳазорсолаи ' .. millenium .. ']][[Category:' .. categoryNamePrefix .. ' ҳазорсолаи ' .. millenium .. ']]'
			else
				return '[[ҳазорсолаи ' .. millenium .. ']][[Category:' .. categoryNamePrefix .. ' ҳазорсолаи ' .. millenium .. ']]'
			end
		else
			return '[[ҳазорсолаи ' .. millenium .. ']]'
		end
	end
end

local function formatDateImpl( value, options, microformatClass, categoryPrefix, leftBracket, rightBracket )
	if ( not value ) then error( 'value not specified'); end;
	if ( not options ) then error( 'options not specified'); end;

	-- The calendar model used for saving the data is always the proleptic Gregorian calendar according to ISO 8601.
	local timeISO8601 = string.gsub( string.gsub( tostring( value.time ), '-00%-', '-01-' ), '-00T', '-01T' )
	local unixtime = parseISO8601( timeISO8601 )
	
	if not unixtime then
		return ''
	end

	local structure = os.date("*t", unixtime)
	local precision = tonumber( value.precision )
	
	if precision <= 6 then
		return formatMillenium(structure, categoryPrefix)
	end	
	if precision == 7 then
		return formatCentury(structure, categoryPrefix)
	end

	if precision == 8 then
		return formatDecade(structure, categoryPrefix)
	end

	if precision == 9 then
		local tCopy = deepcopy( structure )
		tCopy.day = nil
		tCopy.month = nil
		return moduleDates.formatWikiImpl(tCopy, tCopy, infoclass, categoryPrefix, leftBracket, rightBracket )
	end

	-- year and month only
	if precision == 10 then
		local tCopy = deepcopy( structure )
		tCopy.day = nil
		return moduleDates.formatWikiImpl(tCopy, tCopy, infoclass, categoryPrefix, leftBracket, rightBracket )
	end

	local calendarmodel = 'gregorian';
	if (mw.ustring.find(value.calendarmodel, 'Q1985786', 1, true)) then
		calendarmodel = 'julian';
	end

	if (calendarmodel == 'gregorian') then
    	return moduleDates.formatWikiImpl( structure, structure, microformatClass, categoryPrefix, leftBracket, rightBracket )
    else
		return p.formatAsJulian( unixtime, infoclass, categoryPrefix, leftBracket, rightBracket )
	end
end

local function formatApproximateDateClaim( context, options, statement )
	if ( not context ) then error( 'context not specified'); end;
	if ( not options ) then error( 'options not specified'); end;
	if ( not options.entity ) then error( 'options.entity is missing'); end;
	if ( not statement ) then error( 'statement not specified'); end;
	if options.nocat then categoryUnknownBirthDate = "" end
	
	local qNotSoonerThan = getQualifierWithDataValue( statement, 'P1319' );
	local qNotLaterThan = getQualifierWithDataValue( statement, 'P1326' );
	if ( qNotSoonerThan or qNotLaterThan ) then
		local results = {};
		if ( qNotSoonerThan ) then
			local formattedDate = formatDateImpl( qNotSoonerThan.datavalue.value, {}, nil, nil, options.leftBracket, options.rightBracket )
			local value = 'на&nbsp;пештар&nbsp;аз&nbsp;' .. context.wrapSnak( formattedDate, qNotSoonerThan.hash )
			table.insert( results, context.wrapQualifier( value, 'P1319' ) );
		end
		if ( qNotLaterThan ) then
			local formattedDate = formatDateImpl( qNotLaterThan.datavalue.value, {}, nil, nil, options.leftBracket, options.rightBracket )
			local value = 'на&nbsp;дертар&nbsp;аз&nbsp;' .. context.wrapSnak( formattedDate, qNotLaterThan.hash )
			table.insert( results, context.wrapQualifier( value, 'P1326' ) );
		end
	    return mw.text.listToText( results, ' и ' , ' и ' ) .. categoryUnknownBirthDate;
	end
	
	return nil
end

function p.formatDateOfBirthClaim( context, options, statement )
	local value = formatApproximateDateClaim( context, options, statement )
	if value then
		return value
	end

    options['conjunction'] = '&#32;ё&#32;';
    options['value-module'] = 'Wikidata/date';
    options['value-function'] = 'formatBirthDate';
    options.i18n.somevalue = '\'\'номаълум\'\'' .. categoryUnknownBirthDate;
	local result = context.formatStatementDefault( context, options, statement );

	local bTable = { parseClaim( statement ) };
	local dTable = parseProperty ( context, options, 'P570' )

	if ( bTable and not dTable ) then
		local age = ageCurrent( bTable )
		if ( age ) then
			if ( options.suppressAge == nil or options.suppressAge == '' ) then
				result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'tg' ):plural( age, 'сол', 'сол', 'сола') .. ')</span>'
			end
			if ( not options.nocat ) then
				if ( age > 115 ) then
					result = result .. categoryBigCurrentAge;
				else
					result = result .. categoryBiographiesOfLivingPersons;
				end
			end
		end
	end

	return result;
end

function p.formatDateOfDeathClaim( context, options, statement )
	local value = formatApproximateDateClaim( context, options, statement )
	if value then
		return value
	end

    options['conjunction'] = '&#32;ё&#32;';
    options['value-module'] = 'Wikidata/date';
    options['value-function'] = 'formatDeathDate';
    options.i18n.somevalue = '\'\'номаълум\'\'' .. categoryUnknownDeathDate;
	local result = context.formatStatementDefault( context, options, statement );

	local bTable = parseProperty ( context, options, 'P569' )
	local dTable = { parseClaim( statement ) };

	if ( bTable and dTable ) then
		local age = age( bTable, dTable )
		if ( age ) then
			if ( options.suppressAge == nil or options.suppressAge == '' ) then
				result = result .. ' <span style="white-space:nowrap;">(' .. age .. ' ' .. mw.language.new( 'tg' ):plural( age, 'сол', 'сол', 'сола') .. ')</span>'
			end
		end
		
		-- returns category to recently deceased persons
		local unixAvailable, unixDateOfDeath = pcall(function()
			local r = os.time(dTable[1].structure)
			if ( r ~= os.time() ) then
				return r
			end
			error()
		end)
		if ( unixAvailable and os.time() - unixDateOfDeath < 31536000 and not options.nocat ) then
			result = result .. categoryRecentlyDeceased
		end
	end

	return result;
end

-- Reentry point for Wikidata Snak formatting
function p.formatBirthDate( context, options, value )
	if ( not context ) then error( 'context not specified'); end;
	if ( not options ) then error( 'options not specified'); end;
	if ( not value ) then error( 'value not specified'); end;
	
	local microformatClass = nil
	if options.microformat ~= '-' then
		microformatClass = options.microformat or 'bday'
	end
	if ( options.nocat ) then
		return formatDateImpl( value, options, microformatClass, nil, options.leftBracket, options.rightBracket )
	else
		return formatDateImpl( value, options, microformatClass, 'Зодагони', options.leftBracket, options.rightBracket )
	end
end

-- Reentry point for Wikidata Snak formatting
function p.formatDeathDate( context, options, value )
	if ( not context ) then error( 'context not specified'); end;
	if ( not options ) then error( 'options not specified'); end;
	if ( not value ) then error( 'value not specified'); end;

	local microformatClass = nil
	if options.microformat ~= '-' then
		microformatClass = options.microformat or 'dday'
	end
	if ( options.nocat and options.nocat ~= '' ) then
		return formatDateImpl( value, options, microformatClass, nil, options.leftBracket, options.rightBracket )
	else
		return formatDateImpl( value, options, microformatClass, 'Даргузаштагони', options.leftBracket, options.rightBracket )
	end
end

-- Reentry point for Wikidata Snak formatting -- default one
function p.formatDate( context, options, value )
	if ( not context ) then error( 'context not specified'); end;
	if ( not options ) then error( 'options not specified'); end;
	if ( not value ) then error( 'value not specified'); end;

	local microformatClass = options.microformat or nil;
	if ( options.nocat and options.nocat ~= '' ) then
		return formatDateImpl( value, options, microformatClass, nil, options.leftBracket, options.rightBracket )
	else
		local categoryPrefix = options.categoryPrefix or nil;
		return formatDateImpl( value, options, microformatClass, categoryPrefix, options.leftBracket, options.rightBracket )
	end
end

function p.formatDateIntervalProperty( context, options )
	if ( not context ) then error( 'context not specified' ); end;
	if ( not options ) then error( 'options not specified' ); end;
	if ( not options.entity ) then error( 'options.entity missing' ); end;

	-- Получение нужных утверждений
	local WDS = require( 'Module:WikidataSelectors' );
	local fromProperty = options.property
	if options.from and options.from ~= '' then
		fromProperty = options.from
	end
	local fromClaims = WDS.filter( options.entity.claims, fromProperty );
	local toClaims = WDS.filter( options.entity.claims, options.to );
	local withinClaims = WDS.filter( options.entity.claims, options.within );

    if fromClaims == nil and toClaims == nil then
        return ''
    end

    local formattedFromClaims = {}
    if fromClaims then
	    for i, claim in ipairs( fromClaims ) do
	        local formattedStatement = context.formatStatement( options, claim )
	        if formattedStatement then
	            formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( options.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>'
	            table.insert( formattedFromClaims, formattedStatement )
	        end
	    end
    end

    local formattedToClaims = {}
    local toOptions = deepcopy( options )
    toOptions.property = options.to
    toOptions.novalue = nowLabel
    if toClaims then
	    for i, claim in ipairs( toClaims ) do
	        local formattedStatement = context.formatStatement( toOptions, claim )
	        if formattedStatement then
	            formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( toOptions.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>'
	            table.insert( formattedToClaims, formattedStatement )
	        end
	    end
    end

	local out = ''
    local fromOut = mw.text.listToText( formattedFromClaims, options.separator, options.conjunction )
    local toOut = mw.text.listToText( formattedToClaims, options.separator, options.conjunction )
    
	if fromOut ~= '' or toOut ~= '' then
	    if fromOut ~= '' then
	    	out = fromOut
		else
			out = '?'
		end

		if toOut ~= '' then
			out = out .. ' — ' .. toOut
		else
			if withinClaims ~= nil then
				out = 'аз ' .. out
			else
				out = out .. ' — ' .. nowLabel
			end
		end
	end

    if out ~= '' then
	    if options.before then
	    	out = options.before .. out
		end
	    if options.after then
	    	out = out .. options.after
		end
	end

    return out
end

local lowestBoundary = parseISO8601('-900-02-20T00:00:00Z')
local mainBoundary = parseISO8601('1582-10-05T00:00:00Z')
local lastBoundary = parseISO8601('1918-01-31T00:00:00Z');

local boundaries = {
	-- from (G) till next will be diff(G = J + diff), at current
	{ lowestBoundary,                       -9 },
	{ parseISO8601('-700-02-29T00:00:00Z'), -8 },
	{ parseISO8601('-600-02-29T00:00:00Z'), -7 },
	{ parseISO8601('-500-02-29T00:00:00Z'), -6 },
	{ parseISO8601('-300-02-29T00:00:00Z'), -5 },
	{ parseISO8601('-200-02-29T00:00:00Z'), -4 },
	{ parseISO8601('-100-02-29T00:00:00Z'), -3 },
	{ parseISO8601('0000-00-00T00:00:00Z'), -2 },
	{ parseISO8601('0100-02-29T00:00:00Z'), -1 },
	{ parseISO8601('0200-02-29T00:00:00Z'),  0 },
	{ parseISO8601('0300-02-29T00:00:00Z'),  1 },
	{ parseISO8601('0500-02-29T00:00:00Z'),  2 },
	{ parseISO8601('0600-02-29T00:00:00Z'),  3 },
	{ parseISO8601('0700-02-29T00:00:00Z'),  4 },
	{ parseISO8601('0900-02-29T00:00:00Z'),  5 },
	{ parseISO8601('1000-02-29T00:00:00Z'),  6 },
	{ parseISO8601('1100-02-29T00:00:00Z'),  7 },
	{ parseISO8601('1300-02-29T00:00:00Z'),  8 },
	{ parseISO8601('1400-02-29T00:00:00Z'),  9 },
	{ parseISO8601('1500-02-29T00:00:00Z'), 10 },
	{ parseISO8601('1700-02-29T00:00:00Z'), 11 },
	{ parseISO8601('1800-02-29T00:00:00Z'), 12 },
	{ parseISO8601('1900-02-29T00:00:00Z'), 13 },
	{ lastBoundary, '' },
};

-- Передаваемое время обязано быть по Юлианскому календарю (старому стилю)
function p.formatAsJulian( julTime, infocardClass, categoryNamePrefix, leftBracket, rightBracket )
	if 'table' == type( julTime ) then
		if julTime.args and julTime.args[1] then
			julTime = tonumber( julTime.args[1] )
		else
			return 'unknown argument type: ' .. type( julTime ) .. ': ' .. table.tostring( julTime )
		end
	end

	local t = os.date( "*t", julTime )
	if ( julTime <= lowestBoundary ) then
		return "''таърихи носаҳеҳ (саҳеҳии дастнорас)''";
	end
	if ( julTime >= lastBoundary ) then
		return "''таърихи носаҳеҳ (тақвими юлианӣ пас аз 1918-01-26 ба кор бурда мешавад)''";
	end

    for i=1,#boundaries,1 do
		local b1 = boundaries[i][1];
		local b2 = boundaries[i + 1][1];
		if ( b1 <= julTime and julTime < b2 ) then
			local b1s = os.date( "*t", b1 )
			if ( b1s.year == t.year and b1s.month == t.month and b1s.day == t.day) then
				if ( julTime < mainBoundary ) then
					-- only julian
					return moduleDates.formatWikiImpl( {year=t.year, month=2, day=29}, {year=t.year, month=2, day=29}, infocardClass, categoryNamePrefix, leftBracket, rightBracket );
				else
					--julian and grigorian
					return moduleDates.formatWikiImpl( {year=t.year, month=2, day=29}, t, infocardClass, categoryNamePrefix, leftBracket, rightBracket )
				end
			else
				local gregTime = os.date( "*t", julTime + boundaries[i][2] * 24 * 60 * 60 );
				if ( julTime < mainBoundary ) then
					-- only julian
					return moduleDates.formatWikiImpl( t, t, infocardClass, categoryNamePrefix, leftBracket, rightBracket );
				else
					--julian and grigorian
					return moduleDates.formatWikiImpl( t, gregTime, infocardClass, categoryNamePrefix, leftBracket, rightBracket )
				end
			end
		end
	end

    if ( julTime >= lastBoundary ) then
		return "''хато дар модули Модул:Wikidata/date (тағйири табдили тақвим ёфт нашуд)''";
	end
end

return p