モジュール:Date

local fun = {}
local TableBuilder = require( 'モジュール:TableBuilder' )
local Outils = require( 'モジュール:Outils' )
-- chargement de la base de donnée répertoriant certaines pages existant ou n'existant pas pour éviter les "ifexist".
local dataLiens
local success, resultat = pcall ( mw.loadData, 'モジュール:Date/Data' )
if success then
	dataLiens = resultat
else
	-- protection au cas ou le sous module serait mal modifié
	dataLiens = {  = { mois = { aucun = 1000, tous = { 1773, 2014 } }, } }
end
-- nettoie un paramètre non nommé (vire les espaces au début et à la fin)
-- retourne  nil si le texte est vide ou n'est pas du texte. Attention c'est important pour les fonction qui l'utilise.
local trim = Outils.trim
-- Fonction destiné à mettre la première lettre du mois en majuscule du mois : 
-- utilisation de string car aucun mois ne commance par une lettre non ascii en français ou anglais. 
local function ucfirst( str )
	return str:sub( 1, 1 ):upper() .. str:sub( 2 )
end
local modelePremier = '1er'
-- liste des mois, écriture exacte et simplifiée, en minuscule
local liste_mois = {
	{ "janvier", "jan.", "janv.", "jan", "janv", "january", nJour = 31 },
	{ "février", "fevrier", "fev.", "fev", "fév.", "fév", "february", nJour = 29 },
	{ "mars", "mar.", "mar", "march", nJour = 31 },
	{ "avril", "avr.", "avr", "apr", "april", nJour = 30 },
	{ "mai", "may", nJour = 31 },
	{ "juin", "jun", "june", nJour = 30 },
	{ "juillet", "juil.", "juil", "juill.", "juill", "jul", "july", nJour = 31 },
	{ "août", "aout", "aou", "aug", "august", nJour = 31 },
	{ "septembre", "sept.", "sept", "sep.", "sep", "september", nJour = 30 },
	{ "octobre", "oct.", "oct", "october", nJour = 31 },
	{ "novembre", "nov.", "nov", "november", nJour = 30 },
	{ "décembre", "decembre", "déc.", "dec.", "dec", "déc", "december", nJour = 31 },
}
local liste_saison = {
	{ 'printemps', 'spring', },
	{ 'été', 'summer', },
	{ 'automne', 'autumn', },
	{ 'hiver', 'winter', },
}
-- nom du mois à partir du numéro
function fun.nomDuMois( num )
	if type( num ) ~= "number" or num  12 then
		return nil
	end
	return liste_moisnum1
end
---
-- valide que la chaîne passée est un mois valide.
-- retourne le nom complet ou nil si non reconnu
-- si reconnu, retourne aussi le numéro du mois 1-12
function fun.valideMois( mois )
	local m = trim( mois )
	if m then
		m = mw.ustring.lower( m )
		for i = 1, 12 do
			local j = 1
			while liste_moisij do
				if liste_moisij == m then
					return liste_moisi1, i
				end
				j = j + 1
			end
		end
	end
	-- pas trouvé = return nil
end
---
-- valide que la chaîne passée est un mois valide.
-- retourne le nom complet ou nil si non reconnu
-- si reconnu, retourne aussi le numéro du mois 1-12
function fun.valideSaison( saison )
	if type( saison ) ~= "string" then
		return nil
	end
	
	local m = mw.ustring.lower( trim( saison ) )
	
	for i = 1, 4 do
		local j = 1
		while liste_saisonij ~= nil do
			if liste_saisonij == m then
				return liste_saisoni1
			end
			j = j + 1
		end
	end
	-- pas trouvé = return nil
end
---
-- determinationMois trouve le numéro du mois et son nom,
-- à partir de son nom, de son numéro ou d'une expression mathématique.
-- Si le deuxième paramètre est vrai, les nombres supérieur à 12 ou non entiers sont acceptés.  
function fun.determinationMois( mois, mod, boucle )
	local num, nom
	if tonumber( mois ) then
		num = math.floor( tonumber( mois ) )
		if mod then			
			-- si le nombre du mois est calculé par une exression, le résultat peut être supérieur à 12, ou inférieur à 1
			num = math.fmod( num + 239, 12 ) + 1  -- +239 car fmod(-1) = -1 et non 11 
		elseif num  12 then
			num = nil
		end
	elseif trim( mois ) then
		nom, num = fun.valideMois( mois )
		if nom == nil and boucle == nil then
			-- essai de détermination d'un nombre avec le parser #expr de Mediawiki.
			-- le paramètre boucle évite de tourner en boucle.
			nom, num = fun.determinationMois( mw.getCurrentFrame():callParserFunction( '#expr', mois ), true, true )
		end
	end
	if num and not nom then
		nom = liste_moisnum1
	end
	return nom, num
end
--  fonction interne à modeleDate, pour déterminer si on peut se passer de faire un ifexit
local function existDate( dataQualificatif, annee, mois )
	local data
	if mois then
		data = dataQualificatif.mois
	else
		data = dataQualificatif.annee
	end
	if type( data ) ~= 'table' then
		-- si data n'existe pas c'est que l'on considère qu'il n'y a pas de lien.
		return
	end
	-- le qualificatif est remplacer par celui de la base de donnée, ce qui permet des alias.
	local lien = annee .. ' ' .. ( dataQualificatif.qualificatif or  )
	local seul = annee
	if mois then
		lien = mois .. ' ' .. lien
		seul = ucfirst( mois ) .. ' ' .. annee
	end
	local aucun = tonumber( data.aucun )
	if aucun and annee  nil
		return nil
	elseif type( data.tous ) == 'table' then
		local tous1, tous2 = tonumber( data.tous1 ), tonumber( data.tous2 )
		if tous1 and tous2 and annee >= tous1 and annee 1er', '1er' }
		for i, v in ipairs( nomJour ) do
			jour = jour:gsub( v,  )
		end
		for i, v in ipairs( premier ) do
			jour = jour:gsub( v, '1' )
		end
		jour = trim( jour )
	end
	return jour
end
---
-- Sépare une chaine date en un table contenant les champs jour, mois et annee.
-- la date doit contenir le mois.
function fun.separationJourMoisAnnee( date )
	date = trim( date )
	if date then
		local function erreur( periode, valeur )
			return false, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' )
		end
		local jour, mois, annee, masquerMois, masquerAnnee
		-- variable pour construire les regex
		local j = '(0-3?%d)'      -- jour
		local m = '(01?%d)'       -- mois numérique
		local mmm =  '(^%s%p%d+.?)' -- mois en toute lettre
		local aj = '(%-?%d+)'       -- année ou jour
		local s = ' ./-+'          -- séparateur simple
		local sep = '( ./-+)'      -- séparateur avec capture, pour le détecter deux fois
		local moins = '(%-?)'        -- signe moins pour signifier qu'il ne faut pas afficher cette donnée
		local regexb = {
			jmmm = '^'..j..s..mmm..moins..'$',
			mmmjva = '^'..mmm..s..j..', ?'..aj..'$',
		}
		
		date = fun.nettoyageJour( date )
		-- réduction av. J-C pour simplifier un peu les regex :
		date = date:gsub( '(%d+) ?AaVv%.? ?Jj .-*Cc%.?', '-%1' )
		-- gestion de 1er
		date = date:gsub( modelePremier, '1' ):gsub( '%f%d1er', '1' )
		-- supression de l'heure dans les date ISO
		date = date:gsub( '^+?(%d-*%d%d%-%d%d)T%d%d%d:,.+-*Z?$' , '%1')
		
		-- test année seule
		if date:match( '^'..aj..'$' ) then
			annee = date:match( '^'..aj..'$' )
		elseif date:match( '^'..aj..s..aj..moins..'$' ) then
			-- jj/mm, mm/aaaa ou aaaa/mm 
			local a, b, sb = date:match( '^'..aj..s..aj..moins..'$' )
			a, b = tonumber( a ), tonumber( b )
			if  a > 12 and ( b  31 ) or
				b > 12 and ( a  31 ) then
				return erreur( 'Date', date )
			elseif b  31 then
				mois, annee, masquerAnnee = a, b, sb 
			elseif a  31 then
				annee, mois  = a, b
			elseif b > 12 then
				return erreur( 'Mois', b )
			else
				jour, mois, masquerMois = a, b, sb
			end
		elseif date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' ) then
			-- jj/mm/aaaa ou aaaa/mm/jj
			jour, sep, mois, masquerMois, annee, masquerAnnee =  date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' )
			if sep == '-' and masquerMois == '-' and masquerAnnee ==  and tonumber( annee ) > 0 then
				-- date au format jj-mm--aaaa type 17-06--44 pour 17 juin 44 av. JC
				masquerMois = nil
				annee = 0 - annee
			end
		elseif date:match( '^'..j..sep..mmm..moins..'%2'..aj..moins..'$' ) then
			-- jj mmm aaaa
			jour, sep, mois, masquerMois, annee, masquerAnnee = date:match( '^'..j..sep..mmm..moins..'%2'..aj..moins..'$' )
		elseif date:match( '^'..mmm..s..aj..moins..'$' ) then
			-- mmm aaaa
			mois, annee, masquerAnnee = date:match( '^'..mmm..s..aj..moins..'$' )
		elseif date:match( '^'..j..s..mmm..moins..'$' ) then
			-- jj mmmm
			jour, mois, masquerMois = date:match( '^'..j..s..mmm..moins..'$' )
		elseif date:match( '^'..mmm..s..j..', ?'..aj..'$') then
			-- mmm jj, aaaa (format anglo-saxon)
			mois, jour, annee = date:match( '^'..mmm..s..j..', ?'..aj..'$')
		elseif date:match( '^'..mmm..'$' ) then
			mois = date
		else
			return erreur( 'Date', date )
		end
		local jn, an = tonumber( jour ), tonumber( annee )
		if jn and an and ( jn > 31 or jn = 3 ) and an  31 then
					return erreur( 'Jour', bjour )
				elseif jour > liste_moisnumMois.nJour then
					return erreur( 'Jour', bjour .. ' ' .. mois )
				elseif jour == 29 and numMois == 2 and annee and ( math.fmod( annee, 4 ) ~= 0 ) then
					-- l'année bisextile sur les siècles est toujours acceptée pour être compatible avec les dates juliennes.
					return erreur( 'Jour', '29 février ' .. annee  )
				end
			else
				-- S'il n'y a pas de jour on regarde si la première lettre du mois est en majuscule
				if bmois:match( '^%u' ) then
					-- oui, on passe la première lettre en majuscule
					mois = ucfirst( mois )
				end
				-- s'il n'y a pas d'année non plus on retourne le mois simple
			end
		end
	else
		-- on teste le jour si présent
		local bjour = args1
		if Outils.notEmpty( bjour ) then
			if annee then
				return erreur( 'Mois', 'absent' )
			else
				bjour = fun.nettoyageJour( bjour )
				jour = tonumber( bjour )
				if jour then
					if jour > 31 or jour  12 then
			-- le mois est dans le premier paramètre et l'année dans le deuxième
			decalage = -1
		end
	end
	return decalage
end
function fun._modeleDateParam( args, decalage )
	local function analyseParam( p )
		-- sépare le signe moins final éventuel signifiant que le paramètre ne soit pas être affiché.
		p = trim( p )
		if p then
			local d, s = p:match( '^(.-)(%-?)$' )
			if s == '-' then
				return d, true
			end
			return d
		end
	end
	local param, resultat, test, mM, mA
	if decalage == 0 then
		param = { trim( args 1 + decalage  ), }
		param2, mM = analyseParam( args 2 + decalage  )
		param3, mA = analyseParam( args 3 + decalage  )
		test, resultat = fun.validationJourMoisAnnee( param )
	elseif decalage == -2 then
		test, resultat = fun.separationJourMoisAnnee( args1 )
	elseif decalage == 'D' then
		test, resultat = fun.separationJourMoisAnnee( args2 )
		mA = true
		decalage = -1
	else
		param = { trim( args 1 + decalage  ), trim( args 2 + decalage  ), trim( args 3 + decalage  ) }
		test, resultat = fun.validationJourMoisAnnee( param )
		if decalage > 0 then
			mA = true
			if decalage == 2 then
				mM = true
			end
		end
	end
	if test then
		resultat.qualificatif = trim( args 4 + decalage  )
		resultat.masquerAnnee = resultat.masquerAnnee or mA
		resultat.masquerMois = resultat.masquerMois or mM
	end
	return test, resultat
end
---
-- émule le modèle Date.
-- Paramètres :
--		1 : jour (numéro ou "1er"). optionnel, si absent pas de jour
--		2 : mois (en toutes lettres)
--		3 : année (nombre)
--		4 : optionnel, spécialité de l'année
--		Comportement spécial ("truc à deux balles au lieu d'utiliser un
--		paramètre nommé du genre "sans année=oui"...") : si 1 est vide
--		mais que le reste est complet → on n'affiche pas l'année
function fun.modeleDate( frame )
	local args = Outils.extractArgs( frame )
	local decalage = fun.determinationDecalage(args)
	local test, resultat = fun._modeleDateParam( args, decalage )
	local cat = 
	if test then
		for n, v in pairs( resultat ) do
			argsn = v
		end
		resultat = fun._modeleDate( args )
		if decalage ~= 0 then
			decalage = ( decalage == -2 ) and 'P' or decalage
			cat =  .. decalage ..  -- catégorisation temporaire
		end
	else
		local namespaceCategorisation = { 0 = true, 4 = true, 10 = true, 14 = true, 100 = true }
		if namespaceCategorisation mw.title.getCurrentTitle().namespace  and not Outils.notEmpty( args.nocat ) then
			cat = 'Catégorie:Page utilisant le modèle date avec une syntaxe erronée'
		end
	end
	return ( resultat or  ) .. cat
end
function fun._modeleDate( args )
	local annee, mois, numMois, jour = args.annee, args.mois, args.numMois, args.jour
	local qualificatif = args.qualificatif
	
	if ( annee or mois or jour ) == nil then
		return
	end
	
	-- on traite l'age, naissance et mort
	local age = trim( args'âge' or args'age' )
	age = age and  fun.age( annee, numMois, jour )
	local naissance = trim( args.naissance )
	local mort = trim( args.mort )
	
	-- on traite le calendrier
	local gannee, gmois, gjour = annee, numMois, jour        -- date suivant le calendrier grégorien pour 
	local jannee, jmois, jjour = annee, mois, jour   -- servira éventuellement à a affiché la date selon le calendrier julien
	local julien2, julien3 = nil, nil                        -- servira éventuellement à a affiché des parenthèses
	local julien = trim( string.lower( args.julien or  ) )
	if annee and jour then
		local amj = annee * 10000 + numMois * 100 + jour
		if amj  0 then
				gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
			else
				-- calendrier grégorien proleptique avec année 0.
				gannee, gmois, gjour = fun.julianToGregorian( annee + 1, numMois, jour )
			end
		elseif julien == 'oui' then
			gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
			annee, mois, jour = gannee, liste_moisgmois1, gjour
		end
	else
		if annee and annee 1er'
		end
		if jannee ~= annee then
			julien3 = '(' .. jjour .. ' ' .. jmois .. ' ' .. jannee .. ')'
		else
			julien2 = '(' .. jjour .. ' ' .. jmois .. ')'
		end
	end
	
	-- le jour si présent
	local qualifJour = 
	if jour then
		local texteJour = jour
		if args.nolinks then
			if jour == 1 then
				jour = modelePremier
			end
			wikiListe.insert( jour )
		else
			qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif
				or dataCat.jour and dataCat.qualificatif
				or 
			local lien = jour .. ' ' .. mois .. ' ' .. qualifJour
			if jour == 1 then
				jour = '1er'
				lien = '1er ' .. mois .. ' ' .. qualifJour
			end
			-- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour.
			wikiListe.insert( wikiLien( lien, jour ) )
			wikiListe.insert( wikiLien( lien, jour .. ' '.. mois ) )
		end
		iso.insert( 1, string.sub( '0' .. gjour, -2 ) )
	end
	
	-- le mois
	if mois then
		if #wikiListe == 0 and annee == nil then
			return mois
		end
		if args.nolinks then
			if not args.masquerMois then 
				wikiListe.insert( mois )
			end
		else
			local lien
			if annee then
				lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois )
				if lien == nil and qualificatif and qualifJour ==  then
					-- test nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif.
					lien = existDate( dataLiens, annee, mois )
				end
			end
			if lien or args.masquerMois then
				-- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter 'mois'
				wikiListe.remove()
				if not args.masquerMois then
					wikiListe.insert( wikiLien( lien, mois ) )
				end
			elseif #wikiListe > 0 then
				-- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois'
				wikiListe.remove( #wikiListe - 1 )
			elseif args.masquerAnnee then
				-- s'il n'y a pas de jour et que l'année n'est pas affichée, on insère le mois seul.
				wikiListe.insert( mois )
			end
		end
		if gmois then
			iso.insert( 1, string.sub( '0' .. gmois, -2 ) )
		end
	end
	if( julien2 ) then
		wikiListe.insert( julien2 )
	end
	
	-- l'année
	if annee then
		if not args.masquerAnnee then
			local texteAnnee = annee
			local lien
			if annee av. J.-C.'
				end
			end
			if args.nolinks then -- seulement si on doit l'affichée
				wikiListe.insert( texteAnnee )
			else
				lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee ) or lien or annee			
				if mois and #wikiListe == 0 then
					-- si le mois n'a pas de lien et n'est pas affiché avec le jour, il est affiché avec l'année.
					texteAnnee = mois .. ' ' .. texteAnnee
				end
				wikiListe.insert( wikiLien( lien, texteAnnee ) )
			end
		end
		if gannee > 999 then
			iso.insert( 1, gannee )
		elseif gannee > -1 then
			iso.insert( 1, string.sub( '000' .. gannee , -4 ) )
		elseif gannee > -999 then
			-- calendrier grégorien proleptique avec année 0.
			iso.insert( 1, 'U-' .. string.sub( '000' .. ( 0 - gannee ), -4 ) )
		else
			iso.insert( 1, 'U' .. gannee )				
		end
	end
	if( julien3 ) then
		wikiListe.insert( julien3 )
	end
		
	-- l'age
	if type( age ) == 'number' and age >= 0 and ( not naissance or age ' ) then 
			return args3
		end
	elseif args2:match(  ) then
		return args2
	end
	
	local nolinks = trim( args.nolinks )
	local nocat = args.nocat
	local dateN = nettoyageDate( args 2 or  )
	local dateM, qualificatif
	if naissance or mort then
		dateM = nettoyageDate( args3 or  )
		qualificatif = args.qualificatif or args4 
	else
		qualificatif = args.qualificatif or args3 
	end
	
	if mort then
		if not dateM then
			return
		end	
		local t2, tdateM = fun.separationJourMoisAnnee( dateM )
		if t2 then
			local age = 	
			if dateN then
				local t1, tdateN = fun.separationJourMoisAnnee( dateN )
				if t1 and t2 then
					local calcul = fun.age( tdateN.annee, tdateN.numMois, tdateN.jour, tdateM.annee, tdateM.numMois, tdateM.jour )
					if tonumber( calcul ) then
						if calcul > 1 then
							age = ' (à ' .. calcul .. ' ans)' 
						elseif calcul == 1 then
							age = ' (à un an)'
						elseif calcul == 0 then
							age = ' (à moins d’un an)'
						end
					end
				end
			end
			return fun.modeleDate{ dateM, qualificatif = qualificatif, mort = '1', nolinks = nolinks, nocat = nocat } .. age
		else
			return args3
		end
	else
		if not trim( dateN ) then
			return
		end		
		local t1, tdateN = fun.separationJourMoisAnnee( dateN )
		if t1 then
			if naissance and dateM == nil then
				return fun.modeleDate{ dateN, qualificatif = qualificatif, age = 'oui', naissance='1', nolinks = nolinks, nocat = nocat }
			else
				return fun.modeleDate{ dateN, qualificatif = qualificatif, naissance=naissance, nolinks = nolinks, nocat = nocat }
			end	
		else
			return args 2
		end
	end
end
---
-- la fonction dateISO renvoie un date au format aaaa-mm-jj (sans liens)
-- l'année peut être sous la forme 2013 ou 2013
-- le mois peut être en lettre ou en chiffres
-- le jour peut être sous la forme '05',  ou 'vendredi 13'
function fun.dateISO( frame )
	local args = Outils.extractArgs( frame )
	local annee = Outils.notEmpty( args'année', args.annee, args.year, args.date )
	-- extraction de l'année
	if type( annee ) == 'string' then
		annee = ( tonumber( annee )	-- match '2013'
				or string.match ( annee, '%D(%d%d%d%d)%D' ) -- match  '2013'
				or string.match ( annee, '%D(%d%d%d%d)$' )  -- match '17 septembre 2013'
				or string.match ( annee, '^(%d%d%d%d)%D' )  -- match '2013-09-17'
		)
	end
	annee = tonumber( annee )
	
	-- le format de date iso est défini suivant le calendrier grégorien.
	-- Avant l'année 1583 la date est calendrier est probablement du calendrier julien,
	-- donc autant s'abstenir.
	if annee and annee > 1582  then
		local mois = Outils.notEmpty( args.mois, args.month )
		-- num mois trouve le numéro du mois, qu'il soit numérique ou texte, complet ou abrégé.
		local nomMois, numMois = fun.determinationMois( mois )
		if numMois then
			mois = '-' .. string.sub( '0' .. numMois, -2 )
			
			local jour = Outils.notEmpty( args.jour, args.day, args'quantième' )
			if type( jour ) == 'string' then
				jour = tonumber( jour ) or tonumber( string.match ( jour, '%d+') )
			end
			jour = tonumber( jour )
			if jour and jour = 3)) then
		rank = rank+1
	end
	return rank
end
-- Nombre de jours entre deux années (du 1er janvier au 1er janvier)
-- Suit le calendrier grégorien
function fun.do_daysBetween(arguments)
	local yr1 = tonumber(arguments1) or 0
	local yr2 = tonumber(arguments2) or 0
	
	return fun.daysSinceOrigin(yr2) - fun.daysSinceOrigin(yr1)
end
-- Nombre de jours depuis l'année 1 (du 1er janvier au 1er janvier)
function fun.daysSinceOrigin(year)
	local yr = year-1
	return 365*yr + math.floor(yr/4) - math.floor(yr/100) + math.floor(yr/400)
end
-- Test d'année bissextile (Suit le calendrier grégorien)
function fun.isLeapYear(year)
	local yr = tonumber(year) or 1
	return (yr%4 == 0) and ((yr%100 ~= 0) or (yr%400 == 0))
end
-- Conversion d'un nombre en chiffres romains
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=4000) then
		result = "---"
	else
		for i=1,7,2 do
			local p = patternn%10 + 1
			for j=0,2 do
				p = string.gsub(p,tostring(j),lettersi+j)
			end
			result = p .. result
			n = math.floor(n/10)
		end
	end
	return result
end
-- Conversion et affichage d'une date dans le calendrier républicain
function fun.dateRepublicain(frame)
	local pframe = frame:getParent()
	local arguments = pframe.args
	return fun.formatRepCal(fun.do_toRepCal(arguments))
end
---
-- Calcul d'une date dans le calendrier républicain
-- On suppose que les années 4n+3 sont sextiles (3, 7, 11...)
function fun.do_toRepCal(arguments)
	local yr = tonumber(arguments.year or arguments1) or 2000
	-- rang absolu du jour demandé, le jour 0 étant le 22 septembre 1792 (1er jour de l'an I)
	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
---
-- Formatage d'une date selon le calendrier républicain
-- Usage : fun.formatRepCal{année,mois,jour}
function fun.formatRepCal(arguments)
	local months = {"Vendémiaire","Brumaire","Frimaire","Nivôse","Pluviôse","Ventôse","Germinal","Floréal","Prairial","Messidor","Thermidor","Fructidor"}
	local extras = {"de la vertu","du génie","du travail","des récompenses","de l'opinion","de la révolution"}
	local result = ""
	if(arguments2  12 then
		year = year + 1
		month = month - 12
	end
	return year, month, day
end
---
-- calcul d'une date dans le calendrier grégorien à partir d'une date dans le calendrier julien
function fun.julianToGregorian( year, month, day )
	return fun.julianDayToGregorian( fun.julianDayJulian( year, month, day ) )
end
---
-- calcul d'une date dans le calendrier julien à partir d'une date dans le calendrier grégorien
function fun.gregorianToJulian( year, month, day )
	year = tonumber(year)
	if month then month = tonumber(month) else month = 6 end --prend une valeur centrale pour donner un best "guess"
	if day then day = tonumber(day) else day = 15 end
	return fun.julianDayToJulian( fun.julianDay( year, month, day ) )
end
---
-- erreurModuleData affiche  d'un message d'erreur si le Module:Langue/Data n'a pas été chargé correctement,
-- pour la page de discussion de la base de donnée et ceux qui veulent surveiller cette page.
function fun.erreurModuleData()
	local success, resultat = pcall ( mw.loadData, 'モジュール:Date/Data' )
	if success == false then
		local message = Le chargement du module Date/Data génère une erreur : %s
Cette erreur doit être corrigée au plus vite car des milliers de page ne s'affichent pas correctement
		return string.format( message, resultat )
	end
end
---
-- checkDataCat génère des liens vers les pages annuelles, mensuelles et d'éphémérides liè aux
-- catégories du Module:Date/Data. La date la plus ancienne dépend de 'aucun' et 'seul1'
-- Paramètres :
-- 	1 : la catégorie. Il y aura une section par qualificatif de cette catégorie.
-- 	mois : oui pour avoir les liens vers les pages mensuelles et éphémérides (4 jours dans l'année)
-- 	alias : pour avoir des lien pour les alias en plus des qualificatif
function fun.checkDataCat( frame )
	local category = trim(frame.args1)
	local monthLinks = frame .args.mois == 'oui'
	local alias = frame.args.alias == 'oui'
	local dataLink = mw.loadData( 'モジュール:Date/Data' )
	local wikiList =  TableBuilder.new()
	local currentYear = tonumber( os.date( '%Y' ) )
	local columns = 
	local newSection
	if monthLinks then
		newSection = '\n\n== %s ==\n\n=== Années ===\n' .. columns
	else
		newSection ='\n\n== %s ==\n' .. columns
	end
	for field, dataField in pairs( dataLink ) do
		-- boucle sur tous les qualificatif ayant pour catégorie le premier paramère
		if dataField.cat == category or ( category == 'cat' and dataField.cat == field ) then
			local monthInitialYear, initialYear
			-- définition de l'année à partir de laquelle on va tester toutes les année / mois
			if dataField.qualificatif == field or ( category == 'cat' and dataField.cat == field ) then
				if dataField.annee and dataField.annee.aucun and dataField.annee.aucun ' )
				
				if monthLinks then
					-- insertstion de liens vers les mois de l'année en court + 1 jusqu'à monthInitialYear
					wikiList.insert( '\n\n=== Mois ==='  )
					local month, sep
					for year = ( currentYear + 1 ), monthInitialYear, -1  do
						wikiList.insert( '\n* ' .. year .. ' : ' )
						sep = ' • '
						for j = 1, 12 do
							month = ucfirst( liste_moisj1 ) .. ' '
							if j == 12 then sep = 
							end
							wikiList.insert(  .. month ..  .. sep )
						end
					end
					
					-- insertion de quelques date pour tester les éphémérides
					wikiList.insert( '\n\n=== Jours ==='  )
					wikiList.insert( '\n* 1er janvier ' .. fieldLink ..  )
					wikiList.insert( '\n* 14 mars ' .. fieldLink ..  )
					wikiList.insert( '\n* 22 juin ' .. fieldLink ..  )
					wikiList.insert( '\n* 3 septembre ' .. fieldLink ..  )
				end
			end
		end
	end
	
	return table.concat( wikiList )
end
--
Cette fonction retourne "CET" ou "CEST" selon que dans la pseudo-timezone en cours
c'est l'heure d'été ou l'heure d'hiver.
Cette fonction n'a de sens a priori que pour des modèles utilisés en Europe

Paramètre optionnel non nommé : "sans lien" : retourne le texte CET/CEST. sinon
retourne ce même texte avec un wikilien vers les articles correspondant
--
function fun.CEST(frame)
	-- option : ne pas créer de wikilien
	local opt = trim(frame.args1 or frame:getParent().args1)
	-- on récupère l'information dans la zone courante
	local t = mw.getContentLanguage():formatDate("I", nil, true)
	
	if (t == "1") then  -- heure d'été
		if (opt == "sans lien") then
			return "CEST"
		elseif (opt == "décalage") then
			return "2"
		else
			return "CEST"
		end
	else  -- heure d'hiver (ou autre zone où ça ne s'applique pas)
		if (opt == "sans lien") then
			return "CET"
		elseif (opt == "décalage") then
			return "1"
		else
			return "CET"
		end
	end
end
return fun