« Module:Date » : différence entre les versions

Une page de Wikipédia, l'encyclopédie libre.
Contenu supprimé Contenu ajouté
Demande de Zebulon84 : rendre le tiret insécable dans "av J.-C."
Zebulon84 (discuter | contributions)
copie de Module:Date/Bac à sable : permet {{date|22 avril 1015}} et Modèle:Date-, et amélioration diverses. (voir WP:DIPP#Module:Date (d · h · j · ↵)
Ligne 3 : Ligne 3 :
local TableBuilder = require( 'Module:TableBuilder' )
local TableBuilder = require( 'Module:TableBuilder' )
local Outils = require( 'Module:Outils' )
local Outils = require( 'Module: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, 'Module: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)
-- nettoie un paramètre non nommé (vire les espaces au début et à la fin)
Ligne 28 : Ligne 36 :
{ "novembre", "nov.", "nov", "november", nJour = 30 },
{ "novembre", "nov.", "nov", "november", nJour = 30 },
{ "décembre", "decembre", "déc.", "dec.", "dec", "déc", "december", nJour = 31 },
{ "décembre", "decembre", "déc.", "dec.", "dec", "déc", "december", nJour = 31 },
}

local liste_saison = {
{ 'printemps', 'spring', },
{ 'été', 'summer', },
{ 'automne', 'autumn', },
{ 'hiver', 'winter', },
}
}


Ligne 54 : Ligne 69 :
if liste_mois[i][j] == m then
if liste_mois[i][j] == m then
return liste_mois[i][1], i
return liste_mois[i][1], i
end
j = j + 1
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( mw.text.trim( saison ) )
for i = 1, 4 do
local j = 1
while liste_saison[i][j] ~= nil do
if liste_saison[i][j] == m then
return liste_saison[i][1]
end
end
j = j + 1
j = j + 1
Ligne 64 : Ligne 102 :
-- determinationMois trouve le numéro du mois et son nom,
-- determinationMois trouve le numéro du mois et son nom,
-- à partir de son nom, de son numéro ou d'une expression mathématique.
-- à 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.
-- l'objet frame est facultatif, mais permet de tester si mois est une expression type '2+3'
function fun.determinationMois( mois, frame )
function fun.determinationMois( mois, mod, boucle )
local num, nom
local num, nom
if tonumber( mois ) then
if tonumber( mois ) then
num = math.floor( math.fmod( tonumber( mois ) - 1, 12 ) ) + 1
num = math.floor( tonumber( mois ) )
if mod then
elseif type( mois ) == "string" 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 < 1 or num > 12 then
num = nil
end
elseif trim( mois ) then
nom, num = fun.valideMois( mois )
nom, num = fun.valideMois( mois )
if nom == nil and frame and frame.callParserFunction then
if nom == nil and boucle == nil then
-- essai de détermination d'un nombre avec le parser #expr de Mediawiki.
-- essai de détermination d'un nombre avec le parser #expr de Mediawiki.
-- la fonction s'appelle sans l'objet frame pour ne pas tourner en boucle.
-- le paramètre boucle évite de tourner en boucle.
nom, num = fun.determinationMois( frame:callParserFunction( '#expr', mois ) )
nom, num = fun.determinationMois( mw.getCurrentFrame():callParserFunction( '#expr', mois ), true, true )
end
end
end
end
Ligne 130 : Ligne 174 :


---
---
-- Supprime le jour de la semaine, et "le" avant une date
-- émule le modèle {{m|Date}}.
function fun.nettoyageJour( jour )
-- Paramètres :
if type( jour ) == 'string' then
-- 1 : jour (numéro ou "1er"). optionnel, si absent pas de jour
local nomJour = { '[Ll]undi', '[Mm]ardi', '[Mm]ercredi', '[Jj]eudi', '[Vv]endredi',
-- 2 : mois (en toutes lettres)
'[Ss]amedi', '[Dd]imanche', '^ *[Ll]e' }
-- 3 : année (nombre)
local premier = { '<abbr class="abbr" title="Premier" >1<sup>er</sup></abbr>', '1er' }
-- 4 : optionnel, spécialité de l'année
for i, v in ipairs( nomJour ) do
-- Comportement spécial ("truc à deux balles au lieu d'utiliser un
jour = jour:gsub( v, '' )
-- paramètre nommé du genre "sans année=oui"...") : si 1 est vide
end
-- mais que le reste est complet → on n'affiche pas l'année
for i, v in ipairs( premier ) do
function fun.modeleDate( frame )
jour = jour:gsub( v, '1' )
local args = Outils.extractArgs( frame )
end
jour = mw.text.trim( jour )
end
return jour
end

---
-- sépare une chaine date en un tabel contenant les champs jour, mois annee.
function fun.separationJourMoisAnnee( date, decalage, args )
date = trim( date )
if date then
local function erreur( periode, valeur )
return false, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' )
end
decalage = decalage or 0
args = args or {}
date = fun.nettoyageJour( date )
local annee = string.match( date, '^%d%d%d%d?$' )
if annee then
return fun.validationJourMoisAnnee{ '', '', annee, args[2 + decalage], decalage = decalage }
end
-- test date au format jj-mm-aaaa
local j, m, a = string.match( date, '^([0-3]?%d)-([^ /-]+)-*(%d*[ AVJCavjc.-]*)$' )
if not m then
-- test date aux formats jj/mm/aaaa ou dd mmm aaaa
j, m, a = string.match( date, '^([0-3]?%d)[ /]+([^ /-]+)[ /]*(-?%d*[ AVJCavjc.-]*)$' )
end
if not m then
-- test date aux formats aaaa-mm-jj, aaaa/mm/jj et aaaa mmm jj
a, m, j = string.match( date, '^(-?%d%d*)[ /-]+([^ /-]+)[ /-]*([0-3]?%d?)$' )
end
if not m then
-- test date aux formats mm/aaaa ou mmm aaaa
m, a = string.match( date, '^([^ /-]+)-?[ /]*(-?%d*[ AVJCavjc.]*)$' )
end
if m then
if decalage < 0 then
decalage = decalage + 2
end
if tonumber( j ) and tonumber( j ) > 31 and tonumber( a ) and tonumber( a ) < 31 then
-- la date est au format aaaa/mm/jj
return fun.validationJourMoisAnnee{ a, m, j, args[2 + decalage], decalage = decalage }
else
return fun.validationJourMoisAnnee{ j, m, a, args[2 + decalage], decalage = decalage }
end
else
return erreur( 'Date', date )
end
else
return true, {}
end
end
-- chargement de la base de donnée répertoriant certaines pages existant ou n'existant pas pour éviter les "ifexist".
---
local dataLiens
-- separationJourMoisAnnee prend jusqu'a cinq paramètre et essaie de les séparer en jour, mois, annee et qualificatif
local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' )
-- la date peut être dans le premier paramètre ou séparée dans les paramètre 1 à 3 ou 2 à 4.
if success then
-- Le qualificatif est cherché dans le paramètre suivant.
dataLiens = resultat
-- La fonction retourne true suivit d'une table avec la date en paramètres nommé (sans accent sur année)
else
-- ou false suivit d'un message d'erreur.
-- protection au cas ou le sous module serait mal modifié
function fun.validationJourMoisAnnee( frame, ... )
dataLiens = { [''] = { mois = { aucun = 1000, tous = { 1938, 2013 } }, } }
local args = Outils.extractArgs( frame, ... )
local jour, mois, numMois, annee, qualificatif, erreur
args[1] = tostring( args[1] or args['jour'] or '' )
args[2] = tostring( args[2] or args['mois'] or '' )
args[3] = tostring( args[3] or args['annee'] or args['année'] or '')
args[4] = tostring( args[4] or '' )
local function erreur( periode, valeur )
return false, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' )
end
end
local annee, mois, numMois, jour
local decalage = 0
local decalage = 0
-- si pas de jour mais que args[2] est un mois on décale tout et on
-- si pas de jour mais que args[2] est un mois on décale tout et on
-- n'affiche pas l'année
-- n'affiche pas l'année
if trim( args[1] ) == nil and fun.valideMois( trim( args[3] ) ) ~= nil then
local arg1, arg2, arg3 = trim( args[1] ), trim( args[2] ), trim( args[3] )
if arg1 == nil and arg2 and (fun.determinationMois( args[3] ) or arg2:match( '[^ /][ /-].*%d%d$' ) ) then
decalage = 1
decalage = 1
elseif arg1 == nil and arg2 == nil and arg3 and
( fun.determinationMois( args[4] ) or arg3:match( '[^ /][ /-].*%d%d$' ) ) then
decalage = 2
elseif arg1 and arg1:match( '%d%d%d$' ) then
-- l'année est dans le premier paramètre
decalage = -2
elseif not tonumber( args[3] ) and
tonumber( args[2] ) and tonumber( args[2] ) > 12 and
fun.determinationMois( args[1] )
then
-- le mois est dans le premier paramètre et l'année dans le deuxième
decalage = -1
end
end
Ligne 166 : Ligne 283 :
if annee == nil and type( bannee ) == 'string' then
if annee == nil and type( bannee ) == 'string' then
-- test si l'année contient av. J.-C.
-- test si l'année contient av. J.-C.
annee = mw.ustring.match( mw.ustring.upper( bannee ), '^(%d+)%sAV%.?%s?J%.?%-?C' )
annee = string.match( string.upper( bannee ), '^(%d+)%sAV%.?%s?J%.?%-?C' )
annee = tonumber( annee )
annee = tonumber( annee )
if annee then
if annee then
annee = 0 - annee
annee = 0 - annee
elseif decalage == -2 then
return fun.separationJourMoisAnnee( bannee, decalage, args )
else
else
return erreur( 'Année', bannee )
local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
if namespaceCategorisation[ mw.title.getCurrentTitle().namespace ] then
return Outils.erreur( 'Année invalide (' .. bannee .. ')[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée|A]]')
else
return Outils.erreur( 'Année invalide (' .. bannee .. ')')
end
end
end
end
end
Ligne 186 : Ligne 300 :
local bmois = args[2 + decalage]
local bmois = args[2 + decalage]
if Outils.notEmpty( bmois ) then
if Outils.notEmpty( bmois ) then
mois, numMois = fun.valideMois( bmois )
mois, numMois = fun.determinationMois( bmois )
if mois == nil then
if mois == nil then
mois = fun.valideSaison( bmois )
-- on teste si le mois est fourni sous forme numérique
if mois == nil then
numMois = tonumber( bmois )
if annee then
mois = fun.nomDuMois( numMois )
return erreur( 'Mois', bmois )
end
elseif type( dataLiens[ trim( bmois ) ] ) == 'table' then
if mois == nil then
-- le deuxième paramètre est un qualificatif, donc la date est dans le premier paramètre
local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
local bjour = args[1 + decalage]
if namespaceCategorisation[ mw.title.getCurrentTitle().namespace ] then
return fun.separationJourMoisAnnee( bjour, decalage, args )
return Outils.erreur( 'Mois invalide (' .. bmois .. ')[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée|M]]')
else
else
return Outils.erreur( 'Mois invalide (' .. bmois .. ')')
return erreur( 'Mois ou qualificatif', bmois )
end
end
end
else
else
Ligne 205 : Ligne 320 :
if Outils.notEmpty( bjour ) then
if Outils.notEmpty( bjour ) then
jour = tonumber( bjour )
jour = tonumber( bjour )
if jour == nil and type( bjour ) == 'string' then
if jour == nil then
bjour = mw.text.trim( bjour )
jour = tonumber( fun.nettoyageJour( bjour ) )
end
if bjour == '1er'
if jour == nil then
or bjour == '<abbr class="abbr" title="Premier" >1<sup>er</sup></abbr>' -- correspond à {{1er}}
return erreur( 'Jour', bjour )
then
jour = 1
else
local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
if namespaceCategorisation[ mw.title.getCurrentTitle().namespace ] then
return Outils.erreur( 'Jour invalide (' .. bjour .. ')[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée|J]]')
else
return Outils.erreur( 'Jour invalide (' .. bjour .. ')')
end
end
end
end
-- on valide que le jour est correct
-- on valide que le jour est correct
if jour < 1 or jour > 31 then
if jour < 1 or jour > 31 then
return Outils.erreur( 'Jour invalide (' .. bjour .. ')' )
return erreur( 'Jour', bjour )
elseif jour > liste_mois[numMois].nJour then
elseif jour > liste_mois[numMois].nJour then
return erreur( 'Jour', bjour .. bmois )
local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
if namespaceCategorisation[ mw.title.getCurrentTitle().namespace ] then
return Outils.erreur( 'Jour invalide (' .. bjour .. ')[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée|J]]')
else
return Outils.erreur( 'Jour invalide (' .. bjour .. ')')
end
-- l'année bisextile n'est pas testée pour accepter les dates juliennes.
-- l'année bisextile n'est pas testée pour accepter les dates juliennes.
end
end
Ligne 241 : Ligne 342 :
end
end
end
end
elseif decalage > -1 then
-- else
-- on teste le jour si présent
-- return Outils.erreur("Le mois est obligatoire")
local bjour = args[1 + decalage]
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 < 1 then
annee = jour
jour = nil
else
return erreur( 'date', 'jour seul : ' .. bjour )
end
else
return fun.separationJourMoisAnnee( bjour, decalage, args )
end
end
end
end
end
-- on traite le champs optionnel
-- on traite le champs optionnel
local qualificatif = trim( args[4 + decalage] ) or args.qualificatif
qualificatif = trim( args[4 + decalage] ) or args.qualificatif
local result = {
jour = jour,
mois = mois,
numMois = numMois,
annee = annee,
qualificatif = qualificatif,
decalage = decalage + ( args.decalage or 0 )
}
return true, result
end


---
-- émule le modèle {{m|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 )
-- séparation des paramètres jour, mois et année si nécessaire
local test, resultat = fun.validationJourMoisAnnee( args )
if not test then
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
return resultat .. '[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée]]'
else
return resultat
end
end
local annee, mois, numMois, jour = resultat.annee, resultat.mois, resultat.numMois, resultat.jour
local decalage, qualificatif = resultat.decalage, resultat.qualificatif
if ( annee or mois or jour ) == nil then
return
end
-- on traite l'age, naissance et mort
-- on traite l'age, naissance et mort
Ligne 256 : Ligne 419 :
-- on traite le calendrier
-- on traite le calendrier
local gannee, gmois, gjour = annee, numMois, jour -- date suivant le calendrier grégorien pour <time>
local gannee, gmois, gjour = annee, numMois, jour -- date suivant le calendrier grégorien pour <time>
local jannee, jmois, jjour, julien = annee, mois, jour -- servira éventuellement à a affiché la date selon le calendrier julien
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 julien2, julien3 = nil, nil -- servira éventuellement à a affiché des parenthèses
local julien = trim( string.lower( args.julien or '' ) )
local julien = trim( string.lower( args.julien or '' ) )
Ligne 262 : Ligne 425 :
local amj = annee * 10000 + numMois * 100 + jour
local amj = annee * 10000 + numMois * 100 + jour
if amj < 15821014 then
if amj < 15821014 then
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
if annee > 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
elseif julien == 'oui' then
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
annee, mois, jour = gannee, liste_mois[gmois][1], gjour
annee, mois, jour = gannee, liste_mois[gmois][1], gjour
end
else
if annee and annee < 0 then
gannee = gannee + 1
end
end
end
end
Ligne 276 : Ligne 448 :
local iso = TableBuilder.new() -- reçois le format date ISO de ce paramètre
local iso = TableBuilder.new() -- reçois le format date ISO de ce paramètre
local dataQualificatif = dataLiens[qualificatif or '']
local dataQualificatif, dataCat
if type( dataQualificatif ) ~= 'table' then
if not args.nolinks then
dataQualificatif = dataLiens[qualificatif or '']
-- si le qualifiquatif n'est pas dans la base de donnée, on crée une table minimum,
if type( dataQualificatif ) ~= 'table' then
-- qui imposera un test sur l'annee, mais considère qu'il n'y a pas de lien sur le jour ou le mois
-- si le qualifiquatif n'est pas dans la base de donnée, on crée une table minimum,
dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } }
-- qui imposera un test sur l'annee, mais considère qu'il n'y a pas de lien sur le jour ou le mois
dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } }
end
dataCat = dataLiens[dataQualificatif.cat]
if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then
dataCat = { qualificatif = '' }
end
end
end
local function wikiLien( lien, texte )
local dataCat = dataLiens[dataQualificatif.cat]
if lien == texte then
if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then
dataCat = { qualificatif = '' }
return '[[' .. texte .. ']]'
else
return '[[' .. lien .. '|' .. texte .. ']]'
end
end
end
Ligne 299 : Ligne 481 :
end
end

-- le jour si présent
-- le jour si présent
local qualifJour = ''
local qualifJour = ''
if jour then
if jour then
local texteJour = jour
-- éphémérides du qualificatif, ou de sa catégorie, ou générale.
if args.nolinks then
qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif
or dataCat.jour and dataCat.qualificatif
if jour == 1 then
jour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
or ''
end
local lien = jour .. ' ' .. mois .. ' ' .. qualifJour
if jour == 1 then
wikiListe.insert( jour )
else
jour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif
lien = '1er ' .. mois .. ' ' .. qualifJour
or dataCat.jour and dataCat.qualificatif
or ''
local lien = jour .. ' ' .. mois .. ' ' .. qualifJour
if jour == 1 then
jour = '1<sup>er</sup>'
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
end
-- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour.
wikiListe.insert( '[[' .. lien .. '|' .. jour .. ']]' )
wikiListe.insert( '[[' .. lien .. '|' .. jour .. ' '.. mois .. ']]' )
iso.insert( 1, string.sub( '0' .. gjour, -2 ) )
iso.insert( 1, string.sub( '0' .. gjour, -2 ) )
end
end
Ligne 320 : Ligne 509 :
-- le mois
-- le mois
if mois then
if mois then
if #wikiListe == 0 and ( annee == nil or decalage > 1 ) then
local lien
return mois
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
end
if lien then
if args.nolinks then
if decalage < 2 then
-- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois']]
wikiListe.remove()
wikiListe.insert( mois )
end
wikiListe.insert( '[[' .. lien .. '|' .. mois .. ']]' )
else
else
local lien
-- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois'
if annee then
wikiListe.remove( #wikiListe - 1 )
lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois )
-- s'il n'y avait pas je jour, la liste est vide mais ça ne pose pas de problème
if lien == nil and qualificatif and qualifJour == '' then
-- sauf si l'année n'est pas affichée :
-- test nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif.
if #wikiListe == 0 and ( annee == nil or decalage > 0 ) then
lien = existDate( dataLiens[''], annee, mois )
return mois
end
end
end
if lien or decalage == 2 then
-- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois']]
wikiListe.remove()
if decalage < 2 then
wikiListe.insert( wikiLien( lien, mois ) )
end
else
-- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois'
wikiListe.remove( #wikiListe - 1 )
-- s'il n'y avait pas je jour, la liste est vide mais ça ne pose pas de problème
-- sauf si l'année n'est pas affichée :
if #wikiListe == 0 and ( annee == nil or decalage > 0 ) then
return mois
end
end
end
if gmois then
iso.insert( 1, string.sub( '0' .. gmois, -2 ) )
end
end
iso.insert( 1, string.sub( '0' .. gmois, -2 ) )
end
end
if( julien2 ) then
if( julien2 ) then
wikiListe.insert( julien2 )
wikiListe.insert( julien2 )
end
end
-- l'année
-- l'année
if annee and decalage == 0 then -- seulement si on doit l'affichée
if annee then
local texteAnnee = annee
local lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee )
local texte = annee
local lien
if annee < 0 then
if annee < 0 then
local annneeAvJc = 0 - annee
local annneeAvJc = 0 - annee
Ligne 355 : Ligne 558 :
local avJC = trim( string.lower( args.avJC or '' ) )
local avJC = trim( string.lower( args.avJC or '' ) )
if args.avJC == 'non' then
if args.avJC == 'non' then
texte = annneeAvJc
texteAnnee = annneeAvJc
else
else
texte = annneeAvJc .. ' <abbr class="abbr" title="'
texteAnnee = annneeAvJc .. ' <abbr class="abbr" title="'
.. annneeAvJc .. ' avant Jésus-Christ">av. J.-C.</abbr>'
.. annneeAvJc .. ' avant Jésus-Christ">av. J.-C.</abbr>'
end
end
end
iso.insert( 1, tostring( annneeAvJc - 10000 ) ) -- ! avant JC, ce n'est pas une date iso ! Pour un tri correct sur wiki.
if args.nolinks then -- seulement si on doit l'affichée
if decalage < 1 then
wikiListe.insert( texteAnnee )
end
else
else
lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee ) or lien or annee
iso.insert( 1, string.sub( '000' .. annee , -4 ) )
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
if decalage < 1 then -- seulement si on doit l'affichée
wikiListe.insert( wikiLien( lien, texteAnnee ) )
end
end
end
lien = lien or annee
if mois and #wikiListe == 0 then
if gannee > 999 then
iso.insert( 1, gannee )
-- si le mois n'a pas de lien et n'est pas affiché avec le jour, il est affiché avec l'année.
elseif gannee > -1 then
texte = mois .. ' ' .. texte
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
wikiListe.insert( '[[' .. lien .. '|' .. texte .. ']]' )
end
end
if( julien3 ) then
if( julien3 ) then
wikiListe.insert( julien3 )
wikiListe.insert( julien3 )
end
end

-- l'age
-- l'age
if type( age ) == 'number' and age >= 0 then
if type( age ) == 'number' and age >= 0 and ( not naissance or age < 120 ) then
if age == 0 then
if age == 0 then
age = '(moins d\'un&nbsp;an)'
age = '(moins d\'un&nbsp;an)'
Ligne 383 : Ligne 604 :
age = '(' .. age .. '&nbsp;ans)'
age = '(' .. age .. '&nbsp;ans)'
end
end
else
age = false
end
end
Ligne 392 : Ligne 615 :
local wikiHtml = mw.html.create( '' )
local wikiHtml = mw.html.create( '' )
local dateHtml = wikiHtml:tag( 'span' )
local dateHtml = wikiHtml:tag( 'time' )
:addClass( 'date-lien nowrap' )
:addClass( 'datasortkey' )
:attr( 'data-sort-value', isoTexte )
:wikitext( wikiTexte )
:wikitext( wikiTexte )
if wikiTexte:match( ' ' ) then
dateHtml:addClass( 'nowrap' )
end
if isoTexte ~= wikiTexte then
dateHtml:attr( 'datetime', isoTexte )
end
if not args.nolinks then
dateHtml:addClass( 'date-lien' )
end
if naissance then
if naissance then
dateHtml:addClass( 'bday' )
dateHtml:addClass( 'bday' )
Ligne 412 : Ligne 641 :
return tostring( wikiHtml )
return tostring( wikiHtml )
end
end

---
-- fonction destinée aux infobox, notamment pour afficher les dates de naissance et de mort
-- les liens présent dans les dates fournies sont automatiquement supprimées pour gérer les cas ou
-- le paramètre contient déjà un modèle date.
-- Paramètres :
-- 1 : type de date à afficher (naissance / n, mort / m, ou date / d)
-- 1 : Date ou date de naissance
-- 2 : Date de mort si type n ou m
-- qualificatif = suffixe des page de date à lier (exemple : en musique)
-- nolinks : n'affiche pas de lien
function fun.dateInfobox( frame )
local args = frame.args
if not args[1] and args[2] then
return
end
local function nettoyageDate( date )
-- supprime les liens en ce qui est entre parenthèse à la fin
date = date:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end )
date = date:gsub( '%([^()]+%)$', '' )
if date:match( '^%s*[-?/Nn][/Aa.]*%s*$' ) or date:match( 'inconn?ue?' ) then
return false
else
return trim( date )
end
end
local naissance = args[1]:match( '^n' )
local mort = args[1]:match( '^m' )
-- S'il y a des liens il y a probablement déjà un modèle date, évitons de l'exècuter une 2e fois
if mort and args[3] and args[2]:match( '</time>' ) then
return args[3]
elseif args[2]:match( '</time>' ) then
return args[2]
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( args[3] or '' )
qualificatif = args.qualificatif or args[4]
else
qualificatif = args.qualificatif or args[3]
end
if mort then
if not dateM then
return
end
local age = ''
if dateN then
local t1, tdateN = fun.separationJourMoisAnnee( dateN )
local t2, tdateM = fun.separationJourMoisAnnee( dateM )
if t1 and t2 then
local calcul = fun.age( tdateN.annee, tdateN.numMois, tdateN.jour, tdateM.annee, tdateM.numMois, tdateM.jour )
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
return fun.modeleDate{ dateM, qualificatif, mort = '1', nolinks = nolinks, nocat = nocat } .. age
else
if not trim( dateN ) then
return
end
if naissance and dateM == nil then
return fun.modeleDate{ dateN, qualificatif, age = 'oui', naissance='1', nolinks = nolinks, nocat = nocat }
else
return fun.modeleDate{ dateN, qualificatif, naissance=naissance, nolinks = nolinks, nocat = nocat }
end
end
end



---
---
Ligne 421 : Ligne 732 :
local annee = Outils.notEmpty( args['année'], args.annee, args.year )
local annee = Outils.notEmpty( args['année'], args.annee, args.year )
if annee then
if annee then
if annee:match( '^%-?%d+$' ) then
-- si l'année est renseigné, on essaye de trouver le mois
-- si l'année est correctement renseigné, on essaye de trouver le mois
local mois = Outils.notEmpty( args.mois, args.month )
local mois = Outils.notEmpty( args.mois, args.month )
if mois then
if mois then
local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
mois = fun.determinationMois( mois, frame ) or mois
return fun.modeleDate{ jour, mois:lower(), annee, nolinks=true, nocat=true }
local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
if jour then
-- si le mois est valide on détermine le jour
jour = tonumber( jour ) or jour -- suppresion des 0 qui trainent
if jour == 1 or jour == '1er' then
jour = '<abbr class="abbr" title="Premier">1<sup>er</sup></abbr>'
end
return jour .. '&nbsp;' .. mois .. '&nbsp;' .. annee
else
else
return mois .. '&nbsp;' .. annee
return fun.modeleDate{ annee, nolinks=true, nocat=true }
end
end
else
else
Ligne 444 : Ligne 749 :
local date = Outils.validTextArg( args, 'date' )
local date = Outils.validTextArg( args, 'date' )
if date then
if date then
date = date:lower()
return '<span class="nowrap">' .. date .. '</span>'
local t, jma = fun.separationJourMoisAnnee( date )
else
return ''
if t then
args['année'] = jma.annee
jma.nolinks = true
jma.nocat = true
return fun.modeleDate( jma )
else
-- date non reconnue, on essaye Month day, year
local mois, jour, annee = mw.ustring.match( date, '^([%a]+)%s*(%d%d?),?%s*(%d+)$' )
if mois then
return fun.modeleDate{ jour, mois, annee, nolinks=true, nocat=true }
end
end
return date
end
end
end
end
return ''
end
end


Ligne 655 : Ligne 973 :


---
---
-- calcul du jour julien à partir d'une date du calendrier julier
-- calcul du jour julien à partir d'une date du calendrier julien
function fun.julianDayJulian( year, month, day, hour, min, sec )
function fun.julianDayJulian( year, month, day, hour, min, sec )
local julian
local julian

Version du 22 avril 2015 à 18:10

 Documentation[voir] [modifier] [historique] [purger]

Utilisation

Fonctions utilisables depuis un modèle

  • modeleDate(frame) – affiche une date, optionnellement avec les liens les plus pertinents (précisions ci-dessous).
  • dateISO(frame) – similaire à modeleInscriptionDate mais la date est au format aaaa-mm-jj. Paramètres nommés année, mois, jour. Pour respecter l'ISO 8601 qui définit la date uniquement selon le calendrier grégorien, cette fonction ne retourne rien pour les dates avant 1583.
  • dateInfobox(frame) affiche une date avec les liens pertinents, gère correctement les paramètres contenant déjà un modèle date, ou avec du texte suivant la date (précisions ci-dessous). Prévu pour être utilisé dans les Infobox.
  • dureeInfobox(frame) affiche la durée entre deux dates, si elle n'est pas déjà incluse. Prévu pour être utilisé dans les Infobox.
  • dateRepublicain(frame) – affiche une date grégorienne au format républicain (sans liens). Paramètres 1=année, 2=mois, 3=jour.
  • modeleAge(frame) - retourne l'âge (nombre d'années) depuis une date ou entre deux dates. Paramètres 1=année, 2=mois, 3=jour, 4=année, 5=mois, 6=jour.
  • erreurModuleData() - retourne un message d'erreur si Module:Date/Data ne se charge pas correctement.
  • checkDataCat(frame) - retourne une liste de pages annuelle et mensuelle pour faciliter les mises à jour de Date/Data. Paramètres 1=cat, mois=liste de mois si 'oui', alias = liste tous les alias si 'oui'

Fonctions utilisables depuis un autre module

  • determinationMois( mois ) - à partir d'un nom de mois, de son numéro ou d'une abréviation, retourne, si le mois a bien été trouvé, son nom canonique et son numéro
  • determinationSaison( saison ) - à partir d'un nom de saison, retourne, si la saison a bien été trouvée, son nom canonique
  • do_dayRank(arguments) - Rang du jour dans l'année. Paramètre arguments = { année, mois, jour } ou { year = année, month = mois, day = jour }
  • isLeapYear(year) - retourne true si year est une année bissextile dans le calendrier grégorien.
  • toRoman(number) - transforme number en une chaine le représentant en « chiffres romains ».
  • age( an, mn, jn, ac, mc, jc ) - similaire à modeleAge, mais les paramètres ne sont pas dans une table
  • julianDay( year, month, day, hour, minute, second ) - retourne le jour julien de la date transmise, suivant le calendrier grégorien astronomique (avec année 0)
  • julianDayJulian( year, month, day, hour, minute, second ) - retourne le jour julien d'une date du calendrier julien astronomique (avec année 0)
  • julianDayToGregorian( jd ) - retourne trois variables année, mois, jour représentant la date du calendrier grégorien astronomique correspondant à ce jour julien.
  • julianDayToJulian( jd ) - retourne trois variables année, mois, jour représentant la date du calendrier julien correspondant à ce jour julien.
  • julianToGregorian( year, month, day ) - transforme une date du calendrier julien en date du calendrier grégorien.
  • gregorianToJulian( year, month, day ) - transforme une date du calendrier grégorien en date du calendrier julien.

Modules externes dont ce module a besoin pour fonctionner

  • Date/Data - Base de donnée permettant de ne pas tester les pages que l'on sait existantes, ou n'existant pas.
  • TableBuilder – Utilise .insert et .concat pour simplifier la syntaxe.

modeleDate( frame )

Paramètres

  • 1 - jour ou vide - numérique, exception possible pour 1er ou 1er.
  • 2 - mois ou jour - numérique ou nom français ou anglais, éventuellement une abréviation courante.
  • 3 - année ou mois - Un nombre sera considéré comme année. Les années sont considérées comme suivant le calendrier grégorien après le 14 octobre 1582 (sauf si julien = 'oui') et le calendrier julien avant, sans année 0.
  • 4 - qualificatif ou année - texte correspondant à une page type « en photographie » pour « 2008 en photographie »
  • 5 - qualificatif
  • age ou âge - non vide pour afficher l'âge (aucun âge n'est affiché pour les dates dans le futur)
  • julien - 'oui' pour que la date soit considérée comme suivant le calendrier julien après le 14 octobre 1582. La date grégorienne avec liens est affichée suivie de la date julienne entre parenthèses.
  • compact - 'oui' pour abréger le nom du mois.
  • avJC - 'non' pour ne pas afficher 'av. J.-C.' après l'année si elle représente une année avant Jésus-Christ. Utile pour éviter les répétitions.
  • liens - 'oui' pour forcer l'ajout de liens quand la date ne contient pas de qualificatif.
  • nolinks - 'oui' pour empêcher l'ajout de liens (a précédence sur liens).

Fonctionnement

  • par défaut, le modèle n'ajoute des liens que si un qualificatif a été renseigné.
  • le modèle cherche à afficher la date avec des liens vers les pages liées au qualificatif. S'il n'y a pas de page liée au qualificatif un lien sera fait vers la page générale.
  • le premier paramètre est vide et le troisième correspond à un mois (texte uniquement), tous les paramètres sont considérés comme décalés et l'année ne sera pas affichée.
  • s'il n'y a pas de page spécifique pour ce mois-année, le mois sera affiché lié avec le jour à l'éphéméride. Priorité est donnée à l'éphéméride du qualificatif sur le lien mois-année sans qualificatif.
  • le modèle s'aide de la base de donnée Date/Data pour éviter d'utiliser la fonction mw.title (équivalent du parser #ifexist:).
  • cette base permet de remplacer le qualificatif par une catégorie plus générique. Si le qualificatif est « en tennis », l'éphéméride et la page mensuelle sera liée au qualificatif « en sport ».

Fonction modeleDate

Motif testé Chaîne testée Module Fonctions coûteuses
modèle d'avant / module
date récente 14|octobre|2001 1 / 0
date ancienne (1700 - 1943), jour =1 1|octobre|1842 1 / 1
date très ancienne (<1700), jour = 1er 1|janvier|537 1 / 0
qualificatif qui n'est pas dans la base 14|octobre|2010|en animation asiatique 4 / 1
date ancienne, qualificatif qui n'est pas dans la base 14|octobre|1842|en animation asiatique 4 / 2
avec qualificatif 14|Octobre|2001|en astronautique 3 / 0
avec qualificatif avec éphémérides 14|octobre|2005|dans les chemins de fer 4 / 0
pas de jour |octobre|2001 1 / 0
pas de jour avec qualificatif |Octobre|2001|en astronautique 3 / 0
qualificatif avec page annuelle qui pourrait exister 14|octobre|2006|en Égypte 4 / 1
qualificatif avec page mensuelle existante 14|octobre|2008|en France 3 / 0
qualificatif avec page mensuelle qui pourrait exister 14|octobre|2012|en France 4 / 1
qualificatif avec page annuelle et mensuelle qui pourrait exister 14|octobre|2012|en économie 4 / 2
date ancienne avec qualificatif 14|octobre|1845|en aéronautique 4 / 1
date négative 13|octobre|-63 1 / 0
date av. J.-C. (orthographe de la page) 1|octobre|63 av. J.-C. Erreur Lua à la ligne 256 : attempt to call field 'erreur' (a nil value). 1 / 0
date avJC (orthographe abrégée) 13|octobre|63 avJC 1 / 0
date négative, paramètre pour cacher av. J.-C. 13|octobre|-63|avJC=non 1 / 0
année invalide 14|octobre|2001 en sport Erreur Lua à la ligne 256 : attempt to call field 'erreur' (a nil value). 1 / 0
jour + mois avec majuscule 14|Octobre|2001 1 / 0
mois en abrégé 14|oct.|2001 1 / 0
mois en chiffre 14|10|2001 1 / 0
mois invalide 14|otcobre|2001 Erreur Lua à la ligne 256 : attempt to call field 'erreur' (a nil value). 1 / 0
jour invalide jeudi 14|octobre|2001 1 / 0
jour invalide (trop grand pour le mois) 31|septembre|2001 Erreur Lua à la ligne 256 : attempt to call field 'erreur' (a nil value). 1 / 0
uniquement l’année ||2001 1 / 0
uniquement l’année avec qualificatif ||2001|en littérature 1 / 0
sans année 14|octobre 0 / 0
jour uniquement 14 Erreur Lua à la ligne 256 : attempt to call field 'erreur' (a nil value). 0 / 0
mois uniquement |Octobre Octobre 0 / 0
sans argument 0 / 0
date du calendrier julien 1|octobre|2001|julien=oui
date du calendrier julien (changement de mois) 25|octobre|2001|julien=oui
date du calendrier julien (changement d'année) 25|décembre|2001|julien=oui
date de naissance 14|octobre|2001|age=oui (22 ans)

Comparaison avec {{date de naissance}}

  • les fonctions coûteuses sont les mêmes que celles du modèle Date
  • sans l'âge, voir comparaison avec {{Date}}
Motif testé Chaîne testée Modèle Date de naissance Module
simple 1|8|2006|âge=oui (17 ans) (17 ans)
avec qualificatif 1|août|2006|en Suisse|age=oui (17 ans) (17 ans)
date ancienne 2|1|598|age=oui (1426 ans)
l'an dernier 2|1|2012|age=oui (12 ans) (12 ans)
cette année 2|1|2013|age=oui (11 ans) (11 ans)
l'an prochain 2|1|2014|age=oui (10 ans) (10 ans)
sans jour |8|2006|âge=oui (17 ans) (17 ans)
annee seule ||2006|âge=oui (18 ans) (18 ans)


dateInfobox( frame )

Fonction destinée aux infobox, notamment pour afficher les dates de naissance et de mort. Les dates sont affichées avec des liens. Gère les cas où le paramètre contient déjà un modèle date. Le contenu du paramètre situé après la date (par exemple un lieu, une référence) est conservé.

Paramètres

  • 1 : type de date à afficher (naissance / n, mort / m, ou date / d)
  • 2 : Date ou date de naissance
  • 3 : Date de mort si type n ou m
  • qualificatif : suffixe des pages de date à lier (exemple : en musique)
  • nolinks : n'affiche pas de lien
  • préfixe : préfixe à afficher s'il y a un jour (par défaut vide)
  • préfixe sans jour : préfixe à afficher s'il n'y a pas de jour (par défaut vide)

Ces paramètres doivent être directement dans le #invoke appelant la fonction.

Exemples

  • {{#invoke:Date|dateInfobox|date|13 juillet 1927}}
  • {{#invoke:Date|dateInfobox|naissance|13 juillet 1927|}} (96 ans)
  • {{#invoke:Date|dateInfobox|naissance|13 juillet 1927|14 mai 2017}}
  • {{#invoke:Date|dateInfobox|naissance|30 juin 2017-}}Erreur Lua à la ligne 256 : attempt to call field 'erreur' (a nil value).
  • {{#invoke:Date|dateInfobox|mort|13 juillet 1927|30 juin 2017}} (à 89 ans)
  • {{#invoke:Date|dateInfobox|mort||30 juin 2017}}
  • {{#invoke:Date|dateInfobox|mort|13 juillet 1927|}}
  • {{#invoke:Date|dateInfobox|date|13 juillet 1927| qualificatif = en France}}
  • {{#invoke:Date|dateInfobox|date|13 juillet 1927| préfixe = le}}
  • {{#invoke:Date|dateInfobox|date|13 juillet 1927| préfixe = le | préfixe sans jour = en}}
  • {{#invoke:Date|dateInfobox|date|juillet 1927| préfixe = le}}
  • {{#invoke:Date|dateInfobox|date|juillet 1927| préfixe = le | préfixe sans jour = en}}
  • {{#invoke:Date|dateInfobox|date|13 juillet [[1927]]}}
  • {{#invoke:Date|dateInfobox|date|13 juillet [[1927 en France|1927]]}}
  • {{#invoke:Date|dateInfobox|date|{{date|13 juillet 1927|en France}}}}

dureeInfobox( frame )

Fonction destinée aux infobox, pour afficher la durée entre deux dates, notamment pour un poste ou une fonction. Gère les cas où la durée est déjà spécifiée dans l'une des valeurs. La durée est préfixée d'un retour à la ligne (<br />) et écrite en petit (<small>).

Paramètres

  • 1 : Date de début
  • 2 : Date de fin (optionnelle, date du jour par défaut)

Ces paramètres doivent être directement dans le #invoke appelant la fonction.

Exemples

  • {{#invoke:Date|dureeInfobox|8 septembre 2022}}Erreur de script : la fonction « dureeInfobox » n’existe pas.
  • {{#invoke:Date|dureeInfobox|{{date|27 juin 1940-}}|{{date|6 septembre 1940}}}}Erreur de script : la fonction « dureeInfobox » n’existe pas.

Voir aussi : les tests unitaires et ceux du bac à sable.

local fun = {}

local TableBuilder = require( 'Module:TableBuilder' )
local Outils = require( 'Module: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, 'Module: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

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


-- 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 < 1 or num > 12 then
		return nil
	end
	return liste_mois[num][1]
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 )
	if type( mois ) ~= "string" then
		return nil
	end
	
	local m = mw.ustring.lower( mw.text.trim( mois ) )
	
	for i = 1, 12 do
		local j = 1
		while liste_mois[i][j] ~= nil do
			if liste_mois[i][j] == m then
				return liste_mois[i][1], i
			end
			j = j + 1
		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( mw.text.trim( saison ) )
	
	for i = 1, 4 do
		local j = 1
		while liste_saison[i][j] ~= nil do
			if liste_saison[i][j] == m then
				return liste_saison[i][1]
			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 < 1 or 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_mois[num][1]
	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 <= aucun then
		-- si la l'année est dans la partie 'aucun' on teste s'il y a malgré tout un lien isolé
		if type( data.seul ) == 'table' then
			for i, v in ipairs( data.seul ) do
				if seul == v or seul == tonumber( v ) then
					return lien
				end
			end
		end
		-- partie aucun et pas de lien => nil
		return nil
	elseif type( data.tous ) == 'table' then
		local tous1, tous2 = tonumber( data.tous[1] ), tonumber( data.tous[2] )
		if tous1 and tous2 and annee >= tous1 and annee <= tous2 then
			-- l'année est dans la partie 'tous' donc on retourne le lien
			return lien
		end
	end
	-- l'annee n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe.
	cibleLien = mw.title.new( lien )
	if cibleLien and cibleLien.exists then
		return lien
	end
end

---
-- Supprime le jour de la semaine, et "le" avant une date
function fun.nettoyageJour( jour )
	if type( jour ) == 'string' then
		local nomJour = { '[Ll]undi', '[Mm]ardi', '[Mm]ercredi', '[Jj]eudi', '[Vv]endredi',
			'[Ss]amedi', '[Dd]imanche', '^ *[Ll]e' }
		local premier = { '<abbr class="abbr" title="Premier" >1<sup>er</sup></abbr>', '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 = mw.text.trim( jour )
	end
	return jour
end

---
-- sépare une chaine date en un tabel contenant les champs jour, mois annee.
function fun.separationJourMoisAnnee( date, decalage, args )
	date = trim( date )
	if date then
		local function erreur( periode, valeur )
			return false, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' )
		end
		decalage = decalage or 0
		args = args or {}
		date = fun.nettoyageJour( date )
		local annee = string.match( date, '^%d%d%d%d?$' )
		if annee then
			return fun.validationJourMoisAnnee{ '', '', annee, args[2 + decalage], decalage = decalage }
		end
		-- test date au format jj-mm-aaaa
		local j, m, a = string.match( date, '^([0-3]?%d)-([^ /-]+)-*(%d*[ AVJCavjc.-]*)$' )
		if not m then
			-- test date aux formats jj/mm/aaaa ou dd mmm aaaa
			j, m, a = string.match( date, '^([0-3]?%d)[ /]+([^ /-]+)[ /]*(-?%d*[ AVJCavjc.-]*)$' )
		end
		if not m then
			-- test date aux formats aaaa-mm-jj, aaaa/mm/jj et aaaa mmm jj
			a, m, j = string.match( date, '^(-?%d%d*)[ /-]+([^ /-]+)[ /-]*([0-3]?%d?)$' )
		end
		if not m then
			-- test date aux formats mm/aaaa ou mmm aaaa 
			m, a = string.match( date, '^([^ /-]+)-?[ /]*(-?%d*[ AVJCavjc.]*)$' )
		end
		if m then 
			if decalage < 0 then
				decalage = decalage + 2
			end
			if tonumber( j ) and tonumber( j ) > 31 and tonumber( a ) and tonumber( a ) < 31 then
				-- la date est au format aaaa/mm/jj
				return fun.validationJourMoisAnnee{ a, m, j, args[2 + decalage], decalage = decalage }
			else
				return fun.validationJourMoisAnnee{ j, m, a, args[2 + decalage], decalage = decalage }
			end
		else
			return erreur( 'Date', date )
		end
	else 
		return true, {}
	end
end
	
	
---
-- separationJourMoisAnnee prend jusqu'a cinq paramètre et essaie de les séparer en jour, mois, annee et qualificatif
-- la date peut être dans le premier paramètre ou séparée dans les paramètre 1 à 3 ou 2 à 4.
-- Le qualificatif est cherché dans le paramètre suivant.
-- La fonction retourne true suivit d'une table avec la date en paramètres nommé (sans accent sur année)
-- ou false suivit d'un message d'erreur.
function fun.validationJourMoisAnnee( frame, ... )
	local args = Outils.extractArgs( frame, ... )
	local jour, mois, numMois, annee, qualificatif, erreur
	args[1] = tostring( args[1] or args['jour'] or '' )
	args[2] = tostring( args[2] or args['mois'] or '' )
	args[3] = tostring( args[3] or args['annee'] or args['année'] or '')
	args[4] = tostring( args[4] or '' )
	
	local function erreur( periode, valeur )
		return false, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' )
	end
	
	local decalage = 0
	-- si pas de jour mais que args[2] est un mois on décale tout et on
	-- n'affiche pas l'année
	local arg1, arg2, arg3 = trim( args[1] ), trim( args[2] ), trim( args[3] )
	if arg1 == nil and arg2 and (fun.determinationMois( args[3] ) or arg2:match( '[^ /][ /-].*%d%d$' ) ) then
		decalage = 1
	elseif arg1 == nil and arg2 == nil and arg3 and 
		( fun.determinationMois( args[4] ) or arg3:match( '[^ /][ /-].*%d%d$' ) ) then
		decalage = 2
	elseif arg1 and arg1:match( '%d%d%d$' )  then
		-- l'année est dans le premier paramètre
		decalage = -2
	elseif not tonumber( args[3] ) and
			tonumber( args[2] ) and tonumber( args[2] ) > 12 and  
			fun.determinationMois( args[1] )
	then
		-- le mois est dans le premier paramètre et l'année dans le deuxième
		decalage = -1
	end
	
	-- on traite l'année
	local bannee = args[3 + decalage]
	if Outils.notEmpty( bannee ) then
		annee = tonumber( bannee )
		if annee == nil and type( bannee ) == 'string'  then
			-- test si l'année contient av. J.-C.
			annee = string.match( string.upper( bannee ), '^(%d+)%sAV%.?%s?J%.?%-?C' )
			annee = tonumber( annee )
			if annee then
				annee = 0 - annee
			elseif decalage == -2 then
				return fun.separationJourMoisAnnee( bannee, decalage, args )
			else
				return erreur( 'Année', bannee )
			end
		end
	else
		annee = nil
	end
	
	-- on traite le mois
	local bmois = args[2 + decalage]
	if Outils.notEmpty( bmois ) then
		mois, numMois = fun.determinationMois( bmois )
		if mois == nil then
			mois = fun.valideSaison( bmois )
			if mois == nil then
				if annee then
					return erreur( 'Mois', bmois )
				elseif type( dataLiens[ trim( bmois ) ] ) == 'table' then
					-- le deuxième paramètre est un qualificatif, donc la date est dans le premier paramètre
					local bjour = args[1 + decalage]
					return fun.separationJourMoisAnnee( bjour, decalage, args )
				else
					return erreur( 'Mois ou qualificatif', bmois )
				end
			end
		else
		
			-- on traite le jour si présent
			local bjour = args[1 + decalage]
			if Outils.notEmpty( bjour ) then
				jour = tonumber( bjour )
				if jour == nil then
					jour = tonumber( fun.nettoyageJour( bjour ) )
				end
				if jour == nil then
					return erreur( 'Jour', bjour )
				end
				-- on valide que le jour est correct
				if jour < 1 or jour > 31 then
					return erreur( 'Jour', bjour )
				elseif jour > liste_mois[numMois].nJour then
					return erreur( 'Jour', bjour .. bmois )
					-- l'année bisextile n'est pas testée pour accepter les dates juliennes.
				end
			else
				-- S'il n'y a pas de jour on regarde si la première lettre du mois est en majuscule
				if mw.ustring.match( bmois, '^%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
	elseif decalage > -1 then
		-- on teste le jour si présent
		local bjour = args[1 + decalage]
		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 < 1 then
						annee = jour
						jour = nil
					else
						return erreur( 'date', 'jour seul : ' .. bjour )
					end
				else
					return fun.separationJourMoisAnnee( bjour, decalage, args )
				end
			end
		end	
	end
	
	-- on traite le champs optionnel
	qualificatif = trim( args[4 + decalage] ) or args.qualificatif
	
	local result = { 
		jour = jour,
		mois = mois,
		numMois = numMois,
		annee = annee, 
		qualificatif = qualificatif, 
		decalage = decalage + ( args.decalage or 0 )
	}
	return true, result
end


---
-- émule le modèle {{m|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 )
	
	-- séparation des paramètres jour, mois et année si nécessaire
	local test, resultat = fun.validationJourMoisAnnee( args )
	if not test then
		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
			return resultat .. '[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée]]'
		else
			return resultat
		end
	end
	local annee, mois, numMois, jour = resultat.annee, resultat.mois, resultat.numMois, resultat.jour
	local decalage, qualificatif = resultat.decalage, resultat.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 <time>
	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 < 15821014 then
			if annee > 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_mois[gmois][1], gjour
		end
	else
		if annee and annee < 0 then
			gannee = gannee + 1
		end
	end
	
	
	-- on génère le résultat
	
	-- Déclarations des variables
	local wikiListe = TableBuilder.new()   -- reçois le texte affiché pour chaque paramètre
	local iso = TableBuilder.new()         -- reçois le format date ISO de ce paramètre
	
	local dataQualificatif, dataCat
	if not args.nolinks then
		dataQualificatif = dataLiens[qualificatif or '']
		if type( dataQualificatif ) ~= 'table' then
			-- si le qualifiquatif n'est pas dans la base de donnée, on crée une table minimum,
			-- qui imposera un test sur l'annee, mais considère qu'il n'y a pas de lien sur le jour ou le mois
			dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } }
		end
		dataCat = dataLiens[dataQualificatif.cat]
		if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then
			dataCat = { qualificatif = '' }
		end
	end
	local function wikiLien( lien, texte )
		if lien == texte then 
			return '[[' .. texte .. ']]'
		else
			return '[[' .. lien .. '|' .. texte .. ']]'
		end	
	end
	
	-- Date julienne
	if jjour ~= jour then
		if jjour == 1 then
			jjour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
		end
		if jannee ~= annee then
			julien3 = '(<abbr class=abbr title="selon le calendrier julien">' .. jjour .. ' ' .. jmois .. ' ' .. jannee .. '</abbr>)'
		else
			julien2 = '(<abbr class=abbr title="selon le calendrier julien">' .. jjour .. ' ' .. jmois .. '</abbr>)'
		end
	end
	

	-- le jour si présent
	local qualifJour = ''
	if jour then
		local texteJour = jour
		if args.nolinks then
			if jour == 1 then
				jour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
			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 = '1<sup>er</sup>'
				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 or decalage > 1 ) then
			return mois
		end
		if args.nolinks then
			if decalage < 2 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 decalage == 2 then
				-- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois']]
				wikiListe.remove()
				if decalage < 2 then
					wikiListe.insert( wikiLien( lien, mois ) )
				end
			else
				-- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois'
				wikiListe.remove( #wikiListe - 1 )
				-- s'il n'y avait pas je jour, la liste est vide mais ça ne pose pas de problème
				-- sauf si l'année n'est pas affichée :
				if #wikiListe == 0 and ( annee == nil or decalage > 0 ) then
					return mois
				end
			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
		local texteAnnee = annee
		local lien
		if annee < 0 then
			local annneeAvJc = 0 - annee
			lien = lien or ( annneeAvJc .. ' av. J.-C.' )
			local avJC = trim( string.lower( args.avJC or '' ) )
			if args.avJC == 'non' then
				texteAnnee = annneeAvJc
			else
				texteAnnee = annneeAvJc .. ' <abbr class="abbr" title="'
					.. annneeAvJc .. ' avant Jésus-Christ">av. J.-C.</abbr>'
			end
		end
		if args.nolinks then -- seulement si on doit l'affichée
			if decalage < 1 then
				wikiListe.insert( texteAnnee )
			end
		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
			if decalage < 1 then -- seulement si on doit l'affichée
				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 < 120 ) then
		if age == 0 then
			age = '(moins d\'un&nbsp;an)'
		elseif age == 1 then
			age = '(1&nbsp;an)'
		else
			age = '(' .. age .. '&nbsp;ans)'
		end
	else
		age = false
	end
	
	-- compilation du résultat
	local wikiTexte = wikiListe.concat( ' ' )
	local isoTexte = iso.concat( '-' )
	
	-- On ajoute un peu de sémantique.
	local wikiHtml = mw.html.create( '' )
	
	local dateHtml = wikiHtml:tag( 'time' )
			:wikitext( wikiTexte )
	if wikiTexte:match( ' ' ) then
		dateHtml:addClass( 'nowrap' )
	end
	if isoTexte ~= wikiTexte then
		dateHtml:attr( 'datetime', isoTexte )
	end
	if not args.nolinks then
		dateHtml:addClass( 'date-lien' )
	end
	if naissance then 
		dateHtml:addClass( 'bday' )
	elseif mort then
		dateHtml:addClass( 'dday' )
	end
	if age then
		wikiHtml:wikitext( ' ' )
				:tag( 'span' )
					:addClass( 'noprint')
					:wikitext( age )
					:done()
	end
	
	return tostring( wikiHtml )
end

---
-- fonction destinée aux infobox, notamment pour afficher les dates de naissance et de mort
-- les liens présent dans les dates fournies sont automatiquement supprimées pour gérer les cas ou 
-- le paramètre contient déjà un modèle date.
-- Paramètres :
-- 		1 : type de date à afficher (naissance / n, mort / m, ou date / d)
-- 		1 : Date ou date de naissance
-- 		2 : Date de mort si type n ou m
-- 		qualificatif = suffixe des page de date à lier (exemple : en musique)
-- 		nolinks : n'affiche pas de lien
function fun.dateInfobox( frame )
	local args = frame.args
	if not args[1] and args[2] then
		return
	end
	
	local function nettoyageDate( date )
		-- supprime les liens en ce qui est entre parenthèse à la fin
		date = date:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end )
		date = date:gsub( '%([^()]+%)$', '' )
		
		if date:match( '^%s*[-?/Nn][/Aa.]*%s*$' ) or date:match( 'inconn?ue?' ) then
			return false
		else
			return trim( date  )
		end
	end
	
	local naissance = args[1]:match( '^n' )
	local mort = args[1]:match( '^m' )
	
	-- S'il y a des liens il y a probablement déjà un modèle date, évitons de l'exècuter une 2e fois
	if mort and args[3] and args[2]:match( '</time>' ) then 
		return args[3]
	elseif args[2]:match( '</time>' ) then
		return args[2]
	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( args[3] or '' )
		qualificatif = args.qualificatif or args[4] 
	else
		qualificatif = args.qualificatif or args[3] 
	end
	
	if mort then
		if not dateM then
			return
		end
		local age = ''
		if dateN then
			local t1, tdateN = fun.separationJourMoisAnnee( dateN )
			local t2, tdateM = fun.separationJourMoisAnnee( dateM )
			if t1 and t2 then
				local calcul = fun.age( tdateN.annee, tdateN.numMois, tdateN.jour, tdateM.annee, tdateM.numMois, tdateM.jour )
				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
		return fun.modeleDate{ dateM, qualificatif, mort = '1', nolinks = nolinks, nocat = nocat } .. age
	else
		if not trim( dateN ) then
			return
		end
		if naissance and dateM == nil then
			return fun.modeleDate{ dateN, qualificatif, age = 'oui', naissance='1', nolinks = nolinks, nocat = nocat }
		else
			return fun.modeleDate{ dateN, qualificatif, naissance=naissance, nolinks = nolinks, nocat = nocat }
		end	
	end
end


---
-- voir émule le modèle:Inscription date
-- la détection des arguments permet d'utilisé la fonction depuis un modèle, depuis invoke, ou depuis une autre fonction.
-- pour facilité l'écriture de lua, annee (sans accent) est accepté lors de l'appel depuis lua.
function fun.inscriptionDate( frame )
	local args = Outils.extractArgs( frame )
	local annee = Outils.notEmpty( args['année'], args.annee, args.year )
	if annee then
		if annee:match( '^%-?%d+$' ) then
			-- si l'année est correctement renseigné, on essaye de trouver le mois
			local mois = Outils.notEmpty( args.mois, args.month )
			
			if mois then		
				local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
				return fun.modeleDate{ jour, mois:lower(), annee, nolinks=true, nocat=true }
			else
				return fun.modeleDate{ annee, nolinks=true, nocat=true }
			end
		else
			return annee
		end
	else
		-- si annee n'est pas précisé, on utilise la paramètre date
		local date = Outils.validTextArg( args, 'date' )
		if date then
			date = date:lower()
			local t, jma = fun.separationJourMoisAnnee( date )
			if t then 
				args['année'] = jma.annee
				jma.nolinks = true
				jma.nocat = true
				return fun.modeleDate( jma )
			else	
				-- date non reconnue, on essaye Month day, year
				local mois, jour, annee = mw.ustring.match( date, '^([%a]+)%s*(%d%d?),?%s*(%d+)$' )
				if mois then
					return fun.modeleDate{ jour, mois, annee, nolinks=true, nocat=true }
				end
			end
			return date
		end
	end
	return ''
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 en litérature|2013]]
-- le mois peut être en lettre ou en chiffres
-- le jour peut être sous la forme '05', '{{1er}}' 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 en musique|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 <= liste_mois[numMois].nJour then
				jour = '-' .. string.sub( '0' .. jour, -2 )
				return annee .. mois .. jour
			else
				return annee .. mois
			end
		else
			return tostring( annee )
		end
	end
end

---
-- Rang du jour dans l'année
-- Usage : do_dayRank{année,mois,jour}
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
	-- Rangs des premiers des mois
	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

-- 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(arguments[1]) or 0
	local yr2 = tonumber(arguments[2]) 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<=0 or n>=4000) then
		result = "---"
	else
		for i=1,7,2 do
			p = pattern[n%10 + 1]
			for j=0,2 do
				p = string.gsub(p,tostring(j),letters[i+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 arguments[1]) 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(arguments[2] < 13) then
		result = result .. tostring(arguments[3]) .. "&nbsp;" .. months[arguments[2]]
	else
		result = result .. "jour " .. extras[arguments[3]]
	end
	result = result .. " de l'an " .. fun.toRoman(arguments[1])
	return result
end

---
-- Voir Modèle:Âge
-- retourne l'age en fonction de la ou les dates fournies. La valeur retounée est de type 'number'
-- Parammètres :
-- 1, 2, 3 : année, mois jour de naissance (supposé dans le calendrier grégorien)
-- 4, 5, 6 : année, mois, joue du calcul (facultatif, par défaut la date UTC courante).
function fun.age( an, mn, jn, ac, mc, jc )
	local an = tonumber( an )
	if an == nil then
		-- pas de message d'erreur qui risque de faire planter la fonction appelante
		-- à elle de gérer ce retour.
		return
	end
	-- les jours et mois sont par défaut égal à 1, pour pouvoir calculer un age même si la date est incompète.
	local mn = tonumber( mn ) or 1
	local jn = tonumber( jn ) or 1
	
	local today = os.date( '!*t' )
	local ac = tonumber( ac ) or today.year
	local mc = tonumber( mc ) or today.month
	local jc = tonumber( jc ) or today.day
	
	local age = ac - an
	if mc < mn or ( mc == mn and jc < jn ) then
		age = age - 1
	end
	return age
end

function fun.modeleAge( frame )
	args = frame.getParent().args
	local annee = args[1] or args['année']
	if annee == nil then
		return Outils.erreur( "Il faut au minimum l'année pour calculer un âge" )
	end
	local age = fun.age (
		args[1] or args['année'],
		args[2] or args['mois'],
		args[3] or args['jour'],
		args[4],
		args[5],
		args[6]
	)
	if age then
		return age
	else
		return Outils.erreur("les paramètres doivent être des chiffres" )
	end
end

---
-- calcul du jour julien à partir d'une date du calendrier grégorien
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

---
-- calcul du jour julien à partir d'une date du calendrier julien
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

---
-- calcul d'une date dans le calendrier grégorien à partir du jour julien
function fun.julianDayToGregorian( julianDay )
	local base = math.floor( julianDay + 32044.5 )  -- 1 March -4800 (proleptic Gregorian date)
	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

---
-- calcul d'une date dans le calendrier julien à partir du jour julien
-- calcul basé sur l'algorythme de la page fr.wikipedia.org/wiki/Jour_julien (1/10/2013)
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

---
-- 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, 'Module:Date/Data' )
	if success == false then
		local message = [[<strong class="error">Le chargement du module Date/Data génère une erreur : </strong><br />%s<br />

<span class="error">Cette erreur doit être corrigée au plus vite car des milliers de page ne s'affichent pas correctement</span>
]]
		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 'seul[1]'
-- 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.args[1])
	local monthLinks = frame .args.mois == 'oui'
	local alias = frame.args.alias == 'oui'
	local dataLink = mw.loadData( 'Module:Date/Data' )
	local wikiList =  TableBuilder.new()
	local currentYear = tonumber( os.date( '%Y' ) )
	local columns = '<div style="-moz-column-width:5em;-webkit-column-width:5em;column-width:5em;-moz-column-gap:1em;-webkit-column-gap:1em;column-gap:1em;text-align:left;">'
	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 < currentYear then
					local aucun = ( dataField.annee.seul and dataField.annee.seul[1] ) or dataField.annee.aucun
					initialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), currentYear - 50 )
				else
					initialYear = currentYear - 50
				end
				if dataField.mois and tonumber( dataField.mois.aucun ) and ( tonumber( dataField.mois.aucun ) < currentYear ) then
					local aucun = dataField.mois.aucun
					monthInitialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), currentYear - 8 )
				else
					monthInitialYear = currentYear - 8
				end
			elseif alias then
				-- si le paramètre alias est défini on teste aussi tous les alias, sinon ils sont ignorés
				initialYear = currentYear - 50
				monthInitialYear = currentYear - 8
			end
			
			-- création de l'ensembles des liens
			if initialYear then
				-- ajout de lien vers les pages annuelles de l'année en court + 5 jusqu'à initialYear
				wikiList.insert( string.format( newSection, field ) )
				local fieldLink = ' ' .. field
				if category == 'cat' then
					fieldLink = ' ' .. dataField.qualificatif
				end
				for year = ( currentYear + 5 ), initialYear, -1  do
					wikiList.insert( '\n* [[' .. year .. fieldLink ..'|' .. year .. ']]' )
				end
				wikiList.insert( '\n</div>' )
				
				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_mois[j][1] ) .. ' '
							if j == 12 then sep = ''
							end
							wikiList.insert( '[[' .. month .. year .. ' ' .. fieldLink .. '|' .. 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 = mw.text.trim(frame.args[1] or frame:getParent().args[1] or "")
	-- 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 "[[Heure d'été d'Europe centrale|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 "[[Heure normale d'Europe centrale|CET]]"
		end
	end
end

return fun