Documentation for this module may be created at Modul:Infocards/doc

local fun = {}

local Utilities = require 'Module:Utilities'
local Ordinal = require 'Module:Ordinal'
local dataLinks
local success, result = pcall ( mw.loadData, 'Module:Date/Data' )
if success then
	dataLinks = result
else
	dataLinks = { [''] = { month = { none = 1000, all = { 1773, 2014 } }, } }
end

local trim = Utilities.trim
local kym = 'Category only in articles'
local birthsCat = ' zandemonon'
local deathsCat = ' mardon'
local inYearCat = ' sorədə'

local function ucfirst( str )
	return str:sub( 1, 1 ):upper() .. str:sub( 2 )
end

local listMonths = {
	{ num = 1,  nDay = 31, abrev = 'yan.',  name = 'Janvar', alias = {'janvar', 'jan.', 'janv.', 'jan', 'janv', 'january' } },
	{ num = 2,  nDay = 29, abrev = 'fev.',   name = 'Fevral', alias = {'fevral', 'fevral', 'fev.', 'fev', 'february', 'feb', 'feb.' } },
	{ num = 3,  nDay = 31, abrev = 'mart',   name = 'Mart', alias = {'mart', 'mar.', 'mar', 'march' } },
	{ num = 4,  nDay = 30, abrev = 'apr.',   name = 'Aprel', alias = {'aprel', 'avr.', 'avr', 'apr', 'april'} },
	{ num = 5,  nDay = 31, abrev = 'may',    name = 'Maj', alias = {'maj', 'maj' } },
	{ num = 6,  nDay = 30, abrev = 'iyun',   name = 'Ijun', alias = {'ijun', 'jun', 'june' } },
	{ num = 7,  nDay = 31, abrev = 'iyul', name = 'Ijul', alias = {'ijul', 'ijul', 'ijl', 'ijl.', 'july' } },
	{ num = 8,  nDay = 31, abrev = 'avq.',   name = 'Avgust', alias = {'avgust', 'avg.', 'avg', 'avgust' } },
	{ num = 9,  nDay = 30, abrev = 'sen.',  name = 'Sentjabr', alias = {'sentjabr', 'sent.', 'sent', 'sen', 'sent', 'sep.', 'sep', 'september' } },
	{ num = 10, nDay = 31, abrev = 'okt.',   name = 'Oktjabr', alias = {'oktjabr', 'okt.', 'okt', 'oct.', 'oct', 'october' } },
	{ num = 11, nDay = 30, abrev = 'noy.',   name = 'Nojabr', alias = {'nojabr', 'noj.', 'noj', 'november' } },
	{ num = 12, nDay = 31, abrev = 'dek.',   name = 'Dekabr', alias = {'dekabr', 'dekabr', 'dek.', 'dec.', 'dek', 'dec', 'december' } },
	avqust = { num = 8, nDay = 31, abrev = 'avq.', name = 'Avgust', alias = {'avgust', 'avg', 'avgust', 'aug.', 'aug', 'august' } },
}

for i = 1, 12 do
	local month = listMonths[i]
	listMonths[tostring( i )] = month
	if i < 10 then
		listMonths['0' .. i] = month
	end
	listMonths[month.name] = month
	listMonths[month.abrev] = month
	for _, n in ipairs( month.alias ) do
		listMonths[n] = month
	end
end
for _, n in ipairs( listMonths.avqust.alias ) do
	listMonths[n] = listMonths.avqust
end

local list_seasons = {
	{ 'yaz', 'spring', },
	{ 'yay', 'summer', },
	{ 'payız', 'autumn', },
	{ 'qış', 'winter', },
}

function fun.determinationSeason( season )
	local s = trim( season )
	if s then
		s = mw.ustring.lower( s )
		for i = 1, 4 do
			for _, n in ipairs( list_seasons[i] ) do
				if s == n then
					return list_seasons[i][1]
				end
			end
		end
	end
end

function fun.determineMonth( month )
	local result

	local num = tonumber( month )
	if num then
		result = listMonths[num]
	else
		local str = trim( month )
		if str then
			result = listMonths[str]
			if not result then
				result = listMonths[mw.ustring.lower( str )]
			end
		end
	end

	if result then
		return result.name, result.num
	else
		return nil, nil
	end
end


local function existDate( dataQualifier, year, month )
	local data
	if month then
		data = dataQualifier.month
	else
		data = dataQualifier.year
	end
	if type( data ) ~= 'table' then
		return
	end
	local link = year
	if dataQualifier.qualifier then
		link = link .. ' ' .. dataQualifier.qualifier
	end
	local single = year
	if month then
		link = month .. ' ' .. link
		single = ucfirst( month ) .. ' ' .. year
	end
	local none = tonumber( data.none )
	if none and year <= none then
		if type( data.single ) == 'table' then
			for i, v in ipairs( data.single ) do
				if single == v or single == tonumber( v ) then
					return link
				end
			end
		end
		return nil
	elseif type( data.all ) == 'table' then
		local all1, all2 = tonumber( data.all[1] ), tonumber( data.all[2] )
		if all1 and all2 and year >= all1 and year <= all2 then
			-- l'year est dans la partie 'all' donc on retourne le link
			return link
		end
	end
	local targetLink = mw.title.new( link )
	if targetLink and targetLink.exists then
		return link
	end
end

---
function fun.cleanDay( day )
	if type( day ) == 'string' then
		local nameDay = { '[Ll]undi', '[Mm]ardi', '[Mm]ercredi', '[Jj]eudi', '[Vv]endredi',
			'[Ss]amedi', '[Dd]imanche', '^ *[Ll]e' }
		local first = { '<abbr class="abbr ?" title="[Pp]remier" ?>1<sup>er</sup></abbr>', '1', '1' }
		for i, v in ipairs( nameDay ) do
			day = day:gsub( v, '' )
		end
		for i, v in ipairs( first ) do
			day = day:gsub( v, '1' )
		end
		day = trim( day )
	end
	return day
end

function fun.separationDayMonthYear( date )
	date = trim( date )
	if date then
		local function ifError( period, value )
			return false, '<span class="error"> yanlış ' .. period .. ' (' .. value .. ')</span>'
		end
		local day, month, year, hideMonth, hideYear, separateur
		local d = '([0-3]?%d)'                            -- day
		local m = '([01]?%d)'                             -- numeric month
		local mmm = '([^%s%p%d]+[.]?)'                    -- full month names
		local mmm2 = '([^%s%p%d]+[.]?[-/][^%s%p%d]+[.]?)' -- month-month en toute lettre
		local yd = '(%-?%d+)'                             -- year or day
		local s = '[ ./-]+'                               -- simple separator
		local sep = '([ ./-]+)'                           -- separator with capture, to detect it twice
		local minus = '(%-?)'                             -- minus sign to indicate that this data should not be displayed

		date = fun.cleanDay( date )
		date = mw.ustring.gsub( date, '%[%[[Kk]ateqoriya?:.-%]%]', '' )
		date = date	:gsub( '%b<>', '' )
					:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end )
					-- nbsp
					:gsub( '\194\160', ' ' )
					:gsub( '&nbsp;', ' ' )
					:gsub( '&#160;', ' ' )
					-- narrow nbsp
					:gsub( '\226\128\175', ' ' )
					:gsub( '&#8239;', ' ' )
					-- thin space
					:gsub( '\226\128\137', ' ' )
					:gsub( '&thinsp;', ' ' )
					:gsub( '&#8201;', ' ' )
					-- simple space
					:gsub( '&#32;', ' ' )
					-- plusieurs espaces
					:gsub( ' +', ' ' )
					:gsub( '(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?', '-%1' )
					:gsub( '^+?([%d-]*%d%d%-%d%d)T%d%d[%d:,.+-]*Z?$' , '%1')

		-- test year single
		if date:match( '^'..yd..'$' ) then
			year = date:match( '^'..yd..'$' )
		elseif date:match( '^'..yd..s..yd..minus..'$' ) then
			-- dd/mm, mm/yyyy or yyyy/mm
			local a, separateur, b, sb = date:match( '^'..yd..sep..yd..minus..'$' )
			a, b = tonumber( a ), tonumber( b )
			if separateur:match( '^.+%-$' ) then
				-- probablement mm/-yyyy, year av.JC
				b = 0 - b
			end
			if  a > 12 and ( b < 1 or b > 31 ) or
				b > 12 and ( a < 1 or a > 31 ) then
				return ifError( 'Date', date )
			elseif b < 1 or b > 31 then
				month, year, hideYear = a, b, sb
			elseif a < 1 or a > 31 then
				year, month = a, b
			elseif b > 12 then
				return ifError( 'Month', b )
			else
				day, month, hideMonth = a, b, sb
			end
		elseif date:match( '^'..yd..sep..m..minus..'%2'..yd..minus..'$' ) then
			-- dd/mm/yyyy or yyyy/mm/dd
			day, separateur, month, hideMonth, year, hideYear = date:match( '^'..yd..sep..m..minus..'%2'..yd..minus..'$' )
			if separateur == '-' and hideMonth == '-' and hideYear == '' and tonumber( year ) > 0 then
				-- date in format dd-mm--yyyy type 17-06--44 for 17 june 44 BC
				hideMonth = nil
				year = 0 - year
			end
		elseif date:match( '^'..d..sep..mmm..minus..'%2'..yd..minus..'$' ) then
			-- dd mmm yyyy
			day, separateur, month, hideMonth, year, hideYear = date:match( '^'..d..sep..mmm..minus..'%2'..yd..minus..'$' )
		elseif date:match( '^'..mmm..s..yd..minus..'$' ) then
			-- mmm yyyy
			month, separateur, year, hideYear = date:match( '^'..mmm..sep..yd..minus..'$' )
			if separateur:match( '^.+%-$' ) then
				year = '-' .. year
			end
		elseif date:match( '^'..mmm2..s..yd..minus..'$' ) then
			-- mmm-mmm yyyy
			month, separateur, year, hideYear = date:match( '^'..mmm2..sep..yd..minus..'$' )
			if separateur:match( '^.+%-$' ) then
				year = '-' .. year
			end
		elseif date:match( '^'..d..s..mmm..minus..'$' ) then
			-- dd mmm
			day, month, hideMonth = date:match( '^'..d..s..mmm..minus..'$' )
		elseif date:match( '^'..mmm..s..d..', ?'..yd..'$') then
			-- mmm dd, yyyy (format anglo-saxon)
			month, day, year = date:match( '^'..mmm..s..d..', ?'..yd..'$')
		elseif date:match( '^'..mmm..'$' ) then
			month = date
		else
			return ifError( 'Date', date )
		end
		local jn, an = tonumber( day ), tonumber( year )
		if jn and an and ( jn > 31 or jn < 0 or #day >= 3 ) and an <= 31 then
			local temp = year
			year = day
			day = temp
		end

		return fun.validationDayMonthYear{
			day, month, year,
			hideYear = trim( hideYear ) and true or nil,
			hideMonth = ( trim( hideYear ) or not year ) and trim( hideMonth ) and true or nil,
		}
	else
		return true, {}
	end
end

function fun.validationDayMonthYear( frame )
	local args = Utilities.extractArgs( frame )
	local day, month, numMonth, year
	local bday = args[1] or args['gün'] or ''
	local bmonth = tostring( args[2] or args['ay'] or '' )
	local byear = args[3] or args['year'] or args['il'] or ''

	local function ifError( period, value )
		return false, '<span class="error"> yanlış ' .. period .. ' (' .. value .. ')</span>'
	end

	if Utilities.notEmpty( byear ) then
		year = tonumber( byear )
		if year == nil and type( byear ) == 'string' then
			year = byear:upper():match( '^(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?' )
			year = tonumber( year )
			if year then
				year = 0 - year
			else
				return ifError( 'Année', byear )
			end
		elseif year == 0 then
			return ifError( 'Année', 0 )
		end
	else
		year = nil
	end

	if Utilities.notEmpty( bmonth ) then
		month, numMonth = fun.determineMonth( bmonth )
		if month == nil then
			month = fun.determinationSeason( bmonth )
			if month == nil then
				local month1, sep, month2 = bmonth:match( '^([^%s%p%d]+[.]?)([-/])([^%s%p%d]+[.]?)$' )
				if month1 then
					month1 = fun.determineMonth( month1 )
					month2 = fun.determineMonth( month2 )
					if month1 == nil or month2 == nil then
						return ifError( 'Month', bmonth )
					end
					month = month1 .. sep .. month2
				else
					return ifError( 'Month', bmonth )
				end
			end
		end
		if Utilities.notEmpty( bday ) then
			if not numMonth then
				ifError( 'Date', 'day with season or plusieurs month' )
			end
			day = tonumber( bday )
			if day == nil then
				day = tonumber( fun.cleanDay( bday ) )
			end
			if day == nil then
				return ifError( 'Gün', bday )
			end
			if day < 1 or day > 31 then
				return ifError( 'Gün', bday )
			elseif day > listMonths[numMonth].nDay then
				return ifError( 'Gün', bday .. ' ' .. month )
			elseif day == 29 and numMonth == 2 and year and ( math.fmod( year, 4 ) ~= 0 ) then
				return ifError( 'Gün', '29 fevrál ' .. year )
			end
		else
			if bmonth:match( '^%u' ) then
				month = ucfirst( month )
			end
		end
	else
		if Utilities.notEmpty( bday ) then
			if year then
				return ifError( 'Month', 'absent' )
			else
				bday = fun.cleanDay( bday )
				day = tonumber( bday )
				if day then
					if day > 31 or day < 1 then
						year = day
						day = nil
					else
						return ifError( 'Tarix', 'təkcə gün : ' .. bday )
					end
				else
					return ifError( 'Gün', bday )
				end
			end
		end
	end

	if year and year < 13 and year > 0 and not day and ( tonumber( bmonth ) or ( not month and tonumber( args[4] ) ) ) then
		return false, '<span class="error">yanlış il (' .. year .. ')</span>'
	end

	local result = {
		day = day,
		month = month,
		numMonth = numMonth,
		year = year,
		hideYear = args.hideYear,
		hideMonth = args.hideMonth,
	}
	return true, result
end

function fun.modelDate( frame )
	local args = Utilities.extractArgs( frame )
	local cat, result = ''

	local dateBirthDeath

	local test, params
	local arg1, arg2, arg3 = fun.cleanDay( args[1] ), trim( args[2] ), trim( args[3] )
	if type( arg1 ) == 'string' and arg3 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or arg2 == nil or dataLinks[arg2] or mw.ustring.match( arg2, '%a %a' ) ) then
		test, params = fun.separationDayMonthYear( arg1 )
		if test then
			dateBirthDeath = trim( arg2 )
			params.qualifier = trim( arg2 )
		end
	elseif type( arg1 ) == 'string' and type( arg2 ) == 'string' and arg3 ~= nil and arg4 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or dataLinks[arg3] or mw.ustring.match( arg3, '%a %a' ) ) then
		test, params = fun.separationDayMonthYear( arg1 )
		if test then
			dateBirthDeath = trim( arg2 )
			params.qualifier = trim( arg3 )
		end
	else
		local function hideParam( p )
			if type( p ) ~= 'string' then
				return p, nil
			end
			local value, mask = p:match( '^%s*(.-)(%-?)%s*$' )
			return value, ( mask == '-' or nil )
		end
		local cleanArgs = { arg1 or args.day }
		cleanArgs[2], cleanArgs.hideMonth = hideParam( args[2] or args.month )
		cleanArgs[3], cleanArgs.hideYear = hideParam( args[3] or args.year or args['year'] )

		test, params = fun.validationDayMonthYear( cleanArgs )
		if test then
			params.qualifier = trim( args[4] )
		end
	end

	if test then
		local Yesno = require 'Module:Yesno'

		if args.qualifier and args.qualifier ~= '' then
			params.qualifier = args.qualifier
		end

		params.julian = Yesno( args.julian, 'court', false )
		params.BC = Yesno( args.BC )

		if args['republican'] and args['republican'] ~= '' then
			if args['republican'] == 'Links' then
				params.republican = 'Links'
			else
				params.republican = Yesno( args['republican'], false )
			end
		else
			params.republican = false
		end

		if args.dateBirthDeath and args.dateBirthDeath ~= '' then
			dateBirthDeath = args.dateBirthDeath
		end

		if dateBirthDeath then
			local testBirthDeath, paramsBirthDeath = fun.separationDayMonthYear( dateBirthDeath )
			if testBirthDeath then
				params.yearBirthDeath, params.monthBirthDeath, params.numMonthBirthDeath, params.dayBirthDeath = paramsBirthDeath.year, paramsBirthDeath.month, paramsBirthDeath.numMonth, paramsBirthDeath.day
			end
		end

		local listeParam = {
			age = 'sin',
			['sin'] = 'sin',
			birth = 'birth',
			death = 'death',
			['ölüm'] = 'death',
			AD = '',
			nolinks = 'nolinks',
			compact = 'compact',
		}
		for n, v in pairs( listeParam ) do
			params[v] = params[v] or Yesno( args[n], true, false ) or nil
		end

		if args.debug then
			return params
		end

		result = fun._modelDate( params )

	elseif args.onerror then
		if args.onerror == 'input' then
			return args[1]
		else
			return args.onerror
		end

	else
		local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
		if namespaceCategorisation[mw.title.getCurrentTitle().namespace] and not Utilities.notEmpty( args.nocat ) then
			cat = frame:expandTemplate{ title = kym, args = {'Yanlış tarix formatı daxil edilmiş məqalələr'}}
		end
		result = params .. cat
	end

	return result or ''
end

function fun._modelDate( args )
	local year, month, numMonth, day = args.year, args.month, args.numMonth, args.day
	local qualifier = args.qualifier

	if ( year or month or day ) == nil then
		return
	end

	local agePrefix = ''
	local age = args['sin'] and fun.age( year, numMonth, day )
	local birth = args.birth
	local death = args.death
	if death and args.yearBirthDeath then
		age = fun.age( args.yearBirthDeath, args.numMonthBirthDeath, args.dayBirthDeath, year, numMonth, day )
		agePrefix = ''
	end

	local gyear, gmonth, gday = year, numMonth, day   
	local jyear, jmonth, jday = year, month, day     
	local julianDate, julianSup, julianSep              
	local gregAprMonth, gregAprAn, gregEnd               
	if year and day then
		local ymd = year * 10000 + numMonth * 100 + day
		if ymd < 15821014 then
			if year > 0 then
				gyear, gmonth, gday = fun.julianToGregorian( year, numMonth, day )
			else
				gyear, gmonth, gday = fun.julianToGregorian( year + 1, numMonth, day )
			end
			args.julian = false

		elseif args.julian then
			gyear, gmonth, gday = fun.julianToGregorian( year, numMonth, day )
			year, month, day = gyear, listMonths[gmonth].name, gday
			if args.compact then
				jmonth = listMonths[jmonth].abrev
			end
			if args.julian == 'court' then
				julianDate = jday .. ' ' .. jmonth .. ' '
				julianSup = '<sup>[[Yuli təqvimi|yul.]]</sup>'
				if jyear == year then
					gregAprMonth = '<sup>[[Qriqori təqvimi|qri.]]</sup>'
				else
					julianDate = julianDate .. jyear .. ' '
					gregAprAn = '<sup>[[Qriqori təqvimi|qri.]]</sup>'
				end
				julianSep = ' / '
			else
				julianDate = jday .. ' ' .. jmonth .. ' ' .. jyear
				julianSep = ' ('
				gregEnd = ' [[qriqori təqvimi]] ilə)'
			end

		elseif args.republican then
			local DateRep = require 'Module:Date republican'
			local RepSansLinks
			if args.republican == 'Links' then
				RepSansLinks = false
			else
				RepSansLinks = true
			end
 			dateRepublic = DateRep._date_republicane(
 				RepSansLinks,
 				{ fun.formatRepCal( fun.do_toRepCal{gyear, gmonth, gday} ) }
 			)
		end
	else
		if year and year < 0 then
			gyear = gyear + 1
		end
		args.julian = false
		args.republican = false
	end

	local wikiList = {}
	local iso = {}
	local textMonth = month
	if args.compact then
		if not numMonth then
		else
			if args.nolinks then
				textMonth = '<abbr class=abbr title="' .. month .. '">' .. listMonths[month].abrev .. '</abbr>'
			else
				textMonth = listMonths[month].abrev
			end
		end
	end
	month = month and month:gsub( 'avqust', 'avqust' )

	local dataQualifier, dataCat
	if not args.nolinks then
		dataQualifier = dataLinks[qualifier or '']
		if type( dataQualifier ) ~= 'table' then
			dataQualifier = { qualifier = ' ' .. qualifier, year = { } }
		end
		dataCat = dataLinks[dataQualifier.cat]
		if type( dataCat ) ~= 'table' or dataCat == dataQualifier then
			dataCat = { qualifier = '' }
		end
	end
	local function wikiLink( link, text )
		if link == text then
			return '[[' .. text .. ']]'
		else
			return '[[' .. link .. '|' .. text .. ']]'
		end
	end

	local qualifDay = ''
	if day then
		local textDay = day
		if args.nolinks then
			table.insert( wikiList, day )
		else
			qualifDay = dataQualifier.day and dataQualifier.qualifier
				or dataCat.day and dataCat.qualifier
				or ''
			local link = day .. ' ' .. month .. ' ' .. qualifDay
			if day == 1 then
				day = '1'
				link = '1 ' .. month .. ' ' .. qualifDay
			end
			table.insert( wikiList, wikiLink( link, day ) )
			table.insert( wikiList, wikiLink( link, day .. ' '.. textMonth ) )
		end
		table.insert( iso, 1, string.sub( '0' .. gday, -2 ) )
	end

	if month then
		if #wikiList == 0 and year == nil then
			return textMonth
		end
		if args.nolinks then
			if not args.hideMonth then
				table.insert( wikiList, textMonth )
			end
		else
			local link
			if year then
				if not numMonth then
				else
					link = existDate( dataQualifier, year, month ) or existDate( dataCat, year, month )
					if link == nil and qualifier and qualifDay == '' then
						link = existDate( dataLinks[''], year, month )
					end
				end
			end
			if link or args.hideMonth or #wikiList > 0 then
				table.remove( wikiList, #wikiList - 1 )
			elseif args.hideYear then
				table.insert( wikiList, textMonth )
			end
		end
		if gmonth then
			table.insert( iso, 1, string.sub( '0' .. gmonth, -2 ) )
		end
		table.insert( wikiList, gregAprMonth )
	end

	if year and not (args.julian == true and args.nolinks and jyear == year ) then
		if not args.hideYear then
			local textYear = year
			local link
			if year < 0 then
				local yearBC = 0 - year
				link = link or ( 'e.ə. ' .. yearBC)
				if args.BC == false then
					textYear = yearBC
				else
					textYear = ' <abbr class="abbr" title="'
						 .. 'eradan əvvəl">e.ə.</abbr> ' .. yearBC
				end
			elseif args.AD then
				textYear = textYear .. ' .. textYear ..'
			end
			if args.nolinks then
				table.insert( wikiList, textYear )
			else
				link = existDate( dataQualifier, year ) or existDate( dataCat, year ) or link or year
				if month and #wikiList == 0 then
					textYear = textMonth .. ' ' .. textYear
				end
				table.insert( wikiList, wikiLink( link, textYear ) )
			end
		end
	end
	if year then
		if gyear > 999 then
			table.insert( iso, 1, gyear )
		elseif gyear > -1 then
			table.insert( iso, 1, string.sub( '000' .. gyear , -4 ) )
		elseif gyear > -999 then
			table.insert( iso, 1, 'U-' .. string.sub( '000' .. ( 0 - gyear ), -4 ) )
		else
			table.insert( iso, 1, 'U' .. gyear )
		end
	end
	table.insert( wikiList, gregAprAn )

	-- l'age
	if type( age ) == 'number' and age >= 0 and ( not birth or age < 120 ) then
		if age == 0 then
			age = '(' .. agePrefix .. 'mənfi bir\194\160sin)'
		elseif age == 1 then
			age = '(' .. agePrefix .. '1\194\160sin)'
		else
			age = '('.. agePrefix .. age .. '\194\160sin)'
		end
	else
		age = false
	end


	local wikiText = table.concat( wikiList, ' ' )
	local isoText = table.concat( iso, '-' )

	local wikiHtml = mw.html.create( '' )

	if julianDate then
		wikiHtml:tag( 'span')
				:addClass( 'nowrap' )
				:attr( 'data-sort-value', isoText )
				:wikitext( julianDate )
				:node( julianSup )
				:done()
			:wikitext( julianSep )
	end

	local dateHtml = wikiHtml:tag( 'time' )
			:wikitext( wikiText )
	if wikiText:match( ' ' ) then
		dateHtml:addClass( 'nowrap' )
	end
	if isoText ~= wikiText then
		dateHtml:attr( 'datetime', isoText )
				:attr( 'data-sort-value', isoText )
	end
	if not args.nolinks then
		dateHtml:addClass( 'date-link' )
	end
	if birth then
		dateHtml:addClass( 'bday' )
	elseif death then
		dateHtml:addClass( 'dday' )
	end

	wikiHtml:wikitext( gregEnd )

	if args.republican then
		wikiHtml:wikitext( ' (', dateRepublic, ')' )
	end

	if age then
		wikiHtml:wikitext( ' ' )
				:tag( 'span' )
					:addClass( 'noprint')
					:wikitext( age )
					:done()
	end

	return tostring( wikiHtml )
end

function fun.dateInfobox( frame )
	local args = frame.args
	if type( args ) ~= 'table' or not ( args[1] and args[2] ) then
		return
	end

	local function analyseDate( d )
		if trim( d ) then
			local analyse = d:match( ' və ya ') or d:match( 'arasında ' ) or d:match( 'doğru ' ) or d:match( 'sonra ' ) or d:match( 'sonra ' )
			if analyse then
				return d
			end
			analyse = d:match( 'datetime="([%d-]+)"' ) or d
			local start, son = analyse:match( '(.-%d%d%d%]*%-?)([\127 ].+)' )
			if not start then
				start, son = analyse:match( '(.-%d%d%d%]*%-?)(<br ?/?>.+)' )
			end
			analyse = start or analyse
			analyse = analyse:gsub(
				'%[%[([^%[%]|]*)|?([^%[%]]*)%]%]',
				function ( l, t )
					return trim( t ) or l
				end
			)
			local t, r = fun.separationDayMonthYear( analyse )
			if t then
				return r, son
			else
				return d, son
			end
		end
	end
	local function prefix( dateString )
		if dateString then
			local datetime = dateString:match( 'datetime="([U%d%-]+)"' )
			if datetime and datetime:match('%-%d%d%-%d%d') and trim( args['prefix'] ) then
				return args['prefix'] .. ' ' .. dateString
			end
			if trim( args['prefix without day'] ) then
				return args['prefix without day'] .. ' ' .. dateString
			end
		end
		return dateString
	end

	local birth = args[1]:match( '^n' ) == 'n'
	local death = args[1]:match( '^m' ) or args[1]:match( 'ölüm' )
	local displayDate, qualifier = args[2], args[4]
	local displayDateTab, resultDate, completeDate
	local dateBirth, dateDeath
	if death then
		displayDate = args[3]
	end
	if not trim( displayDate ) then
		return
	end
	if displayDate:match( '</time>' ) then
		if ( birth or death ) and ( displayDate:match( 'wikidata%-linkback' )) then
			dateBirth = analyseDate( args[2] )
			dateDeath = analyseDate( args[3] )
			resultDate = displayDate
		else
			return prefix( displayDate )
		end
	else
		displayDateTab, completeDate = analyseDate( displayDate )
		if type( displayDateTab ) ~= 'table' then
			return displayDateTab
		else
			if birth then
				dateBirth = displayDateTab
				dateDeath = analyseDate( args[3] )
			elseif death then
				dateBirth = analyseDate( args[2] )
				dateDeath = displayDateTab
			else
				qualifier = args[3]
			end
			displayDateTab.birth = birth
			displayDateTab.death = death
			displayDateTab.qualifier = args.qualifier or qualifier
			displayDateTab.nolinks = args.nolinks
			displayDateTab.nocat = args.nocat
			displayDateTab.julian = args.julian
		end
	end
	resultDate = resultDate or fun.modelDate( displayDateTab )

	local age, prefixAge, suffixAge, calculAge = '', ' <span class="noprint">(', ')</span>', nil
	if birth and
		dateBirth and
		not dateDeath and
		type( dateBirth ) == 'table'
	then
		calculAge = fun.age( dateBirth.year, dateBirth.numMonth, dateBirth.day )
		if calculAge  then
			if calculAge >= 125 then 
				resultDate = resultDate
			else
				resultDate = resultDate .. frame:expandTemplate{ title = kym, args = {'əjion insonon'}}
			end
		end
			if calculAge and calculAge >= 90 and calculAge < 125 then
			resultDate = resultDate .. frame:expandTemplate{ title = kym, args = {'əjion dırozəjinon'}}
		end
		if calculAge and calculAge > 125 then
			calculAge = nil
		end
	elseif death and
		dateBirth and
		dateDeath and
		type( dateBirth ) == 'table'
		and type( dateDeath ) == 'table'
	then
		calculAge = fun.age(
			dateBirth.year,
			dateBirth.numMonth,
			dateBirth.day,
			dateDeath.year,
			dateDeath.numMonth,
			dateDeath.day
		)
		prefixAge = ' ('
		suffixAge = 'ədə)'
	end
	
	if tonumber( calculAge ) then
		if calculAge > 0 then
			age = prefixAge .. calculAge .. '\194\160sin' .. suffixAge
		elseif calculAge == 0 then
			age = prefixAge .. 'birdən az\194\160sin' .. suffixAge
		end
		if completeDate and completeDate:match( 'sin?%)' ) then
			completeDate = ''
		end
			if age:match( 'ədə' ) then
			age = age .. frame:expandTemplate{ title = kym, args = {calculAge .. ' sinədə' .. deathsCat}}
			if calculAge >= 90 and calculAge < 100 then
				age = age .. frame:expandTemplate{ title = kym, args = {'Uzunömürlülər'}}
			end
		end
			if age:match( 'sin' ) then
				if calculAge >= 100 then
					age = age .. frame:expandTemplate{ title = kym, args = {'100 il və daha artıq yaşayanlar'}}
			end
		end
	end
	
	--Categories. Not the best syntax but gets the job done
		local catBirth
		local catDeath
		--birth
		if birth then 
			if dateBirth.year and not dateBirth.day and not dateBirth.month then -- 1959
				catBirth = frame:expandTemplate{ title = kym, args = {inYear(dateBirth.year) .. inYearCat .. birthsCat}}
			elseif dateBirth.day and dateBirth.month and not dateBirth.year then -- 5 may
				catBirth =  frame:expandTemplate{ title = kym, args = {dateBirth.day .. ' ' .. checkApril(dateBirth.numMonth, dateBirth.month) .. birthsCat}}
			elseif dateBirth.month and not dateBirth.day and not dateBirth.year then -- may
				catBirth =  frame:expandTemplate{ title = kym, args = {checkApril(dateBirth.numMonth, dateBirth.month) .. birthsCat}}
			elseif dateBirth.month and dateBirth.year and not dateBirth.day then -- may 1959
				catBirth = frame:expandTemplate{ title = kym, args = {checkApril(dateBirth.numMonth, dateBirth.month) .. birthsCat}} .. frame:expandTemplate{ title = kym, args = {inYear(dateBirth.year) .. inYearCat .. birthsCat}}
			elseif dateBirth.day and dateBirth.month and dateBirth.year then -- 5 may 1959
				catBirth = frame:expandTemplate{ title = kym, args = {dateBirth.day .. ' ' .. checkApril(dateBirth.numMonth, dateBirth.month) .. birthsCat}}  .. frame:expandTemplate{ title = kym, args = {inYear(dateBirth.year) .. inYearCat .. birthsCat}}
			end
		else
				catBirth = ''
		end
		--death
		if death then 
			if dateDeath.year and not dateDeath.day and not dateDeath.month then -- 1959
				catDeath = frame:expandTemplate{ title = kym, args = {inYear(dateDeath.year) .. inYearCat .. deathsCat}}
			elseif dateDeath.day and dateDeath.month and not dateDeath.year then -- 5 may
				catDeath =  frame:expandTemplate{ title = kym, args = {dateDeath.day .. ' ' .. checkApril(dateDeath.numMonth, dateDeath.month) .. deathsCat}}
			elseif dateDeath.month and not dateDeath.day and not dateDeath.year then -- may
				catDeath =  frame:expandTemplate{ title = kym, args = {checkApril(dateDeath.numMonth, dateDeath.month) .. deathsCat}}
			elseif dateDeath.month and dateDeath.year and not dateDeath.day then -- may 1959
				catDeath = frame:expandTemplate{ title = kym, args = {checkApril(dateDeath.numMonth, dateDeath.month) .. deathsCat}} .. frame:expandTemplate{ title = kym, args = {inYear(dateDeath.year) .. inYearCat .. deathsCat}}
			elseif dateDeath.day and dateDeath.month and dateDeath.year then -- 5 may 1959
				catDeath = frame:expandTemplate{ title = kym, args = {dateDeath.day .. ' ' .. checkApril(dateDeath.numMonth, dateDeath.month) .. deathsCat}}  .. frame:expandTemplate{ title = kym, args = {inYear(dateDeath.year) .. inYearCat .. deathsCat}}
			end
		else
				catDeath = ''
		end
		return prefix( resultDate ) .. ( completeDate or '' ) .. age .. catBirth .. catDeath
end

function fun.dateISO( frame )
	local args = Utilities.extractArgs( frame )
	local year = Utilities.notEmpty( args['il'], args.year, args.year, args.date )
	if type( year ) == 'string' then
		year = ( tonumber( year )	-- match '2013'
				or string.match ( year, '%D(%d%d%d%d)%D' ) -- match '[[2013-cü ildə musiqi|2013]]'
				or string.match ( year, '%D(%d%d%d%d)$' )  -- match '17 sentyabr 2013'
				or string.match ( year, '^(%d%d%d%d)%D' )  -- match '2013-09-17'
		)
	end

	year = tonumber( year )

	if year and year > 1582 then
		local month = Utilities.notEmpty( args.month, args.month )
		local nameMonth, numMonth = fun.determineMonth( month )
		if numMonth then
			month = '-' .. string.sub( '0' .. numMonth, -2 )

			local day = Utilities.notEmpty( args.day, args.day, args['quantième'] )
			if type( day ) == 'string' then
				day = tonumber( day ) or tonumber( string.match ( day, '%d+') )
			end
			day = tonumber( day )
			if day and day <= listMonths[numMonth].nDay then
				day = '-' .. string.sub( '0' .. day, -2 )
				return year .. month .. day
			else
				return year .. month
			end
		else
			return tostring( year )
		end
	end
end

function fun.do_dayRank(arguments)
	local yr = tonumber(arguments.year or arguments[1]) or 1
	local mt = tonumber(arguments.month or arguments[2]) or 1
	local dy = tonumber(arguments.day or arguments[3]) or 1
	local ranks = {0,31,59,90,120,151,181,212,243,273,304,334}

	local rank = (ranks[mt] or 0) + dy - 1
	if(fun.isLeapYear(yr) and (mt >= 3)) then
		rank = rank+1
	end
	return rank
end

function fun.do_daysBetween(arguments)
	local yr1 = tonumber(arguments[1]) or 0
	local yr2 = tonumber(arguments[2]) or 0

	return fun.daysSinceOrigin(yr2) - fun.daysSinceOrigin(yr1)
end

function fun.daysSinceOrigin(year)
	local yr = year-1
	return 365*yr + math.floor(yr/4) - math.floor(yr/100) + math.floor(yr/400)
end

function fun.isLeapYear(year)
	local yr = tonumber(year) or 1
	return (yr%4 == 0) and ((yr%100 ~= 0) or (yr%400 == 0))
end

function fun.toRoman(number)
	local n = math.floor(number)
	local letters = {"I","V","X","L","C","D","M","",""}
	local pattern = {"","0","00","000","01","1","10","100","1000","02"}
	local result = ""
	if(n<=0 or n>=4000) then
		result = "---"
	else
		for i=1,7,2 do
			local p = pattern[n%10 + 1]
			for d=0,2 do
				p = string.gsub(p,tostring(d),letters[i+d])
			end
			result = p .. result
			n = math.floor(n/10)
		end
	end
	return result
end

function fun.dateRepublican(frame)
	local pframe = frame:getParent()
	local arguments = pframe.args
	return fun.formatRepCal(fun.do_toRepCal(arguments))
end

function fun.do_toRepCal(arguments)
	local yr = tonumber(arguments.year or arguments[1]) or 2000
	local repDays = fun.do_dayRank(arguments) + fun.do_daysBetween{1792,yr} - fun.do_dayRank{1792,9,22}
	local repYear = math.floor((repDays+731)/365.25) - 1
	local repDayRank = repDays - 365*(repYear-1) - math.floor(repYear/4)
	local repMonth, repDay = math.floor(repDayRank/30)+1, (repDayRank%30)+1
	return {repYear, repMonth, repDay}
end

function fun.formatRepCal(arguments)
	local months = {"Vandemyer","Brümer","Frimer","Nivoz","Pluvioz","Ventoz","Jerminal","Floreal","Prerial","Messidor","Termidor","Fruktidor"}
	local extras = {"of virtue","genius","work","rewards","of opinion","of the Revolution"}
	local result = ""
	if(arguments[2] < 13) then
		result = result .. tostring(arguments[3]) .. "\194\160" .. months[arguments[2]]
	else
		result = result .. "gün " .. extras[arguments[3]]
	end
	result = result .. " " .. fun.toRoman(arguments[1])
	return result
end

function fun.age( an, mn, jn, ac, mc, jc )
	if ac == nil then
		local today = os.date( '!*t' )
		ac = today.year
		mc = today.month
		jc = today.day
	else
		ac = tonumber( ac )
		mc = tonumber( mc )
		jc = tonumber( jc )
	end

	local an = tonumber( an )
	local mn = tonumber( mn )
	local jn = tonumber( jn )

	if an == nil or ac == nil or mn == nil or mc == nil then
		return
	end

	local age = ac - an
	if mc == mn then
		if jc == nil or jn == nil then
			return
		end
		return age-tonumber( jc < jn and 1 or 0 )
	else
		return age-tonumber( mc < mn and 1 or 0 )
	end
end

function fun.modelAge( frame )
	local args = frame:getParent().args
	local age = fun.age (
		args[1] or args['il'],
		args[2] or args['ay'],
		args[3] or args['gün'],
		args[4],
		args[5],
		args[6]
	)
	if age then
		return age
	else
		return '<span class="error">Dəqiq sinı hesablamaq üçün parametrlər səhvdir və ya çatışmır</span>'
	end
end

function fun.julianDay( year, month, day, hour, min, sec )
	local julian
	julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 )
			- math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 100 )
			+ math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 400 )
			+ math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
			+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400
			- 32167.5
	return julian
end

function fun.julianDayJulian( year, month, day, hour, min, sec )
	local julian
	julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 )
			+ math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
			+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400
			- 32205.5
	return julian
end

function fun.julianDayToGregorian( julianDay )
	local base = math.floor( julianDay + 32044.5 )
	local nCentury = math.floor( ( base * 4 + 3 ) / 146097 )
	local sinceCentury = base - math.floor( nCentury * 146097 / 4 )
	local nYear = math.floor( ( sinceCentury * 4 + 3 ) / 1461 )
	local sinceYear = sinceCentury - math.floor( nYear * 1461 / 4 )
	local nMonth = math.floor( ( sinceYear * 5 + 2 ) / 153 )

	local day = sinceYear - math.floor( ( nMonth * 153 + 2 ) / 5 ) + 1
	local month = nMonth - math.floor( nMonth / 10 ) * 12 + 3
	local year = math.floor( sinceYear / 306 ) + nYear + 100 * nCentury - 4800

	return year, month, day
end

function fun.julianDayToJulian( julianDay )
	local year = math.modf( ( julianDay * 4 - 6884469 ) / 1461 )
	local r2 = julianDay - math.modf( ( 1461 * year + 6884472 ) / 4 )
	local month = math.modf( ( 5 * r2 + 461 ) / 153 )
	local day = r2 - math.modf( ( 153 * month - 457 ) / 5 ) + 1
	if month > 12 then
		year = year + 1
		month = month - 12
	end
	return year, month, day
end

function fun.julianToGregorian( year, month, day )
	return fun.julianDayToGregorian( fun.julianDayJulian( year, month, day ) )
end

function fun.gregorianToJulian( year, month, day )
	year = tonumber(year)
	if month then month = tonumber(month) else month = 6 end
	if day then day = tonumber(day) else day = 15 end
	return fun.julianDayToJulian( fun.julianDay( year, month, day ) )
end

function fun.CEST(frame)
	local opt = trim(frame.args[1] or frame:getParent().args[1])
	local t = mw.getContentLanguage():formatDate("I", nil, true)

	if (t == "1") then
		if (opt == "without link") then
			return "CEST"
		elseif (opt == "shift") then
			return "2"
		else
			return "CEST"
		end
	else
		if (opt == "without link") then
			return "CET"
		elseif (opt == "shift") then
			return "1"
		else
			return "CET"
		end
	end
end

return fun