Module:Date : Différence entre versions

De Wikonsult
Sauter à la navigation Sauter à la recherche
m (1 révision importée)
m (1 révision importée)
 
(2 révisions intermédiaires par un autre utilisateur non affichées)
Ligne 2 : Ligne 2 :
  
 
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".
+
-- chargement de la base de données répertoriant certaines pages existant ou n'existant pas pour éviter les "ifexist".
 
local dataLiens
 
local dataLiens
 
local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' )
 
local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' )
Ligne 8 : Ligne 8 :
 
dataLiens = resultat
 
dataLiens = resultat
 
else
 
else
-- protection au cas ou le sous module serait mal modifié
+
-- protection au cas le sous-module serait mal modifié
 
dataLiens = { [''] = { mois = { aucun = 1000, tous = { 1773, 2014 } }, } }
 
dataLiens = { [''] = { mois = { aucun = 1000, tous = { 1773, 2014 } }, } }
 
end
 
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)
-- retourne nil si le texte est vide ou n'est pas du texte. Attention c'est important pour les fonction qui l'utilise.
+
-- retourne nil si le texte est vide ou n'est pas du texte. Attention c'est important pour les fonctions qui l'utilisent.
 
local trim = Outils.trim
 
local trim = Outils.trim
  
-- Fonction destiné à mettre la première lettre du mois en majuscule du mois :  
+
-- Fonction destinée à mettre la première lettre du mois en majuscule :
-- utilisation de string car aucun mois ne commance par une lettre non ascii en français ou anglais.  
+
-- utilisation de string car aucun mois ne commence par une lettre non ascii en français ou anglais.
 
local function ucfirst( str )
 
local function ucfirst( str )
 
return str:sub( 1, 1 ):upper() .. str:sub( 2 )
 
return str:sub( 1, 1 ):upper() .. str:sub( 2 )
Ligne 39 : Ligne 39 :
 
{ num = 11, nJour = 30, abrev = 'nov.',  nom = 'novembre', alias = { 'nov.', 'nov', 'november' } },
 
{ num = 11, nJour = 30, abrev = 'nov.',  nom = 'novembre', alias = { 'nov.', 'nov', 'november' } },
 
{ num = 12, nJour = 31, abrev = 'déc.',  nom = 'décembre', alias = { 'decembre', 'déc.', 'dec.', 'dec', 'déc', 'december' } },
 
{ num = 12, nJour = 31, abrev = 'déc.',  nom = 'décembre', alias = { 'decembre', 'déc.', 'dec.', 'dec', 'déc', 'december' } },
aout = { num = 8, nJour = 31, abrev = 'aout', nom = 'aout', alias = { 'aou' } },
+
aout = { num = 8, nJour = 31, abrev = 'aout', nom = 'aout', alias = { 'aou' } },
 
}
 
}
  
-- ajoute les noms, abreviations et alias en tant que clé de listeMois
+
-- ajoute les noms, abréviations et alias en tant que clés de listeMois
 
for i = 1, 12 do
 
for i = 1, 12 do
local mois = listeMois[ i ]
+
local mois = listeMois[i]
listeMois[ tostring( i ) ] = mois
+
listeMois[tostring( i )] = mois
listeMois[ '0' .. i ] = mois
+
if i < 10 then
listeMois[ mois.nom ] = mois
+
listeMois['0' .. i] = mois
listeMois[ mois.abrev ] = mois
+
end
 +
listeMois[mois.nom] = mois
 +
listeMois[mois.abrev] = mois
 
for _, n in ipairs( mois.alias ) do
 
for _, n in ipairs( mois.alias ) do
listeMois[ n ] = mois
+
listeMois[n] = mois
 
end
 
end
 
end
 
end
 
for _, n in ipairs( listeMois.aout.alias ) do
 
for _, n in ipairs( listeMois.aout.alias ) do
listeMois[ n ] = listeMois.aout
+
listeMois[n] = listeMois.aout
 
end
 
end
fun.listeMois = listeMois
 
  
 
local liste_saisons = {
 
local liste_saisons = {
Ligne 65 : Ligne 66 :
 
}
 
}
  
---
+
-- à partir d'un nom de saison (en français ou en anglais),
-- valide que la chaîne passée est un mois valide.
+
-- retourne son nom canonique (exemple : "été")
-- retourne le nom complet ou nil si non reconnu
+
-- si non reconnu, retourne nil
-- si reconnu, retourne aussi le numéro du mois [1-12]
+
function fun.determinationSaison( saison )
function fun.valideMois( mois )
 
local m = trim( mois )
 
if m then
 
m = mw.ustring.lower( m )
 
if listeMois[ m ] then
 
return listeMois[ m ].nom, listeMois[ m ].num
 
end
 
end
 
end
 
 
 
---
 
-- valide que la chaîne passée est une saison valide.
 
-- retourne le nom complet ou nil si non reconnu
 
function fun.valideSaison( saison )
 
 
local s = trim( saison )
 
local s = trim( saison )
 
if s then
 
if s then
Ligne 97 : Ligne 84 :
  
 
---
 
---
-- determinationMois trouve le numéro du mois et son nom,
+
-- à partir d'un nom de mois (en français ou en anglais), de son numéro ou d'une abréviation,
-- à partir de son nom, de son numéro ou d'une expression mathématique.
+
-- retourne son nom canonique (exemple : "juin") et son numéro (exemple : 6)
-- Si le deuxième paramètre est vrai, les nombres supérieur à 12 ou non entiers sont acceptés. 
+
-- si non reconnu, retourne nil, nil
function fun.determinationMois( mois, mod, boucle )
+
function fun.determinationMois( mois )
local num, nom
+
local result
if tonumber( mois ) then
+
 
num = math.floor( tonumber( mois ) )
+
local num = tonumber( mois )
if mod then
+
if num then
-- si le nombre du mois est calculé par une exression, le résultat peut être supérieur à 12, ou inférieur à 1
+
result = listeMois[num]
num = math.fmod( num + 239, 12 ) + 1  -- +239 car fmod(-1) = -1 et non 11
+
else
elseif num < 1 or num > 12 then
+
local str = trim( mois )
num = nil
+
if str then
end
+
result = listeMois[str]
elseif trim( mois ) then
+
if not result then
nom, num = fun.valideMois( mois )
+
result = listeMois[mw.ustring.lower( str )]
if nom == nil and boucle == nil then
+
end
-- 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
 
end
 
end
if num and not nom then
+
 
nom = listeMois[ num ].nom
+
if result then
 +
return result.nom, result.num
 +
else
 +
return nil, nil
 
end
 
end
return nom, num
 
 
end
 
end
  
  
-- fonction interne à modeleDate, pour déterminer si on peut se passer de faire un ifexit
+
-- fonction interne à modeleDate, pour déterminer si on peut se passer de faire un ifexist
 
local function existDate( dataQualificatif, annee, mois )
 
local function existDate( dataQualificatif, annee, mois )
 
local data
 
local data
Ligne 137 : Ligne 123 :
 
return
 
return
 
end
 
end
-- le qualificatif est remplacer par celui de la base de donnée, ce qui permet des alias.
+
-- le qualificatif est remplacé par celui de la base de données, ce qui permet des alias.
local lien = annee  
+
local lien = annee
 
if dataQualificatif.qualificatif then
 
if dataQualificatif.qualificatif then
 
lien = lien .. ' ' .. dataQualificatif.qualificatif
 
lien = lien .. ' ' .. dataQualificatif.qualificatif
Ligne 149 : Ligne 135 :
 
local aucun = tonumber( data.aucun )
 
local aucun = tonumber( data.aucun )
 
if aucun and annee <= aucun then
 
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é
+
-- si 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
 
if type( data.seul ) == 'table' then
 
for i, v in ipairs( data.seul ) do
 
for i, v in ipairs( data.seul ) do
Ligne 166 : Ligne 152 :
 
end
 
end
 
end
 
end
-- l'annee n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe.
+
-- l'année n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe.
 
local cibleLien = mw.title.new( lien )
 
local cibleLien = mw.title.new( lien )
 
if cibleLien and cibleLien.exists then
 
if cibleLien and cibleLien.exists then
Ligne 179 : Ligne 165 :
 
local nomJour = { '[Ll]undi', '[Mm]ardi', '[Mm]ercredi', '[Jj]eudi', '[Vv]endredi',
 
local nomJour = { '[Ll]undi', '[Mm]ardi', '[Mm]ercredi', '[Jj]eudi', '[Vv]endredi',
 
'[Ss]amedi', '[Dd]imanche', '^ *[Ll]e' }
 
'[Ss]amedi', '[Dd]imanche', '^ *[Ll]e' }
local premier = { '<abbr class="abbr" title="[Pp]remier" ?>1<sup>er</sup></abbr>', '1<sup>er</sup>', '1er' }
+
local premier = { '<abbr class="abbr ?" title="[Pp]remier" ?>1<sup>er</sup></abbr>', '1<sup>er</sup>', '1er' }
 
for i, v in ipairs( nomJour ) do
 
for i, v in ipairs( nomJour ) do
 
jour = jour:gsub( v, '' )
 
jour = jour:gsub( v, '' )
Ligne 192 : Ligne 178 :
  
 
---
 
---
-- Sépare une chaine date en un table contenant les champs jour, mois et annee.
+
-- Sépare une chaine date en une table contenant les champs jour, mois et annee.
 
-- la date doit contenir le mois.
 
-- la date doit contenir le mois.
 
function fun.separationJourMoisAnnee( date )
 
function fun.separationJourMoisAnnee( date )
Ligne 202 : Ligne 188 :
 
local jour, mois, annee, masquerMois, masquerAnnee, separateur
 
local jour, mois, annee, masquerMois, masquerAnnee, separateur
 
-- variable pour construire les regex
 
-- variable pour construire les regex
local j = '([0-3]?%d)'     -- jour
+
local j = '([0-3]?%d)'                           -- jour
local m = '([01]?%d)'       -- mois numérique
+
local m = '([01]?%d)'                             -- mois numérique
local mmm = '([^%s%p%d]+[.]?)' -- mois en toute lettre
+
local mmm = '([^%s%p%d]+[.]?)'                   -- mois en toute lettre
local aj = '(%-?%d+)'       -- année ou jour
+
local mmm2 = '([^%s%p%d]+[.]?[-/][^%s%p%d]+[.]?)' -- mois-mois en toute lettre
local s = '[ ./-]+'         -- séparateur simple
+
local aj = '(%-?%d+)'                             -- année ou jour
local sep = '([ ./-]+)'     -- séparateur avec capture, pour le détecter deux fois
+
local s = '[ ./-]+'                               -- séparateur simple
local moins = '(%-?)'       -- signe moins pour signifier qu'il ne faut pas afficher cette donnée
+
local sep = '([ ./-]+)'                           -- séparateur avec capture, pour le détecter deux fois
local regexb = {
+
local moins = '(%-?)'                             -- signe moins pour signifier qu'il ne faut pas afficher cette donnée
jmmm = '^'..j..s..mmm..moins..'$',
+
 
mmmjva = '^'..mmm..s..j..', ?'..aj..'$',
 
}
 
 
 
date = fun.nettoyageJour( date )
 
date = fun.nettoyageJour( date )
 
-- suppression catégorie, liens, balises
 
-- suppression catégorie, liens, balises
date = mw.ustring.gsub( date, '%[%[[Cc]at[ée]gor[yi]e?:.-%]%]', '' )  
+
date = mw.ustring.gsub( date, '%[%[[Cc]at[ée]gor[yi]e?:.-%]%]', '' )
 
date = date :gsub( '%b<>', '' )
 
date = date :gsub( '%b<>', '' )
 
:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end )
 
:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end )
Ligne 237 : Ligne 220 :
 
-- réduction av. J-C pour simplifier un peu les regex :
 
-- réduction av. J-C pour simplifier un peu les regex :
 
:gsub( '(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?', '-%1' )
 
:gsub( '(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?', '-%1' )
-- supression de l'heure dans les date ISO
+
-- suppression de l'heure dans les dates ISO
 
:gsub( '^+?([%d-]*%d%d%-%d%d)T%d%d[%d:,.+-]*Z?$' , '%1')
 
:gsub( '^+?([%d-]*%d%d%-%d%d)T%d%d[%d:,.+-]*Z?$' , '%1')
+
 
 
-- test année seule
 
-- test année seule
 
if date:match( '^'..aj..'$' ) then
 
if date:match( '^'..aj..'$' ) then
 
annee = date:match( '^'..aj..'$' )
 
annee = date:match( '^'..aj..'$' )
 
elseif date:match( '^'..aj..s..aj..moins..'$' ) then
 
elseif date:match( '^'..aj..s..aj..moins..'$' ) then
-- jj/mm, mm/aaaa ou aaaa/mm  
+
-- jj/mm, mm/aaaa ou aaaa/mm
 
local a, separateur, b, sb = date:match( '^'..aj..sep..aj..moins..'$' )
 
local a, separateur, b, sb = date:match( '^'..aj..sep..aj..moins..'$' )
 
a, b = tonumber( a ), tonumber( b )
 
a, b = tonumber( a ), tonumber( b )
Ligne 255 : Ligne 238 :
 
return erreur( 'Date', date )
 
return erreur( 'Date', date )
 
elseif b < 1 or b > 31 then
 
elseif b < 1 or b > 31 then
mois, annee, masquerAnnee = a, b, sb  
+
mois, annee, masquerAnnee = a, b, sb
 
elseif a < 1 or a > 31 then
 
elseif a < 1 or a > 31 then
annee, mois = a, b
+
annee, mois = a, b
 
elseif b > 12 then
 
elseif b > 12 then
 
return erreur( 'Mois', b )
 
return erreur( 'Mois', b )
Ligne 265 : Ligne 248 :
 
elseif date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' ) then
 
elseif date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' ) then
 
-- jj/mm/aaaa ou aaaa/mm/jj
 
-- jj/mm/aaaa ou aaaa/mm/jj
jour, separateur, mois, masquerMois, annee, masquerAnnee = date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' )
+
jour, separateur, mois, masquerMois, annee, masquerAnnee = date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' )
 
if separateur == '-' and masquerMois == '-' and masquerAnnee == '' and tonumber( annee ) > 0 then
 
if separateur == '-' 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
 
-- date au format jj-mm--aaaa type 17-06--44 pour 17 juin 44 av. JC
Ligne 277 : Ligne 260 :
 
-- mmm aaaa
 
-- mmm aaaa
 
mois, separateur, annee, masquerAnnee = date:match( '^'..mmm..sep..aj..moins..'$' )
 
mois, separateur, annee, masquerAnnee = date:match( '^'..mmm..sep..aj..moins..'$' )
 +
if separateur:match( '^.+%-$' ) then
 +
annee = '-' .. annee
 +
end
 +
elseif date:match( '^'..mmm2..s..aj..moins..'$' ) then
 +
-- mmm-mmm aaaa
 +
mois, separateur, annee, masquerAnnee = date:match( '^'..mmm2..sep..aj..moins..'$' )
 
if separateur:match( '^.+%-$' ) then
 
if separateur:match( '^.+%-$' ) then
 
annee = '-' .. annee
 
annee = '-' .. annee
 
end
 
end
 
elseif date:match( '^'..j..s..mmm..moins..'$' ) then
 
elseif date:match( '^'..j..s..mmm..moins..'$' ) then
-- jj mmmm
+
-- jj mmm
 
jour, mois, masquerMois = date:match( '^'..j..s..mmm..moins..'$' )
 
jour, mois, masquerMois = date:match( '^'..j..s..mmm..moins..'$' )
 
elseif date:match( '^'..mmm..s..j..', ?'..aj..'$') then
 
elseif date:match( '^'..mmm..s..j..', ?'..aj..'$') then
Ligne 299 : Ligne 288 :
 
jour = temp
 
jour = temp
 
end
 
end
+
 
return fun.validationJourMoisAnnee{  
+
return fun.validationJourMoisAnnee{
jour, mois, annee,  
+
jour, mois, annee,
masquerAnnee = trim( masquerAnnee ) and true or nil,  
+
masquerAnnee = trim( masquerAnnee ) and true or nil,
masquerMois = ( trim( masquerAnnee ) or not annee ) and trim( masquerMois ) and true or nil,  
+
masquerMois = ( trim( masquerAnnee ) or not annee ) and trim( masquerMois ) and true or nil,
 
-- or nil sert juste à éviter de trainer une valeur false dans tous les tests unitaires.
 
-- or nil sert juste à éviter de trainer une valeur false dans tous les tests unitaires.
 
}
 
}
else  
+
else
 
return true, {}
 
return true, {}
 
end
 
end
Ligne 313 : Ligne 302 :
  
 
---
 
---
-- validationJourMoisAnnee vérifie les paramètres correspondent à une date valide.
+
-- validationJourMoisAnnee vérifie que les paramètres correspondent à une date valide.
-- la date peut être dans les paramètre 1 à 3, ou dans des paramètres jour, mois et annee.
+
-- la date peut être dans les paramètres 1 à 3, ou dans des paramètres jour, mois et annee.
-- La fonction retourne true suivit d'une table avec la date en paramètres nommé (sans accent sur année)
+
-- La fonction retourne true suivi d'une table avec la date en paramètres nommés (sans accent sur année)
-- ou false suivit d'un message d'erreur.
+
-- ou false suivi d'un message d'erreur.
function fun.validationJourMoisAnnee( frame, ... )
+
function fun.validationJourMoisAnnee( frame )
local args = Outils.extractArgs( frame, ... )
+
local args = Outils.extractArgs( frame )
 
local jour, mois, numMois, annee
 
local jour, mois, numMois, annee
 
local bjour = args[1] or args['jour'] or ''
 
local bjour = args[1] or args['jour'] or ''
 
local bmois = tostring( args[2] or args['mois'] or '' )
 
local bmois = tostring( args[2] or args['mois'] or '' )
 
local bannee = args[3] or args['annee'] or args['année'] or ''
 
local bannee = args[3] or args['annee'] or args['année'] or ''
+
 
 
local function erreur( periode, valeur )
 
local function erreur( periode, valeur )
 
return false, '<span class="error">' .. periode .. ' invalide (' .. valeur .. ')</span>'
 
return false, '<span class="error">' .. periode .. ' invalide (' .. valeur .. ')</span>'
 
end
 
end
+
 
 
-- on traite l'année
 
-- on traite l'année
 
if Outils.notEmpty( bannee ) then
 
if Outils.notEmpty( bannee ) then
 
annee = tonumber( bannee )
 
annee = tonumber( bannee )
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 = string.match( string.upper( bannee ), '^(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?' )
+
annee = bannee:upper():match( '^(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?' )
 
annee = tonumber( annee )
 
annee = tonumber( annee )
 
if annee then
 
if annee then
Ligne 346 : Ligne 335 :
 
annee = nil
 
annee = nil
 
end
 
end
+
 
 
-- on traite le mois
 
-- on traite le mois
 
if Outils.notEmpty( bmois ) then
 
if Outils.notEmpty( bmois ) then
 
mois, numMois = fun.determinationMois( bmois )
 
mois, numMois = fun.determinationMois( bmois )
 
if mois == nil then
 
if mois == nil then
mois = fun.valideSaison( bmois )
+
mois = fun.determinationSaison( bmois )
 
if mois == nil then
 
if mois == nil then
return erreur( 'Mois', bmois )
+
local mois1, sep, mois2 = bmois:match( '^([^%s%p%d]+[.]?)([-/])([^%s%p%d]+[.]?)$' )
 +
if mois1 then
 +
mois1 = fun.determinationMois( mois1 )
 +
mois2 = fun.determinationMois( mois2 )
 +
if mois1 == nil or mois2 == nil then
 +
return erreur( 'Mois', bmois )
 +
end
 +
mois = mois1 .. sep .. mois2
 +
else
 +
return erreur( 'Mois', bmois )
 +
end
 +
end
 +
end
 +
-- on traite le jour si présent
 +
if Outils.notEmpty( bjour ) then
 +
if not numMois then
 +
erreur( 'Date', 'jour avec saison ou plusieurs mois' )
 +
end
 +
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 > listeMois[numMois].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
 
end
else
+
else
-- on traite le jour si présent
+
-- S'il n'y a pas de jour on regarde si la première lettre du mois est en majuscule
if Outils.notEmpty( bjour ) then
+
if bmois:match( '^%u' ) then
jour = tonumber( bjour )
+
-- oui, on passe la première lettre en majuscule
if jour == nil then
+
mois = ucfirst( mois )
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 > listeMois[numMois].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
 +
-- s'il n'y a pas d'année non plus on retourne le mois simple
 
end
 
end
 
else
 
else
Ligne 402 : Ligne 403 :
 
end
 
end
 
end
 
end
end
+
end
 
end
 
end
+
 
 
-- vérification de l'absence d'un décalage
 
-- vérification de l'absence d'un décalage
if annee and annee < 13 and annee > 0 and not jour and ( tonumber( bmois ) or (not mois and tonumber( args[4] ) ) ) then
+
if annee and annee < 13 and annee > 0 and not jour and ( tonumber( bmois ) or ( not mois and tonumber( args[4] ) ) ) then
 
return false, '<span class="error">année improbable (' .. annee .. ')</span>'
 
return false, '<span class="error">année improbable (' .. annee .. ')</span>'
 
end
 
end
+
 
 
local resultat = {
 
local resultat = {
 
jour = jour,
 
jour = jour,
Ligne 415 : Ligne 416 :
 
numMois = numMois,
 
numMois = numMois,
 
annee = annee,
 
annee = annee,
masquerAnnee = args.masquerAnnee,  
+
masquerAnnee = args.masquerAnnee,
 
masquerMois = args.masquerMois,
 
masquerMois = args.masquerMois,
 
}
 
}
Ligne 431 : Ligne 432 :
 
-- julien : date dans le calendrier julien
 
-- julien : date dans le calendrier julien
 
-- compact : affiche le mois sous forme d'abréviation
 
-- compact : affiche le mois sous forme d'abréviation
-- avJC : non pour désactiver l'affichage de « av. J.-C. » pour les date négatives
+
-- avJC : non pour désactiver l'affichage de « av. J.-C. » pour les dates négatives
-- âge : ajout la durée depuis cette date
+
-- âge : ajoute la durée depuis cette date
-- nolink : ne met pas de lien sur la date
+
-- nolinks : ne met pas de lien sur la date
 +
-- onerror : en cas d'erreur, valeur à retourner à la place du message d'erreur ; la valeur spéciale "input" fait retourner le 1er argument inchangé
 
-- naissance : ajoute la class "bday"
 
-- naissance : ajoute la class "bday"
 
-- mort : ajoute la class "dday"
 
-- mort : ajoute la class "dday"
Ligne 439 : Ligne 441 :
 
local args = Outils.extractArgs( frame )
 
local args = Outils.extractArgs( frame )
 
local cat, resultat = ''
 
local cat, resultat = ''
+
 
 +
local dateNaissanceMort
 +
 
 
-- analyse des paramètres non nommés (ou paramètres de la date jour, mois, annee)
 
-- analyse des paramètres non nommés (ou paramètres de la date jour, mois, annee)
 
local test, params
 
local test, params
Ligne 447 : Ligne 451 :
 
test, params = fun.separationJourMoisAnnee( arg1 )
 
test, params = fun.separationJourMoisAnnee( arg1 )
 
if test then
 
if test then
params.qualificatif = arg2
+
dateNaissanceMort = trim( arg2 )
 +
params.qualificatif = trim( arg2 )
 +
end
 +
elseif type( arg1 ) == 'string' and type( arg2 ) == 'string' and arg3 ~= nil and arg4 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or dataLiens[arg3] or mw.ustring.match( arg3, '%a %a' ) ) then
 +
-- la date est dans le premier paramètre
 +
test, params = fun.separationJourMoisAnnee( arg1 )
 +
if test then
 +
dateNaissanceMort = trim( arg2 )
 +
params.qualificatif = trim( arg3 )
 
end
 
end
 
else
 
else
Ligne 461 : Ligne 473 :
 
cleanArgs[2], cleanArgs.masquerMois = masquerParam( args[2] or args.mois )
 
cleanArgs[2], cleanArgs.masquerMois = masquerParam( args[2] or args.mois )
 
cleanArgs[3], cleanArgs.masquerAnnee = masquerParam( args[3] or args.annee or args['annee'] )
 
cleanArgs[3], cleanArgs.masquerAnnee = masquerParam( args[3] or args.annee or args['annee'] )
+
 
 
test, params = fun.validationJourMoisAnnee( cleanArgs )
 
test, params = fun.validationJourMoisAnnee( cleanArgs )
 
if test then
 
if test then
Ligne 467 : Ligne 479 :
 
end
 
end
 
end
 
end
+
 
 
-- analyse des paramètres nommés
 
-- analyse des paramètres nommés
 
if test then
 
if test then
 
local Yesno = require 'Module:Yesno'
 
local Yesno = require 'Module:Yesno'
params.qualificatif = params.qualificatif or args.qualificatif
+
 
-- julien peut avoir trois valeurs: inactif, format standard (true), format court
+
if args.qualificatif and args.qualificatif ~= '' then
 +
params.qualificatif = args.qualificatif
 +
end
 +
 
 +
-- julien peut avoir trois valeurs : inactif, format standard (true), format court
 
params.julien = Yesno( args.julien, 'court', false )
 
params.julien = Yesno( args.julien, 'court', false )
 
params.avJC = Yesno( args.avJC )
 
params.avJC = Yesno( args.avJC )
params.republicain = Yesno( args['républicain'], 'liens', false)
+
 
+
if args['républicain'] and args['républicain'] ~= '' then
 +
if args['républicain'] == 'liens' then
 +
params.republicain = 'liens'
 +
else
 +
params.republicain = Yesno( args['républicain'], false )
 +
end
 +
else
 +
params.republicain = false
 +
end
 +
 
 +
if args.dateNaissanceMort and args.dateNaissanceMort ~= '' then
 +
dateNaissanceMort = args.dateNaissanceMort
 +
end
 +
 
 +
if dateNaissanceMort then
 +
local testNaissanceMort, paramsNaissanceMort = fun.separationJourMoisAnnee( dateNaissanceMort )
 +
if testNaissanceMort then
 +
params.anneeNaissanceMort, params.moisNaissanceMort, params.numMoisNaissanceMort, params.jourNaissanceMort = paramsNaissanceMort.annee, paramsNaissanceMort.mois, paramsNaissanceMort.numMois, paramsNaissanceMort.jour
 +
end
 +
end
 +
 
 
local listeParam = {
 
local listeParam = {
 
age = 'âge',
 
age = 'âge',
Ligne 491 : Ligne 527 :
 
params[v] = params[v] or Yesno( args[n], true, false ) or nil
 
params[v] = params[v] or Yesno( args[n], true, false ) or nil
 
end
 
end
+
 
-- sortie pour les tests unitaire, ou pour débuger
+
-- sortie pour les tests unitaire, ou pour débugger
 
if args.debug then
 
if args.debug then
 
return params
 
return params
 
end
 
end
+
 
 
resultat = fun._modeleDate( params )
 
resultat = fun._modeleDate( params )
 +
 +
elseif args.onerror then
 +
if args.onerror == 'input' then
 +
return args[1]
 +
else
 +
return args.onerror
 +
end
  
 
else
 
else
 
local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
 
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
+
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]]'
 
cat = '[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée]]'
 
end
 
end
 
resultat = params .. cat
 
resultat = params .. cat
 
end
 
end
+
 
 
return resultat or ''
 
return resultat or ''
 
end
 
end
Ligne 513 : Ligne 556 :
 
local annee, mois, numMois, jour = args.annee, args.mois, args.numMois, args.jour
 
local annee, mois, numMois, jour = args.annee, args.mois, args.numMois, args.jour
 
local qualificatif = args.qualificatif
 
local qualificatif = args.qualificatif
+
 
 
if ( annee or mois or jour ) == nil then
 
if ( annee or mois or jour ) == nil then
 
return
 
return
 
end
 
end
+
 
-- on traite l'age, naissance et mort
+
-- on traite l'âge, naissance et mort
local age = args['âge'] and fun.age( annee, numMois, jour )
+
local agePrefix = ''
 +
local age = args['âge'] and fun.age( annee, numMois, jour )
 
local naissance = args.naissance
 
local naissance = args.naissance
 
local mort = args.mort
 
local mort = args.mort
+
if mort and args.anneeNaissanceMort then
 +
age = fun.age( args.anneeNaissanceMort, args.numMoisNaissanceMort, args.jourNaissanceMort, annee, numMois, jour )
 +
agePrefix = 'à '
 +
end
 +
 
 
-- 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 = annee, mois, jour      -- date suivant le calendrier julien si necessaire
 
local jannee, jmois, jjour = annee, mois, jour      -- date suivant le calendrier julien si necessaire
local julienDate, julienSup, julienSep              -- servira éventuellement à a afficher la date selon le calendrier julien
+
local julienDate, julienSup, julienSep              -- servira éventuellement à afficher la date selon le calendrier julien
local gregAprMois, gregAprAn, gregFin              -- message de calendrier gégorien lorsque la date est selon le calendrier julien
+
local gregAprMois, gregAprAn, gregFin              -- message de calendrier grégorien lorsque la date est selon le calendrier julien
 
if annee and jour then
 
if annee and jour then
 
local amj = annee * 10000 + numMois * 100 + jour
 
local amj = annee * 10000 + numMois * 100 + jour
Ligne 538 : Ligne 586 :
 
end
 
end
 
args.julien = false
 
args.julien = false
+
 
 
elseif args.julien then
 
elseif args.julien then
 
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
 
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
Ligne 546 : Ligne 594 :
 
end
 
end
 
if args.compact then
 
if args.compact then
jmois = listeMois[ jmois ].abrev
+
jmois = listeMois[jmois].abrev
 
end
 
end
 
if args.julien == 'court' then
 
if args.julien == 'court' then
Ligne 563 : Ligne 611 :
 
gregFin = ' [[Passage du calendrier julien au calendrier grégorien|dans le calendrier grégorien]])'
 
gregFin = ' [[Passage du calendrier julien au calendrier grégorien|dans le calendrier grégorien]])'
 
end
 
end
+
 
 
elseif args.republicain then
 
elseif args.republicain then
 
local DateRep = require 'Module:Date républicaine'
 
local DateRep = require 'Module:Date républicaine'
 +
local RepSansLiens
 
if args.republicain == 'liens' then
 
if args.republicain == 'liens' then
 
RepSansLiens = false
 
RepSansLiens = false
Ligne 572 : Ligne 621 :
 
end
 
end
 
  dateRepublicaine = DateRep._date_republicaine(
 
  dateRepublicaine = DateRep._date_republicaine(
  RepSansLiens,  
+
  RepSansLiens,
 
  { fun.formatRepCal( fun.do_toRepCal{gannee, gmois, gjour} ) }
 
  { fun.formatRepCal( fun.do_toRepCal{gannee, gmois, gjour} ) }
 
  )
 
  )
Ligne 581 : Ligne 630 :
 
end
 
end
 
args.julien = false
 
args.julien = false
args.republicain=false
+
args.republicain = false
 
end
 
end
+
 
 
-- on génère le résultat
 
-- on génère le résultat
+
 
 
-- Déclarations des variables
 
-- Déclarations des variables
local wikiListe = {}                  -- reçois le texte affiché pour chaque paramètre
+
local wikiListe = {}                  -- reçoit le texte affiché pour chaque paramètre
local iso = {}                        -- reçois le format date ISO de ce paramètre
+
local iso = {}                        -- reçoit le format date ISO de ce paramètre
 
local texteMois = mois                -- texte du mois qui sera affiché (éventuellement l'abréviation)
 
local texteMois = mois                -- texte du mois qui sera affiché (éventuellement l'abréviation)
 
if args.compact then
 
if args.compact then
if args.nolinks then
+
if not numMois then
texteMois = '<abbr class=abbr title="' .. mois .. '">' .. listeMois[ mois ].abrev .. '</abbr>'
+
-- mois est autre chose qu'un simple mois : saison, mois-mois... auquel cas, pas d'abréviation (provoquait erreur Lua)
 +
-- (les abréviations pour le cas "mois[-/]mois" seraient théoriquement possibles, mais ça reste à implémenter)
 
else
 
else
texteMois = listeMois[ mois ].abrev
+
if args.nolinks then
 +
texteMois = '<abbr class=abbr title="' .. mois .. '">' .. listeMois[mois].abrev .. '</abbr>'
 +
else
 +
texteMois = listeMois[mois].abrev
 +
end
 
end
 
end
 
end
 
end
 
mois = mois and mois:gsub( 'aout', 'août' )
 
mois = mois and mois:gsub( 'aout', 'août' )
+
 
 
local dataQualificatif, dataCat
 
local dataQualificatif, dataCat
 
if not args.nolinks then
 
if not args.nolinks then
 
dataQualificatif = dataLiens[qualificatif or '']
 
dataQualificatif = dataLiens[qualificatif or '']
 
if type( dataQualificatif ) ~= 'table' then
 
if type( dataQualificatif ) ~= 'table' then
-- si le qualifiquatif n'est pas dans la base de donnée, on crée une table minimum,
+
-- si le qualificatif n'est pas dans la base de données, 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
+
-- qui imposera un test sur l'année, mais considère qu'il n'y a pas de lien sur le jour ou le mois
 
dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } }
 
dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } }
 
end
 
end
Ligne 613 : Ligne 667 :
 
end
 
end
 
local function wikiLien( lien, texte )
 
local function wikiLien( lien, texte )
if lien == texte then  
+
if lien == texte then
 
return '[[' .. texte .. ']]'
 
return '[[' .. texte .. ']]'
 
else
 
else
 
return '[[' .. lien .. '|' .. texte .. ']]'
 
return '[[' .. lien .. '|' .. texte .. ']]'
end
+
end
 
end
 
end
+
 
  
 
-- le jour si présent
 
-- le jour si présent
Ligne 629 : Ligne 683 :
 
jour = modelePremier
 
jour = modelePremier
 
end
 
end
table.insert( wikiListe, jour )
+
table.insert( wikiListe, jour )
 
else
 
else
 
qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif
 
qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif
Ligne 640 : Ligne 694 :
 
end
 
end
 
-- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour.
 
-- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour.
table.insert( wikiListe, wikiLien( lien, jour ) )
+
table.insert( wikiListe, wikiLien( lien, jour ) )
table.insert( wikiListe, wikiLien( lien, jour .. ' '.. texteMois ) )
+
table.insert( wikiListe, wikiLien( lien, jour .. ' '.. texteMois ) )
 
end
 
end
table.insert( iso, 1, string.sub( '0' .. gjour, -2 ) )
+
table.insert( iso, 1, string.sub( '0' .. gjour, -2 ) )
 
end
 
end
+
 
 
-- le mois
 
-- le mois
 
if mois then
 
if mois then
Ligne 652 : Ligne 706 :
 
end
 
end
 
if args.nolinks then
 
if args.nolinks then
if not args.masquerMois then  
+
if not args.masquerMois then
table.insert( wikiListe, texteMois )
+
table.insert( wikiListe, texteMois )
 
end
 
end
 
else
 
else
 
local lien
 
local lien
 
if annee then
 
if annee then
lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois )
+
if not numMois then
if lien == nil and qualificatif and qualifJour == '' then
+
-- mois est autre chose qu'un simple mois : saison, mois-mois... auquel cas, pas de lien
-- test nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif.
+
else
lien = existDate( dataLiens[''], annee, mois )
+
lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois )
 +
if lien == nil and qualificatif and qualifJour == '' then
 +
-- 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
 
end
 
end
Ligne 668 : Ligne 726 :
 
table.remove( wikiListe )
 
table.remove( wikiListe )
 
if not args.masquerMois then
 
if not args.masquerMois then
table.insert( wikiListe, wikiLien( lien, texteMois ) )
+
table.insert( wikiListe, wikiLien( lien, texteMois ) )
 
end
 
end
 
elseif #wikiListe > 0 then
 
elseif #wikiListe > 0 then
Ligne 675 : Ligne 733 :
 
elseif args.masquerAnnee then
 
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.
 
-- s'il n'y a pas de jour et que l'année n'est pas affichée, on insère le mois seul.
table.insert( wikiListe, texteMois )
+
table.insert( wikiListe, texteMois )
 
end
 
end
 
end
 
end
 
if gmois then
 
if gmois then
table.insert( iso, 1, string.sub( '0' .. gmois, -2 ) )
+
table.insert( iso, 1, string.sub( '0' .. gmois, -2 ) )
 
end
 
end
 
table.insert( wikiListe, gregAprMois )
 
table.insert( wikiListe, gregAprMois )
 
end
 
end
+
 
 
-- l'année
 
-- l'année
 
if annee and not (args.julien == true and args.nolinks and jannee == annee ) then
 
if annee and not (args.julien == true and args.nolinks and jannee == annee ) then
Ligne 702 : Ligne 760 :
 
.. texteAnnee .. ' après Jésus-Christ">apr. J.-C.</abbr>'
 
.. texteAnnee .. ' après Jésus-Christ">apr. J.-C.</abbr>'
 
end
 
end
if args.nolinks then -- seulement si on doit l'affichée
+
if args.nolinks then -- seulement si on doit l'afficher
table.insert( wikiListe, texteAnnee )
+
table.insert( wikiListe, texteAnnee )
 
else
 
else
lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee ) or lien or annee
+
lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee ) or lien or annee
 
if mois and #wikiListe == 0 then
 
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.
 
-- si le mois n'a pas de lien et n'est pas affiché avec le jour, il est affiché avec l'année.
 
texteAnnee = texteMois .. ' ' .. texteAnnee
 
texteAnnee = texteMois .. ' ' .. texteAnnee
 
end
 
end
table.insert( wikiListe, wikiLien( lien, texteAnnee ) )
+
table.insert( wikiListe, wikiLien( lien, texteAnnee ) )
 
end
 
end
 
end
 
end
Ligne 716 : Ligne 774 :
 
if annee then
 
if annee then
 
if gannee > 999 then
 
if gannee > 999 then
table.insert( iso, 1, gannee )
+
table.insert( iso, 1, gannee )
 
elseif gannee > -1 then
 
elseif gannee > -1 then
table.insert( iso, 1, string.sub( '000' .. gannee , -4 ) )
+
table.insert( iso, 1, string.sub( '000' .. gannee , -4 ) )
 
elseif gannee > -999 then
 
elseif gannee > -999 then
 
-- calendrier grégorien proleptique avec année 0.
 
-- calendrier grégorien proleptique avec année 0.
table.insert( iso, 1, 'U-' .. string.sub( '000' .. ( 0 - gannee ), -4 ) )
+
table.insert( iso, 1, 'U-' .. string.sub( '000' .. ( 0 - gannee ), -4 ) )
 
else
 
else
table.insert( iso, 1, 'U' .. gannee )
+
table.insert( iso, 1, 'U' .. gannee )
 
end
 
end
 
end
 
end
Ligne 731 : Ligne 789 :
 
if type( age ) == 'number' and age >= 0 and ( not naissance or age < 120 ) 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\194\160an)'
+
age = '(' .. agePrefix .. 'moins d’un\194\160an)'
 
elseif age == 1 then
 
elseif age == 1 then
age = '(1\194\160an)'
+
age = '(' .. agePrefix .. '1\194\160an)'
 
else
 
else
age = '(' .. age .. '\194\160ans)'
+
age = '('.. agePrefix .. age .. '\194\160ans)'
 
end
 
end
 
else
 
else
 
age = false
 
age = false
 
end
 
end
+
 
+
 
 
-- compilation du résultat
 
-- compilation du résultat
 
local wikiTexte = table.concat( wikiListe, ' ' )
 
local wikiTexte = table.concat( wikiListe, ' ' )
 
local isoTexte = table.concat( iso, '-' )
 
local isoTexte = table.concat( iso, '-' )
+
 
 
-- On ajoute un peu de sémantique.
 
-- On ajoute un peu de sémantique.
 
local wikiHtml = mw.html.create( '' )
 
local wikiHtml = mw.html.create( '' )
+
 
 
if julienDate then
 
if julienDate then
 
wikiHtml:tag( 'span')
 
wikiHtml:tag( 'span')
 
:addClass( 'nowrap' )
 
:addClass( 'nowrap' )
 
:attr( 'data-sort-value', isoTexte )
 
:attr( 'data-sort-value', isoTexte )
:wikitext( julienDate )
+
:wikitext( julienDate )
 
:node( julienSup )
 
:node( julienSup )
 
:done()
 
:done()
 
:wikitext( julienSep )
 
:wikitext( julienSep )
 
end
 
end
+
 
 
local dateHtml = wikiHtml:tag( 'time' )
 
local dateHtml = wikiHtml:tag( 'time' )
 
:wikitext( wikiTexte )
 
:wikitext( wikiTexte )
Ligne 771 : Ligne 829 :
 
dateHtml:addClass( 'date-lien' )
 
dateHtml:addClass( 'date-lien' )
 
end
 
end
if naissance then  
+
if naissance then
 
dateHtml:addClass( 'bday' )
 
dateHtml:addClass( 'bday' )
 
elseif mort then
 
elseif mort then
 
dateHtml:addClass( 'dday' )
 
dateHtml:addClass( 'dday' )
 
end
 
end
+
 
 
wikiHtml:wikitext( gregFin )
 
wikiHtml:wikitext( gregFin )
+
 
 
if args.republicain then
 
if args.republicain then
 
wikiHtml:wikitext( ' (', dateRepublicaine, ')' )
 
wikiHtml:wikitext( ' (', dateRepublicaine, ')' )
 
end
 
end
+
 
 
if age then
 
if age then
 
wikiHtml:wikitext( ' ' )
 
wikiHtml:wikitext( ' ' )
Ligne 790 : Ligne 848 :
 
:done()
 
:done()
 
end
 
end
+
 
 
return tostring( wikiHtml )
 
return tostring( wikiHtml )
 
end
 
end
Ligne 797 : Ligne 855 :
 
---
 
---
 
-- fonction destinée aux infobox, notamment pour afficher les dates de naissance et de mort
 
-- 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
+
-- les liens présent dans les dates fournies sont automatiquement supprimés pour gérer les cas
 
-- le paramètre contient déjà un modèle date.
 
-- le paramètre contient déjà un modèle date.
 
-- Paramètres :
 
-- Paramètres :
Ligne 812 : Ligne 870 :
 
return
 
return
 
end
 
end
+
 
-- analyseDate sépare la date du contenu qui suit, supprime les liens, et retourne si possible un table avec jour mois année
+
-- analyseDate sépare la date du contenu qui suit, supprime les liens, et retourne si possible une table avec jour mois année
 
local function analyseDate( d )
 
local function analyseDate( d )
 
if trim( d ) then
 
if trim( d ) then
 
local analyse = d:match( ' ou ') or d:match( 'entre ' ) or d:match( 'vers ' ) or d:match( 'après ' ) or d:match( 'avant ' )
 
local analyse = d:match( ' ou ') or d:match( 'entre ' ) or d:match( 'vers ' ) or d:match( 'après ' ) or d:match( 'avant ' )
if analyse then  
+
if analyse then
 
return d
 
return d
 
end
 
end
Ligne 828 : Ligne 886 :
 
end
 
end
 
analyse = debut or analyse
 
analyse = debut or analyse
-- supprime les lien
+
-- supprime les liens
 
analyse = analyse:gsub(
 
analyse = analyse:gsub(
 
'%[%[([^%[%]|]*)|?([^%[%]]*)%]%]',
 
'%[%[([^%[%]|]*)|?([^%[%]]*)%]%]',
Ligne 856 : Ligne 914 :
 
return dateString
 
return dateString
 
end
 
end
+
 
 
local naissance = args[1]:match( '^n' ) == 'n'
 
local naissance = args[1]:match( '^n' ) == 'n'
 
local mort = args[1]:match( '^m' ) or args[1]:match( 'décès' )
 
local mort = args[1]:match( '^m' ) or args[1]:match( 'décès' )
Ligne 869 : Ligne 927 :
 
end
 
end
 
if affichageDate:match( '</time>' ) then
 
if affichageDate:match( '</time>' ) then
-- S'il y a des liens il y a probablement déjà un modèle date, évitons de l'exècuter une 2e fois
+
-- 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 ( naissance or mort ) and ( affichageDate:match( 'wikidata%-linkback' )) then
+
if ( naissance or mort ) and ( affichageDate:match( 'wikidata%-linkback' )) then
 
dateNaissance = analyseDate( args[2] )
 
dateNaissance = analyseDate( args[2] )
 
dateMort = analyseDate( args[3] )
 
dateMort = analyseDate( args[3] )
 
resultatDate = affichageDate
 
resultatDate = affichageDate
else  
+
else
 
return prefix( affichageDate )
 
return prefix( affichageDate )
 
end
 
end
 
else
 
else
 
affichageDateTab, complementDate = analyseDate( affichageDate )
 
affichageDateTab, complementDate = analyseDate( affichageDate )
if type( affichageDateTab ) ~= 'table' then  
+
if type( affichageDateTab ) ~= 'table' then
 
return affichageDateTab
 
return affichageDateTab
 
else
 
else
Ligne 900 : Ligne 958 :
 
end
 
end
 
resultatDate = resultatDate or fun.modeleDate( affichageDateTab )
 
resultatDate = resultatDate or fun.modeleDate( affichageDateTab )
+
 
 
local age, prefixAge, suffixAge, calculAge = '', ' <span class="noprint">(', ')</span>', nil
 
local age, prefixAge, suffixAge, calculAge = '', ' <span class="noprint">(', ')</span>', nil
 
if naissance and
 
if naissance and
Ligne 940 : Ligne 998 :
 
end
 
end
 
end
 
end
+
 
 
return prefix( resultatDate ) .. ( complementDate or '' ) .. age
 
return prefix( resultatDate ) .. ( complementDate or '' ) .. age
 
end
 
end
Ligne 948 : Ligne 1 006 :
 
-- la fonction dateISO renvoie un date au format aaaa-mm-jj (sans liens)
 
-- 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]]
 
-- 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 mois peut être en lettres ou en chiffres
 
-- le jour peut être sous la forme '05', '{{1er}}' ou 'vendredi 13'
 
-- le jour peut être sous la forme '05', '{{1er}}' ou 'vendredi 13'
 
function fun.dateISO( frame )
 
function fun.dateISO( frame )
Ligne 956 : Ligne 1 014 :
 
if type( annee ) == 'string' then
 
if type( annee ) == 'string' then
 
annee = ( tonumber( annee ) -- match '2013'
 
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)%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 '17 septembre 2013'
 
or string.match ( annee, '^(%d%d%d%d)%D' )  -- match '2013-09-17'
 
or string.match ( annee, '^(%d%d%d%d)%D' )  -- match '2013-09-17'
Ligne 962 : Ligne 1 020 :
 
end
 
end
 
annee = tonumber( annee )
 
annee = tonumber( annee )
+
 
 
-- le format de date iso est défini suivant le calendrier grégorien.
 
-- 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,
 
-- Avant l'année 1583 la date est calendrier est probablement du calendrier julien,
 
-- donc autant s'abstenir.
 
-- donc autant s'abstenir.
if annee and annee > 1582 then
+
if annee and annee > 1582 then
 
local mois = Outils.notEmpty( args.mois, args.month )
 
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é.
 
-- num mois trouve le numéro du mois, qu'il soit numérique ou texte, complet ou abrégé.
Ligne 972 : Ligne 1 030 :
 
if numMois then
 
if numMois then
 
mois = '-' .. string.sub( '0' .. numMois, -2 )
 
mois = '-' .. string.sub( '0' .. numMois, -2 )
+
 
 
local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
 
local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
 
if type( jour ) == 'string' then
 
if type( jour ) == 'string' then
Ligne 999 : Ligne 1 057 :
 
-- Rangs des premiers des mois
 
-- Rangs des premiers des mois
 
local ranks = {0,31,59,90,120,151,181,212,243,273,304,334}
 
local ranks = {0,31,59,90,120,151,181,212,243,273,304,334}
+
 
 
local rank = (ranks[mt] or 0) + dy - 1
 
local rank = (ranks[mt] or 0) + dy - 1
 
if(fun.isLeapYear(yr) and (mt >= 3)) then
 
if(fun.isLeapYear(yr) and (mt >= 3)) then
Ligne 1 012 : Ligne 1 070 :
 
local yr1 = tonumber(arguments[1]) or 0
 
local yr1 = tonumber(arguments[1]) or 0
 
local yr2 = tonumber(arguments[2]) or 0
 
local yr2 = tonumber(arguments[2]) or 0
+
 
 
return fun.daysSinceOrigin(yr2) - fun.daysSinceOrigin(yr1)
 
return fun.daysSinceOrigin(yr2) - fun.daysSinceOrigin(yr1)
 
end
 
end
Ligne 1 074 : Ligne 1 132 :
 
function fun.formatRepCal(arguments)
 
function fun.formatRepCal(arguments)
 
local months = {"Vendémiaire","Brumaire","Frimaire","Nivôse","Pluviôse","Ventôse","Germinal","Floréal","Prairial","Messidor","Thermidor","Fructidor"}
 
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 extras = {"de la vertu","du génie","du travail","des récompenses","de l'opinion","de la Révolution"}
 
local result = ""
 
local result = ""
 
if(arguments[2] < 13) then
 
if(arguments[2] < 13) then
Ligne 1 087 : Ligne 1 145 :
 
---
 
---
 
-- Voir Modèle:Âge
 
-- Voir Modèle:Âge
-- retourne l'age en fonction de la ou les dates fournies. La valeur retounée est de type 'number'
+
-- retourne l'âge en fonction de la ou les dates fournies. La valeur retournée est de type 'number'
-- Parammètres :
+
-- Paramètres :
 
-- 1, 2, 3 : année, mois jour de naissance (supposé dans le calendrier grégorien)
 
-- 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).
+
-- 4, 5, 6 : année, mois, jour du calcul (facultatif, par défaut la date UTC courante).
 
function fun.age( an, mn, jn, ac, mc, jc )
 
function fun.age( an, mn, jn, ac, mc, jc )
 
if ac == nil then
 
if ac == nil then
Ligne 1 112 : Ligne 1 170 :
 
return
 
return
 
end
 
end
+
 
 
local age = ac - an
 
local age = ac - an
 
if mc == mn then
 
if mc == mn then
Ligne 1 125 : Ligne 1 183 :
  
 
function fun.modeleAge( frame )
 
function fun.modeleAge( frame )
local args = frame.getParent().args
+
local args = frame:getParent().args
 
local age = fun.age (
 
local age = fun.age (
 
args[1] or args['année'],
 
args[1] or args['année'],
Ligne 1 174 : Ligne 1 232 :
 
local sinceYear = sinceCentury - math.floor( nYear * 1461 / 4 )
 
local sinceYear = sinceCentury - math.floor( nYear * 1461 / 4 )
 
local nMonth = math.floor( ( sinceYear * 5 + 2 ) / 153 )
 
local nMonth = math.floor( ( sinceYear * 5 + 2 ) / 153 )
+
 
local day = sinceYear - math.floor( ( nMonth * 153 + 2 ) / 5 ) + 1
+
local day = sinceYear - math.floor( ( nMonth * 153 + 2 ) / 5 ) + 1
local month = nMonth - math.floor( nMonth / 10 ) * 12 + 3
+
local month = nMonth - math.floor( nMonth / 10 ) * 12 + 3
 
local year = math.floor( sinceYear / 306 ) + nYear + 100 * nCentury - 4800
 
local year = math.floor( sinceYear / 306 ) + nYear + 100 * nCentury - 4800
+
 
 
return year, month, day
 
return year, month, day
 
end
 
end
Ligne 1 184 : Ligne 1 242 :
 
---
 
---
 
-- calcul d'une date dans le calendrier julien à partir du jour julien
 
-- 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)
+
-- calcul basé sur l'algorithme de la page fr.wikipedia.org/wiki/Jour_julien (1/10/2013)
 
function fun.julianDayToJulian( julianDay )
 
function fun.julianDayToJulian( julianDay )
 
local year = math.modf( ( julianDay * 4 - 6884469 ) / 1461 )
 
local year = math.modf( ( julianDay * 4 - 6884469 ) / 1461 )
Ligne 1 217 : Ligne 1 275 :
 
     c'est l'heure d'été ou l'heure d'hiver.
 
     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
 
   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
 
   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
+
     retourne ce même texte avec un wikilien vers les articles correspondants
 
--]]
 
--]]
 
function fun.CEST(frame)
 
function fun.CEST(frame)
Ligne 1 226 : Ligne 1 284 :
 
-- on récupère l'information dans la zone courante
 
-- on récupère l'information dans la zone courante
 
local t = mw.getContentLanguage():formatDate("I", nil, true)
 
local t = mw.getContentLanguage():formatDate("I", nil, true)
+
 
 
if (t == "1") then  -- heure d'été
 
if (t == "1") then  -- heure d'été
 
if (opt == "sans lien") then
 
if (opt == "sans lien") then

Version actuelle datée du 5 mai 2020 à 17:03

Utilisation

Fonctions utilisables depuis un modèle

  • modeleDate(frame) – affiche une date 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.
  • 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.
  • 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.

Fonctionnement

  • 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. 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 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 Mois invalide (otcobre) 1 / 0
jour invalide jeudi 14|octobre|2001 1 / 0
jour invalide (trop grand pour le mois) 31|septembre|2001 Jour invalide (31 septembre) 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 0 / 0
mois uniquement |Octobre Octobre 0 / 0
sans argument 0 / 0
date du calendrier julien 1|octobre|2001|julien=oui 1er octobre 2001 ( dans le calendrier grégorien)
date du calendrier julien (changement de mois) 25|octobre|2001|julien=oui 25 octobre 2001 ( dans le calendrier grégorien)
date du calendrier julien (changement d'année) 25|décembre|2001|julien=oui 25 décembre 2001 ( dans le calendrier grégorien)
date de naissance 14|octobre|2001|age=oui (23 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 (18 ans) (18 ans)
avec qualificatif 1|août|2006|en Suisse|age=oui (18 ans) (18 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 (18 ans) (18 ans)
annee seule ||2006|âge=oui


dateInfobox( frame )

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. 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|}} (97 ans)
  • {{#invoke:Date|dateInfobox|naissance|13 juillet 1927|14 mai 2017}}
  • {{#invoke:Date|dateInfobox|naissance|30 juin 2017-}} (7 ans)
  • {{#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}} → le
  • {{#invoke:Date|dateInfobox|date|13 juillet 1927| préfixe = le | préfixe sans jour = en}} → le
  • {{#invoke:Date|dateInfobox|date|juillet 1927| préfixe = le}}
  • {{#invoke:Date|dateInfobox|date|juillet 1927| préfixe = le | préfixe sans jour = en}} → en
  • {{#invoke:Date|dateInfobox|date|13 juillet [[1927]]}}
  • {{#invoke:Date|dateInfobox|date|13 juillet [[1927 en France|1927]]}} → 13 juillet 1927
  • {{#invoke:Date|dateInfobox|date|{{date|13 juillet 1927|en France}}}}



local fun = {}

local Outils = require 'Module:Outils'
-- chargement de la base de données 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 où 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 fonctions qui l'utilisent.
local trim = Outils.trim

-- Fonction destinée à mettre la première lettre du mois en majuscule :
-- utilisation de string car aucun mois ne commence 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 = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'


-- liste des mois, écriture exacte et alias, en minuscule
local listeMois = {
	{ num = 1,  nJour = 31, abrev = 'janv.',  nom = 'janvier', alias = { 'jan.', 'janv.', 'jan', 'janv', 'january' } },
	{ num = 2,  nJour = 29, abrev = 'fév.',   nom = 'février', alias = { 'fevrier', 'fev.', 'fev', 'fév.', 'fév', 'févr', 'févr.', 'february', 'feb', 'feb.' } },
	{ num = 3,  nJour = 31, abrev = 'mars',   nom = 'mars', alias = { 'mar.', 'mar', 'march' } },
	{ num = 4,  nJour = 30, abrev = 'avr.',   nom = 'avril', alias = { 'avr.', 'avr', 'apr', 'april'} },
	{ num = 5,  nJour = 31, abrev = 'mai',    nom = 'mai', alias = { 'may' } },
	{ num = 6,  nJour = 30, abrev = 'juin',   nom = 'juin', alias = { 'jun', 'june' } },
	{ num = 7,  nJour = 31, abrev = 'juill.', nom = 'juillet', alias = { 'juil.', 'juil', 'juill.', 'juill', 'jul', 'july' } },
	{ num = 8,  nJour = 31, abrev = 'août',   nom = 'août', alias = { 'aoû', 'aug', 'august' } },
	{ num = 9,  nJour = 30, abrev = 'sept.',  nom = 'septembre', alias = { 'sept.', 'sept', 'sep.', 'sep', 'september' } },
	{ num = 10, nJour = 31, abrev = 'oct.',   nom = 'octobre', alias = { 'oct.', 'oct', 'october' } },
	{ num = 11, nJour = 30, abrev = 'nov.',   nom = 'novembre', alias = { 'nov.', 'nov', 'november' } },
	{ num = 12, nJour = 31, abrev = 'déc.',   nom = 'décembre', alias = { 'decembre', 'déc.', 'dec.', 'dec', 'déc', 'december' } },
	aout = { num = 8, nJour = 31, abrev = 'aout', nom = 'aout', alias = { 'aou' } },
}

-- ajoute les noms, abréviations et alias en tant que clés de listeMois
for i = 1, 12 do
	local mois = listeMois[i]
	listeMois[tostring( i )] = mois
	if i < 10 then
		listeMois['0' .. i] = mois
	end
	listeMois[mois.nom] = mois
	listeMois[mois.abrev] = mois
	for _, n in ipairs( mois.alias ) do
		listeMois[n] = mois
	end
end
for _, n in ipairs( listeMois.aout.alias ) do
	listeMois[n] = listeMois.aout
end

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

-- à partir d'un nom de saison (en français ou en anglais),
-- retourne son nom canonique (exemple : "été")
-- si non reconnu, retourne nil
function fun.determinationSaison( saison )
	local s = trim( saison )
	if s then
		s = mw.ustring.lower( s )
		for i = 1, 4 do
			for _, n in ipairs( liste_saisons[i] ) do
				if s == n then
					return liste_saisons[i][1]
				end
			end
		end
	end
end

---
-- à partir d'un nom de mois (en français ou en anglais), de son numéro ou d'une abréviation,
-- retourne son nom canonique (exemple : "juin") et son numéro (exemple : 6)
-- si non reconnu, retourne nil, nil
function fun.determinationMois( mois )
	local result

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

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


-- fonction interne à modeleDate, pour déterminer si on peut se passer de faire un ifexist
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 remplacé par celui de la base de données, ce qui permet des alias.
	local lien = annee
	if dataQualificatif.qualificatif then
		lien = lien .. ' ' .. dataQualificatif.qualificatif
	end
	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 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'année n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe.
	local 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="[Pp]remier" ?>1<sup>er</sup></abbr>', '1<sup>er</sup>', '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 une 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, '<span class="error">' .. periode .. ' invalide (' .. valeur .. ')</span>'
		end
		local jour, mois, annee, masquerMois, masquerAnnee, separateur
		-- 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 mmm2 = '([^%s%p%d]+[.]?[-/][^%s%p%d]+[.]?)' -- mois-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

		date = fun.nettoyageJour( date )
		-- suppression catégorie, liens, balises
		date = mw.ustring.gsub( date, '%[%[[Cc]at[ée]gor[yi]e?:.-%]%]', '' )
		date = date	:gsub( '%b<>', '' )
					:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end )
		-- suppression des espaces insécables
					-- nbsp
					:gsub( '\194\160', ' ' )
					:gsub( '&nbsp;', ' ' )
					:gsub( '&#160;', ' ' )
					-- narrow nbsp
					:gsub( '\226\128\175', ' ' )
					:gsub( '&#8239;', ' ' )
					-- thin space
					:gsub( '\226\128\137', ' ' )
					:gsub( '&thinsp;', ' ' )
					:gsub( '&#8201;', ' ' )
					-- simple space
					:gsub( '&#32;', ' ' )
					-- plusieurs espaces
					:gsub( ' +', ' ' )
		-- réduction av. J-C pour simplifier un peu les regex :
					:gsub( '(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?', '-%1' )
		-- suppression de l'heure dans les dates ISO
					: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, separateur, b, sb = date:match( '^'..aj..sep..aj..moins..'$' )
			a, b = tonumber( a ), tonumber( b )
			if separateur:match( '^.+%-$' ) then
				-- probablement mm/-aaaa, année av.JC
				b = 0 - b
			end
			if  a > 12 and ( b < 1 or b > 31 ) or
				b > 12 and ( a < 1 or a > 31 ) then
				return erreur( 'Date', date )
			elseif b < 1 or b > 31 then
				mois, annee, masquerAnnee = a, b, sb
			elseif a < 1 or 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, separateur, mois, masquerMois, annee, masquerAnnee = date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' )
			if separateur == '-' 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, separateur, mois, masquerMois, annee, masquerAnnee = date:match( '^'..j..sep..mmm..moins..'%2'..aj..moins..'$' )
		elseif date:match( '^'..mmm..s..aj..moins..'$' ) then
			-- mmm aaaa
			mois, separateur, annee, masquerAnnee = date:match( '^'..mmm..sep..aj..moins..'$' )
			if separateur:match( '^.+%-$' ) then
				annee = '-' .. annee
			end
		elseif date:match( '^'..mmm2..s..aj..moins..'$' ) then
			-- mmm-mmm aaaa
			mois, separateur, annee, masquerAnnee = date:match( '^'..mmm2..sep..aj..moins..'$' )
			if separateur:match( '^.+%-$' ) then
				annee = '-' .. annee
			end
		elseif date:match( '^'..j..s..mmm..moins..'$' ) then
			-- jj mmm
			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 < 0 or #jour >= 3 ) and an <= 31 then
			-- cas notamment des date ISO 2015-06-17, -0044-06-17 et -0002-06-17
			-- inversion du jour et de l'année
			local temp = annee
			annee = jour
			jour = temp
		end

		return fun.validationJourMoisAnnee{
			jour, mois, annee,
			masquerAnnee = trim( masquerAnnee ) and true or nil,
			masquerMois = ( trim( masquerAnnee ) or not annee ) and trim( masquerMois ) and true or nil,
			-- or nil sert juste à éviter de trainer une valeur false dans tous les tests unitaires.
		}
	else
		return true, {}
	end
end


---
-- validationJourMoisAnnee vérifie que les paramètres correspondent à une date valide.
-- la date peut être dans les paramètres 1 à 3, ou dans des paramètres jour, mois et annee.
-- La fonction retourne true suivi d'une table avec la date en paramètres nommés (sans accent sur année)
-- ou false suivi d'un message d'erreur.
function fun.validationJourMoisAnnee( frame )
	local args = Outils.extractArgs( frame )
	local jour, mois, numMois, annee
	local bjour = args[1] or args['jour'] or ''
	local bmois = tostring( args[2] or args['mois'] or '' )
	local bannee = args[3] or args['annee'] or args['année'] or ''

	local function erreur( periode, valeur )
		return false, '<span class="error">' .. periode .. ' invalide (' .. valeur .. ')</span>'
	end

	-- on traite l'année
	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 = bannee:upper():match( '^(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?' )
			annee = tonumber( annee )
			if annee then
				annee = 0 - annee
			else
				return erreur( 'Année', bannee )
			end
		elseif annee == 0 then
			return erreur( 'Année', 0 )
		end
	else
		annee = nil
	end

	-- on traite le mois
	if Outils.notEmpty( bmois ) then
		mois, numMois = fun.determinationMois( bmois )
		if mois == nil then
			mois = fun.determinationSaison( bmois )
			if mois == nil then
				local mois1, sep, mois2 = bmois:match( '^([^%s%p%d]+[.]?)([-/])([^%s%p%d]+[.]?)$' )
				if mois1 then
					mois1 = fun.determinationMois( mois1 )
					mois2 = fun.determinationMois( mois2 )
					if mois1 == nil or mois2 == nil then
						return erreur( 'Mois', bmois )
					end
					mois = mois1 .. sep .. mois2
				else
					return erreur( 'Mois', bmois )
				end
			end
		end
		-- on traite le jour si présent
		if Outils.notEmpty( bjour ) then
			if not numMois then
				erreur( 'Date', 'jour avec saison ou plusieurs mois' )
			end
			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 > listeMois[numMois].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
	else
		-- on teste le jour si présent
		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 erreur( 'Jour', bjour )
				end
			end
		end
	end

	-- vérification de l'absence d'un décalage
	if annee and annee < 13 and annee > 0 and not jour and ( tonumber( bmois ) or ( not mois and tonumber( args[4] ) ) ) then
		return false, '<span class="error">année improbable (' .. annee .. ')</span>'
	end

	local resultat = {
		jour = jour,
		mois = mois,
		numMois = numMois,
		annee = annee,
		masquerAnnee = args.masquerAnnee,
		masquerMois = args.masquerMois,
	}
	return true, resultat
end


---
-- émule le modèle {{m|Date}}.
-- Paramètres :
--		1 : jour (numéro ou "1er") ou la date complète
--		2 : mois (en toutes lettres) ou spécialité de l'année
--		3 : année (nombre)
--		4 : spécialité de l'année
--		julien : date dans le calendrier julien
--		compact : affiche le mois sous forme d'abréviation
--		avJC : non pour désactiver l'affichage de « av. J.-C. » pour les dates négatives
--		âge : ajoute la durée depuis cette date
--		nolinks : ne met pas de lien sur la date
--		onerror : en cas d'erreur, valeur à retourner à la place du message d'erreur ; la valeur spéciale "input" fait retourner le 1er argument inchangé
--		naissance : ajoute la class "bday"
--		mort : ajoute la class "dday"
function fun.modeleDate( frame )
	local args = Outils.extractArgs( frame )
	local cat, resultat = ''

	local dateNaissanceMort

	-- analyse des paramètres non nommés (ou paramètres de la date jour, mois, annee)
	local test, params
	local arg1, arg2, arg3 = fun.nettoyageJour( args[1] ), trim( args[2] ), trim( args[3] )
	if type( arg1 ) == 'string' and arg3 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or arg2 == nil or dataLiens[arg2] or mw.ustring.match( arg2, '%a %a' ) ) then
		-- la date est dans le premier paramètre
		test, params = fun.separationJourMoisAnnee( arg1 )
		if test then
			dateNaissanceMort = trim( arg2 )
			params.qualificatif = trim( arg2 )
		end
	elseif type( arg1 ) == 'string' and type( arg2 ) == 'string' and arg3 ~= nil and arg4 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or dataLiens[arg3] or mw.ustring.match( arg3, '%a %a' ) ) then
		-- la date est dans le premier paramètre
		test, params = fun.separationJourMoisAnnee( arg1 )
		if test then
			dateNaissanceMort = trim( arg2 )
			params.qualificatif = trim( arg3 )
		end
	else
		local function masquerParam( p )
			-- sépare le signe moins final éventuel signifiant que le paramètre ne doit pas être affiché.
			if type( p ) ~= 'string' then
				return p, nil
			end
			local value, mask = p:match( '^%s*(.-)(%-?)%s*$' )
			return value, ( mask == '-' or nil )
		end
		local cleanArgs = { arg1 or args.jour }
		cleanArgs[2], cleanArgs.masquerMois = masquerParam( args[2] or args.mois )
		cleanArgs[3], cleanArgs.masquerAnnee = masquerParam( args[3] or args.annee or args['annee'] )

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

	-- analyse des paramètres nommés
	if test then
		local Yesno = require 'Module:Yesno'

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

		-- julien peut avoir trois valeurs : inactif, format standard (true), format court
		params.julien = Yesno( args.julien, 'court', false )
		params.avJC = Yesno( args.avJC )

		if args['républicain'] and args['républicain'] ~= '' then
			if args['républicain'] == 'liens' then
				params.republicain = 'liens'
			else
				params.republicain = Yesno( args['républicain'], false )
			end
		else
			params.republicain = false
		end

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

		if dateNaissanceMort then
			local testNaissanceMort, paramsNaissanceMort = fun.separationJourMoisAnnee( dateNaissanceMort )
			if testNaissanceMort then
				params.anneeNaissanceMort, params.moisNaissanceMort, params.numMoisNaissanceMort, params.jourNaissanceMort = paramsNaissanceMort.annee, paramsNaissanceMort.mois, paramsNaissanceMort.numMois, paramsNaissanceMort.jour
			end
		end

		local listeParam = {
			age = 'âge',
			['âge'] = 'âge',
			naissance = 'naissance',
			mort = 'mort',
			['décès'] = 'mort',
			apJC = 'apJC',
			nolinks = 'nolinks',
			compact = 'compact',
			compacte = 'compact',
		}
		for n, v in pairs( listeParam ) do
			params[v] = params[v] or Yesno( args[n], true, false ) or nil
		end

		-- sortie pour les tests unitaire, ou pour débugger
		if args.debug then
			return params
		end

		resultat = fun._modeleDate( params )

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

	else
		local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
		if namespaceCategorisation[mw.title.getCurrentTitle().namespace] and not Outils.notEmpty( args.nocat ) then
			cat = '[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée]]'
		end
		resultat = params .. cat
	end

	return resultat or ''
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'âge, naissance et mort
	local agePrefix = ''
	local age = args['âge'] and fun.age( annee, numMois, jour )
	local naissance = args.naissance
	local mort = args.mort
	if mort and args.anneeNaissanceMort then
		age = fun.age( args.anneeNaissanceMort, args.numMoisNaissanceMort, args.jourNaissanceMort, annee, numMois, jour )
		agePrefix = 'à '
	end

	-- 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      -- date suivant le calendrier julien si necessaire
	local julienDate, julienSup, julienSep              -- servira éventuellement à afficher la date selon le calendrier julien
	local gregAprMois, gregAprAn, gregFin               -- message de calendrier grégorien lorsque la date est selon le calendrier julien
	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
			args.julien = false

		elseif args.julien then
			gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
			annee, mois, jour = gannee, listeMois[gmois].nom, gjour
			if jjour == 1 then
				jjour = modelePremier
			end
			if args.compact then
				jmois = listeMois[jmois].abrev
			end
			if args.julien == 'court' then
				julienDate = jjour .. ' ' .. jmois .. ' '
				julienSup = '<sup>[[calendrier julien|jul.]]</sup>'
				if jannee == annee then
					gregAprMois = '<sup>[[calendrier grégorien|grég.]]</sup>'
				else
					julienDate = julienDate .. jannee .. ' '
					gregAprAn = '<sup>[[calendrier grégorien|grég.]]</sup>'
				end
				julienSep = ' / '
			else
				julienDate = jjour .. ' ' .. jmois .. ' ' .. jannee
				julienSep = ' ('
				gregFin = ' [[Passage du calendrier julien au calendrier grégorien|dans le calendrier grégorien]])'
			end

		elseif args.republicain then
			local DateRep = require 'Module:Date républicaine'
			local RepSansLiens
			if args.republicain == 'liens' then
				RepSansLiens = false
			else
				RepSansLiens = true
			end
 			dateRepublicaine = DateRep._date_republicaine(
 				RepSansLiens,
 				{ fun.formatRepCal( fun.do_toRepCal{gannee, gmois, gjour} ) }
 			)
		end
	else
		if annee and annee < 0 then
			gannee = gannee + 1
		end
		args.julien = false
		args.republicain = false
	end

	-- on génère le résultat

	-- Déclarations des variables
	local wikiListe = {}                   -- reçoit le texte affiché pour chaque paramètre
	local iso = {}                         -- reçoit le format date ISO de ce paramètre
	local texteMois = mois                 -- texte du mois qui sera affiché (éventuellement l'abréviation)
	if args.compact then
		if not numMois then
			-- mois est autre chose qu'un simple mois : saison, mois-mois... auquel cas, pas d'abréviation (provoquait erreur Lua)
			-- (les abréviations pour le cas "mois[-/]mois" seraient théoriquement possibles, mais ça reste à implémenter)
		else
			if args.nolinks then
				texteMois = '<abbr class=abbr title="' .. mois .. '">' .. listeMois[mois].abrev .. '</abbr>'
			else
				texteMois = listeMois[mois].abrev
			end
		end
	end
	mois = mois and mois:gsub( 'aout', 'août' )

	local dataQualificatif, dataCat
	if not args.nolinks then
		dataQualificatif = dataLiens[qualificatif or '']
		if type( dataQualificatif ) ~= 'table' then
			-- si le qualificatif n'est pas dans la base de données, on crée une table minimum,
			-- qui imposera un test sur l'année, 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


	-- le jour si présent
	local qualifJour = ''
	if jour then
		local texteJour = jour
		if args.nolinks then
			if jour == 1 then
				jour = modelePremier
			end
			table.insert( wikiListe, 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.
			table.insert( wikiListe, wikiLien( lien, jour ) )
			table.insert( wikiListe, wikiLien( lien, jour .. ' '.. texteMois ) )
		end
		table.insert( iso, 1, string.sub( '0' .. gjour, -2 ) )
	end

	-- le mois
	if mois then
		if #wikiListe == 0 and annee == nil then
			return texteMois
		end
		if args.nolinks then
			if not args.masquerMois then
				table.insert( wikiListe, texteMois )
			end
		else
			local lien
			if annee then
				if not numMois then
					-- mois est autre chose qu'un simple mois : saison, mois-mois... auquel cas, pas de lien
				else
					lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois )
					if lien == nil and qualificatif and qualifJour == '' then
						-- 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 or args.masquerMois then
				-- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois']]
				table.remove( wikiListe )
				if not args.masquerMois then
					table.insert( wikiListe, wikiLien( lien, texteMois ) )
				end
			elseif #wikiListe > 0 then
				-- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois'
				table.remove( wikiListe, #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.
				table.insert( wikiListe, texteMois )
			end
		end
		if gmois then
			table.insert( iso, 1, string.sub( '0' .. gmois, -2 ) )
		end
		table.insert( wikiListe, gregAprMois )
	end

	-- l'année
	if annee and not (args.julien == true and args.nolinks and jannee == annee ) then
		if not args.masquerAnnee then
			local texteAnnee = annee
			local lien
			if annee < 0 then
				local annneeAvJc = 0 - annee
				lien = lien or ( annneeAvJc .. ' av. J.-C.' )
				if args.avJC == false then
					texteAnnee = annneeAvJc
				else
					texteAnnee = annneeAvJc .. ' <abbr class="abbr" title="'
						.. annneeAvJc .. ' avant Jésus-Christ">av. J.-C.</abbr>'
				end
			elseif args.apJC then
				texteAnnee = texteAnnee .. ' <abbr class="abbr" title="'
					.. texteAnnee .. ' après Jésus-Christ">apr. J.-C.</abbr>'
			end
			if args.nolinks then -- seulement si on doit l'afficher
				table.insert( wikiListe, 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 = texteMois .. ' ' .. texteAnnee
				end
				table.insert( wikiListe, wikiLien( lien, texteAnnee ) )
			end
		end
	end
	if annee then
		if gannee > 999 then
			table.insert( iso, 1, gannee )
		elseif gannee > -1 then
			table.insert( iso, 1, string.sub( '000' .. gannee , -4 ) )
		elseif gannee > -999 then
			-- calendrier grégorien proleptique avec année 0.
			table.insert( iso, 1, 'U-' .. string.sub( '000' .. ( 0 - gannee ), -4 ) )
		else
			table.insert( iso, 1, 'U' .. gannee )
		end
	end
	table.insert( wikiListe, gregAprAn )

	-- l'age
	if type( age ) == 'number' and age >= 0 and ( not naissance or age < 120 ) then
		if age == 0 then
			age = '(' .. agePrefix .. 'moins d’un\194\160an)'
		elseif age == 1 then
			age = '(' .. agePrefix .. '1\194\160an)'
		else
			age = '('.. agePrefix .. age .. '\194\160ans)'
		end
	else
		age = false
	end


	-- compilation du résultat
	local wikiTexte = table.concat( wikiListe, ' ' )
	local isoTexte = table.concat( iso, '-' )

	-- On ajoute un peu de sémantique.
	local wikiHtml = mw.html.create( '' )

	if julienDate then
		wikiHtml:tag( 'span')
				:addClass( 'nowrap' )
				:attr( 'data-sort-value', isoTexte )
				:wikitext( julienDate )
				:node( julienSup )
				:done()
			:wikitext( julienSep )
	end

	local dateHtml = wikiHtml:tag( 'time' )
			:wikitext( wikiTexte )
	if wikiTexte:match( ' ' ) then
		dateHtml:addClass( 'nowrap' )
	end
	if isoTexte ~= wikiTexte then
		dateHtml:attr( 'datetime', isoTexte )
				:attr( 'data-sort-value', 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

	wikiHtml:wikitext( gregFin )

	if args.republicain then
		wikiHtml:wikitext( ' (', dateRepublicaine, ')' )
	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és pour gérer les cas où
-- 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
--		préfixe : préfixe à afficher s'il y a un jour (par défaut '')
--		préfixe sans jour : préfixe à afficher s'il n'y a pas de jour (par défaut : '')
function fun.dateInfobox( frame )
	local args = frame.args
	if type( args ) ~= 'table' or not ( args[1] and args[2] ) then
		return
	end

	-- analyseDate sépare la date du contenu qui suit, supprime les liens, et retourne si possible une table avec jour mois année
	local function analyseDate( d )
		if trim( d ) then
			local analyse = d:match( ' ou ') or d:match( 'entre ' ) or d:match( 'vers ' ) or d:match( 'après ' ) or d:match( 'avant ' )
			if analyse then
				return d
			end
			analyse = d:match( 'datetime="([%d-]+)"' ) or d
			-- sépare la date (avec ses liens) d'une référence ou contenu commençant par un espace)
			local debut, fin = analyse:match( '(.-%d%d%d%]*%-?)([\127 ].+)' )
			if not debut then
				-- sépare la date du contenu commençant par <br>
				debut, fin = analyse:match( '(.-%d%d%d%]*%-?)(<br ?/?>.+)' )
			end
			analyse = debut or analyse
			-- supprime les liens
			analyse = analyse:gsub(
				'%[%[([^%[%]|]*)|?([^%[%]]*)%]%]',
				function ( l, t )
					return trim( t ) or l
				end
			)
			local t, r = fun.separationJourMoisAnnee( analyse )
			if t then
				return r, fin
			else
				return d, fin
			end
		end
	end
	-- prefix ajoute un préfixe en fonction de la présence ou non du jour si le paramètre "préfixe sans jour" est défini
	local function prefix( dateString )
		if dateString then
			local datetime = dateString:match( 'datetime="([U%d%-]+)"' )
			if datetime and datetime:match('%-%d%d%-%d%d') and trim( args['préfixe'] ) then
				return args['préfixe'] .. ' ' .. dateString
			end
			if trim( args['préfixe sans jour'] ) then
				return args['préfixe sans jour'] .. ' ' .. dateString
			end
		end
		return dateString
	end

	local naissance = args[1]:match( '^n' ) == 'n'
	local mort = args[1]:match( '^m' ) or args[1]:match( 'décès' )
	local affichageDate, qualificatif = args[2], args[4]
	local affichageDateTab, resultatDate, complementDate
	local dateNaissance, dateMort
	if mort then
		affichageDate = args[3]
	end
	if not trim( affichageDate ) then
		return
	end
	if affichageDate:match( '</time>' ) then
		-- 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 ( naissance or mort ) and ( affichageDate:match( 'wikidata%-linkback' )) then
			dateNaissance = analyseDate( args[2] )
			dateMort = analyseDate( args[3] )
			resultatDate = affichageDate
		else
			return prefix( affichageDate )
		end
	else
		affichageDateTab, complementDate = analyseDate( affichageDate )
		if type( affichageDateTab ) ~= 'table' then
			return affichageDateTab
		else
			if naissance then
				dateNaissance = affichageDateTab
				dateMort = analyseDate( args[3] )
			elseif mort then
				dateNaissance = analyseDate( args[2] )
				dateMort = affichageDateTab
			else
				qualificatif = args[3]
			end
			affichageDateTab.naissance = naissance
			affichageDateTab.mort = mort
			affichageDateTab.qualificatif = args.qualificatif or qualificatif
			affichageDateTab.nolinks = args.nolinks
			affichageDateTab.nocat = args.nocat
			affichageDateTab.julien = args.julien
		end
	end
	resultatDate = resultatDate or fun.modeleDate( affichageDateTab )

	local age, prefixAge, suffixAge, calculAge = '', ' <span class="noprint">(', ')</span>', nil
	if naissance and
		dateNaissance and
		not dateMort and
		type( dateNaissance ) == 'table'
	then
		calculAge = fun.age( dateNaissance.annee, dateNaissance.numMois, dateNaissance.jour )
		if calculAge and calculAge > 120 then
			calculAge = nil
		end
	elseif mort and
		dateNaissance and
		dateMort and
		type( dateNaissance ) == 'table'
		and type( dateMort ) == 'table'
	then
		calculAge = fun.age(
			dateNaissance.annee,
			dateNaissance.numMois,
			dateNaissance.jour,
			dateMort.annee,
			dateMort.numMois,
			dateMort.jour
		)
		prefixAge = ' (à '
		suffixAge = ')'
	end
	if tonumber( calculAge ) then
		if calculAge > 1 then
			age = prefixAge .. calculAge .. '\194\160ans' .. suffixAge
		elseif calculAge == 1 then
			age = prefixAge .. 'un\194\160an' .. suffixAge
		elseif calculAge == 0 then
			age = prefixAge .. 'moins d’un\194\160an' .. suffixAge
		end
		if complementDate and complementDate:match( 'ans?%)' ) then
			complementDate = ''
		end
	end

	return prefix( resultatDate ) .. ( complementDate or '' ) .. age
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 lettres 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 <= listeMois[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
			local 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]) .. "\194\160" .. 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'âge en fonction de la ou les dates fournies. La valeur retournée est de type 'number'
-- Paramètres :
-- 1, 2, 3 : année, mois jour de naissance (supposé dans le calendrier grégorien)
-- 4, 5, 6 : année, mois, jour du calcul (facultatif, par défaut la date UTC courante).
function fun.age( an, mn, jn, ac, mc, jc )
	if ac == nil then
		local today = os.date( '!*t' )
		ac = today.year
		mc = today.month
		jc = today.day
	else
		ac = tonumber( ac )
		mc = tonumber( mc )
		jc = tonumber( jc )
	end

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

	if an == nil or ac == nil or mn == nil or mc == nil then
		-- pas de message d'erreur qui risque de faire planter la fonction appelante
		-- à elle de gérer ce retour.
		return
	end

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

function fun.modeleAge( frame )
	local args = frame:getParent().args
	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 '<span class="error">Paramètres incorrects ou insuffisants pour calculer l\'âge précis</span>'
	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'algorithme 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


--[[
  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 correspondants
--]]
function fun.CEST(frame)
	-- option : ne pas créer de wikilien
	local opt = trim(frame.args[1] or frame:getParent().args[1])
	-- 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