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

De Lagny-sur-Marne Wiki
Aller à la navigation Aller à la recherche
0x010D (discussion | contributions)
m retouche de la modification précédente
m Révocation des modifications de Zebulon84 (discussion) vers la dernière version de 0x010C
 
(74 versions intermédiaires par 2 utilisateurs non affichées)
Ligne 2 : Ligne 2 :


local TableBuilder = require( 'Module:TableBuilder' )
local TableBuilder = require( 'Module:TableBuilder' )
 
local Outils = require( 'Module:Outils' )
-- génère une erreur
-- chargement de la base de donnée répertoriant certaines pages existant ou n'existant pas pour éviter les "ifexist".
local function erreurDate(texte)
local dataLiens
    return '<span class="error">' .. (texte or "''aucune erreur indiquée''") .. "</span>"
local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' )
if success then
dataLiens = resultat
else
-- protection au cas ou le sous module serait mal modifié
dataLiens = { [''] = { mois = { aucun = 1000, tous = { 1773, 2014 } }, } }
end
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 fonction qui l'utilise.
local function trim( texte )
local trim = Outils.trim
    if type( texte ) == "string" and texte ~= "" then
        return mw.text.trim( texte )
    end
end


-- Fonction destiné à mettre la première lettre du mois en majuscule du mois :
-- utilisation de string car aucun mois ne commance par une lettre non ascii en français ou anglais.
local function ucfirst( str )
local function ucfirst( str )
    return mw.ustring.upper( mw.ustring.sub( str, 1, 1 ) ) .. mw.ustring.sub( str, 2 )
return str:sub( 1, 1 ):upper() .. str:sub( 2 )
end
end
local modelePremier = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'




-- liste des mois, écriture exacte et simplifiée, en minuscule
-- liste des mois, écriture exacte et simplifiée, en minuscule
local liste_mois = {
local liste_mois = {
    { "janvier", "jan.", "janv.", "jan", "janv", "january", nJour = 31 },
{ "janvier", "jan.", "janv.", "jan", "janv", "january", nJour = 31 },
    { "février", "fevrier", "fev.", "fev", "fév.", "fév", "february", nJour = 29 },
{ "février", "fevrier", "fev.", "fev", "fév.", "fév", "february", nJour = 29 },
    { "mars", "mar.", "mar", "march", nJour = 31 },
{ "mars", "mar.", "mar", "march", nJour = 31 },
    { "avril", "avr.", "avr", "apr", "april", nJour = 30 },
{ "avril", "avr.", "avr", "apr", "april", nJour = 30 },
    { "mai", "may", nJour = 31 },
{ "mai", "may", nJour = 31 },
    { "juin", "jun", "june", nJour = 30 },
{ "juin", "jun", "june", nJour = 30 },
    { "juillet", "juil.", "juil", "juill.", "juill", "jul", "july", nJour = 31 },
{ "juillet", "juil.", "juil", "juill.", "juill", "jul", "july", nJour = 31 },
    { "août", "aout","aou", "aug", "august", nJour = 31 },
{ "août", "aout", "aou", "aug", "august", nJour = 31 },
    { "septembre", "sept.", "sept", "sep.", "sep", "september", nJour = 30 },
{ "septembre", "sept.", "sept", "sep.", "sep", "september", nJour = 30 },
    { "octobre", "oct.", "oct", "october", nJour = 31 },
{ "octobre", "oct.", "oct", "october", nJour = 31 },
    { "novembre", "nov.", "nov", "november", nJour = 30 },
{ "novembre", "nov.", "nov", "november", nJour = 30 },
    { "décembre", "decembre", "déc.", "dec.", "dec", "déc", "december", nJour = 31 },
{ "décembre", "decembre", "déc.", "dec.", "dec", "déc", "december", nJour = 31 },
}
fun.liste_mois = liste_mois
 
local liste_saison = {
{ 'printemps', 'spring', },
{ 'été', 'summer', },
{ 'automne', 'autumn', },
{ 'hiver', 'winter', },
}
}


-- nom du mois à partir du numéro
-- nom du mois à partir du numéro
function fun.nomDuMois( num )
function fun.nomDuMois( num )
    if type( num ) ~= "number" or num < 1 or num > 12 then
if type( num ) ~= "number" or num < 1 or num > 12 then
        return nil
return nil
    end
end
    return liste_mois[num][1]
return liste_mois[num][1]
end
end


---
-- valide que la chaîne passée est un mois valide.
-- valide que la chaîne passée est un mois valide.
-- retourne le nom complet ou nil si non reconnu
-- retourne le nom complet ou nil si non reconnu
-- si reconnu, retourne aussi le numéro du mois [1-12]
-- si reconnu, retourne aussi le numéro du mois [1-12]
function fun.valideMois( mois )
function fun.valideMois( mois )
    if type( mois ) ~= "string" then  
local m = trim( mois )
        return nil
if m then
    end
m = mw.ustring.lower( m )
   
for i = 1, 12 do
    local m = mw.ustring.lower( mois )
local j = 1
while liste_mois[i][j] do
if liste_mois[i][j] == m then
return liste_mois[i][1], i
end
j = j + 1
end
end
end
-- pas trouvé = return nil
end


    for i = 1, 12 do
---
        local j = 1
-- valide que la chaîne passée est un mois valide.
        while liste_mois[i][j] ~= nil do
-- retourne le nom complet ou nil si non reconnu
          if liste_mois[i][j] == m then
-- si reconnu, retourne aussi le numéro du mois [1-12]
              return liste_mois[i][1], i
function fun.valideSaison( saison )
          end
if type( saison ) ~= "string" then
          j = j + 1
return nil
        end
end
    end
    -- pas trouvé = return nil
local m = mw.ustring.lower( trim( saison ) )
for i = 1, 4 do
local j = 1
while liste_saison[i][j] ~= nil do
if liste_saison[i][j] == m then
return liste_saison[i][1]
end
j = j + 1
end
end
-- pas trouvé = return nil
end
end


-- determinationMois trouve le numéro du mois et son nom,  
---
-- determinationMois trouve le numéro du mois et son nom,
-- à partir de son nom, de son numéro ou d'une expression mathématique.
-- à partir de son nom, de son numéro ou d'une expression mathématique.
-- l'objet frame est facultatif, mais permet de tester si mois est une expression type '2+3'
-- Si le deuxième paramètre est vrai, les nombres supérieur à 12 ou non entiers sont acceptés. 
function fun.determinationMois( mois, frame )
function fun.determinationMois( mois, mod, boucle )
    local num, nom
local num, nom
    if tonumber( mois ) then
if tonumber( mois ) then
        num = math.floor( math.fmod( tonumber( mois ) - 1, 12 )  ) + 1
num = math.floor( tonumber( mois ) )
    elseif type( mois ) == "string" then
if mod then
        nom, num = fun.valideMois( mois )
-- si le nombre du mois est calculé par une exression, le résultat peut être supérieur à 12, ou inférieur à 1
        if nom == nil and frame and frame.callParserFunction then
num = math.fmod( num + 239, 12 ) + 1 -- +239 car fmod(-1) = -1 et non 11
            -- essai de détermination d'un nombre avec le parser #expr de Mediawiki.
elseif num < 1 or num > 12 then
            -- la fonction s'appelle sans l'objet frame pour ne pas tourner en boucle.
num = nil
            nom, num = fun.determinationMois( frame:callParserFunction( '#expr', mois ) )
end
        end
elseif trim( mois ) then
    end
nom, num = fun.valideMois( mois )
    if num and not nom then  
if nom == nil and boucle == nil then
        nom = liste_mois[num][1]
-- essai de détermination d'un nombre avec le parser #expr de Mediawiki.
    end
-- le paramètre boucle évite de tourner en boucle.
    return nom, num
nom, num = fun.determinationMois( mw.getCurrentFrame():callParserFunction( '#expr', mois ), true, true )
end
end
if num and not nom then
nom = liste_mois[num][1]
end
return nom, num
end
end


Ligne 91 : Ligne 133 :
--  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 ifexit
local function existDate( dataQualificatif, annee, mois )
local function existDate( dataQualificatif, annee, mois )
    local data
local data
    if mois then
if mois then
        data = dataQualificatif.mois
data = dataQualificatif.mois
    else
else
        data = dataQualificatif.annee
data = dataQualificatif.annee
    end
end
    if type( data ) ~= 'table' then  
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.
-- si data n'existe pas c'est que l'on considère qu'il n'y a pas de lien.
        return  
return
    end
end
    -- le qualificatif est remplacer par celui de la base de donnée, ce qui permet des alias.
-- le qualificatif est remplacer par celui de la base de donnée, ce qui permet des alias.
    local lien = annee .. ' ' .. ( dataQualificatif.qualificatif or '' )
local lien = annee .. ' ' .. ( dataQualificatif.qualificatif or '' )
    local seul = annee
local seul = annee
    if mois then  
if mois then
        lien = mois .. ' ' .. lien
lien = mois .. ' ' .. lien
        seul = ucfirst( mois ) .. ' ' .. annee
seul = ucfirst( mois ) .. ' ' .. annee
    end
end
   
local aucun = tonumber( data.aucun )
    if type( data.aucun ) == 'number' and annee <= (data.aucun or 0) 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 la l'année est dans la partie 'aucun' on teste s'il y a malgré tout un lien isolé
        if type( data.seul ) == 'table' then
if type( data.seul ) == 'table' then
            for i, v in ipairs( data.seul ) do
for i, v in ipairs( data.seul ) do
                if seul == v then
if seul == v or seul == tonumber( v ) then
                    return lien
return lien
                end
end
            end
end
        end
end
        -- partie aucun et pas de lien => nil
-- partie aucun et pas de lien => nil
        return nil
return nil
    elseif type( data.tous ) == 'table'
elseif type( data.tous ) == 'table' then
        and type( data.tous[1] ) == 'number'
local tous1, tous2 = tonumber( data.tous[1] ), tonumber( data.tous[2] )
        and type( data.tous[2] ) == 'number'
if tous1 and tous2 and annee >= tous1 and annee <= tous2 then
        and annee >= data.tous[1]
-- l'année est dans la partie 'tous' donc on retourne le lien
        and annee <= data.tous[2]
return lien
    then
end
        -- l'année est dans la partie 'tous' donc on retourne le lien
end
        return lien
-- l'annee n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe.
    end
local cibleLien = mw.title.new( lien )
    -- l'annee n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe.
if cibleLien and cibleLien.exists then
    if mw.title.new( lien ).exists then
return lien
        return lien
end
    end
end
 
---
-- Supprime le jour de la semaine, et "le" avant une date
function fun.nettoyageJour( jour )
if type( jour ) == 'string' then
local nomJour = { '[Ll]undi', '[Mm]ardi', '[Mm]ercredi', '[Jj]eudi', '[Vv]endredi',
'[Ss]amedi', '[Dd]imanche', '^ *[Ll]e' }
local premier = { '<abbr class="abbr" title="Premier" >1<sup>er</sup></abbr>', '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 un table contenant les champs jour, mois et annee.
-- la date doit contenir le mois.
function fun.separationJourMoisAnnee( date )
date = trim( date )
if date then
local function erreur( periode, valeur )
return false, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' )
end
local jour, mois, annee, masquerMois, masquerAnnee, 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 aj = '(%-?%d+)'      -- année ou jour
local s = '[ ./-]+'          -- séparateur simple
local sep = '([ ./-]+)'      -- séparateur avec capture, pour le détecter deux fois
local moins = '(%-?)'        -- signe moins pour signifier qu'il ne faut pas afficher cette donnée
local regexb = {
jmmm = '^'..j..s..mmm..moins..'$',
mmmjva = '^'..mmm..s..j..', ?'..aj..'$',
}
date = fun.nettoyageJour( date )
-- 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
:gsub( '\194\160', ' ' )
:gsub( '&nbsp;', ' ' )
:gsub( '\226\128\175', ' ' )
:gsub( '&nnbsp;', ' ' )
:gsub( '\226\128\137', ' ' )
:gsub( '&thinsp;', ' ' )
:gsub( '&#32;', ' ' )
:gsub( ' +', ' ' )
-- réduction av. J-C pour simplifier un peu les regex :
:gsub( '(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?', '-%1' )
-- supression de l'heure dans les date 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( '^'..j..s..mmm..moins..'$' ) then
-- jj mmmm
jour, mois, masquerMois = date:match( '^'..j..s..mmm..moins..'$' )
elseif date:match( '^'..mmm..s..j..', ?'..aj..'$') then
-- mmm jj, aaaa (format anglo-saxon)
mois, jour, annee = date:match( '^'..mmm..s..j..', ?'..aj..'$')
elseif date:match( '^'..mmm..'$' ) then
mois = date
else
return erreur( 'Date', date )
end
local jn, an = tonumber( jour ), tonumber( annee )
if jn and an and ( jn > 31 or jn < 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
 
 
---
-- separationJourMoisAnnee prend jusqu'a cinq paramètre et essaie de les séparer en jour, mois, annee et qualificatif
-- la date peut être dans le premier paramètre ou séparée dans les paramètre 1 à 3 ou 2 à 4.
-- Le qualificatif est cherché dans le paramètre suivant.
-- La fonction retourne true suivit d'une table avec la date en paramètres nommé (sans accent sur année)
-- ou false suivit d'un message d'erreur.
function fun.validationJourMoisAnnee( frame, ... )
local args = Outils.extractArgs( frame, ... )
local jour, mois, numMois, annee, erreur
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, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' )
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 = string.match( string.upper( bannee ), '^(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?' )
annee = tonumber( annee )
if annee then
annee = 0 - annee
else
return erreur( 'Année', bannee )
end
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.valideSaison( bmois )
if mois == nil then
return erreur( 'Mois', bmois )
end
else
-- on traite le jour si présent
if Outils.notEmpty( bjour ) then
jour = tonumber( bjour )
if jour == nil then
jour = tonumber( fun.nettoyageJour( bjour ) )
end
if jour == nil then
return erreur( 'Jour', bjour )
end
-- on valide que le jour est correct
if jour < 1 or jour > 31 then
return erreur( 'Jour', bjour )
elseif jour > liste_mois[numMois].nJour then
return erreur( 'Jour', bjour .. ' ' .. mois )
elseif jour == 29 and numMois == 2 and annee and ( math.fmod( annee, 4 ) ~= 0 ) then
-- l'année bisextile sur les siècles est toujours acceptée pour être compatible avec les dates juliennes.
return erreur( 'Jour', '29 février ' .. annee  )
end
else
-- S'il n'y a pas de jour on regarde si la première lettre du mois est en majuscule
if bmois:match( '^%u' ) then
-- oui, on passe la première lettre en majuscule
mois = ucfirst( mois )
end
-- s'il n'y a pas d'année non plus on retourne le mois simple
end
end
else
-- on teste le jour si présent
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, Outils.erreur( 'année improbable (' .. annee .. ')' )
end
local resultat = {
jour = jour,
mois = mois,
numMois = numMois,
annee = annee,
masquerAnnee = args.masquerAnnee,
masquerMois = args.masquerMois,
}
return true, resultat
end
end


function fun.modeleDateAnalyseJMA( args )
local function masquerParam( p ) -- sépare le signe moins final éventuel signifiant que le paramètre ne soit pas être affiché.
local s
if trim( p ) then
p, s = p:match( '^(.-)(%-?)%s*$' )
end
return p, ( s == '-' or nil )
end
local test, resultat
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, resultat = fun.separationJourMoisAnnee( arg1 )
if test then
resultat.qualificatif = arg2
end
else
local param, masquerM, masquerA
param = { args[1] or args.jour }
param[2], masquerM = masquerParam( args[2] or args.mois )
param[3], masquerA = masquerParam( args[3] or args.annee or args['annee'] )
param[4] = masquerParam( args[4] )
test, resultat = fun.validationJourMoisAnnee( param )
if test then
resultat.masquerAnnee = masquerA
resultat.masquerMois = masquerM
resultat.qualificatif = trim( args[4] )
end
end
return test, resultat
end
---
-- émule le modèle {{m|Date}}.
-- émule le modèle {{m|Date}}.
-- Paramètres :
-- Paramètres :
--     1 : jour (numéro ou "1er"). optionnel, si absent pas de jour
-- 1 : jour (numéro ou "1er"). optionnel, si absent pas de jour
--     2 : mois (en toutes lettres)
-- 2 : mois (en toutes lettres)
--     3 : année (nombre)
-- 3 : année (nombre)
--     4 : optionnel, spécialité de l'année
-- 4 : optionnel, spécialité de l'année
--     Comportement spécial ("truc à deux balles au lieu d'utiliser un
-- Comportement spécial ("truc à deux balles au lieu d'utiliser un
--     paramètre nommé du genre "sans année=oui"...") : si 1 est vide
-- paramètre nommé du genre "sans année=oui"...") : si 1 est vide
--     mais que le reste est complet → on n'affiche pas l'année
-- mais que le reste est complet → on n'affiche pas l'année
function fun.modeleDate( frame )
function fun.modeleDate( frame )
    local args = frame.args or frame
local args = Outils.extractArgs( frame )
    if args[1] == nil and args[3] == nil and frame.getParent then
local test, params = fun.modeleDateAnalyseJMA( args )
        args = frame:getParent().args
local cat, resultat = ''
    end
if test then
   
local listeParam = {
    -- chargement de la base de donnée répertoriant certaines pages existant ou n'existant pas pour éviter les "ifexist".
qualificatif = 'qualificatif',
    local dataLiens       
age = 'âge',
    local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' )
['âge'] = 'âge',
    if success then
naissance = 'naissance',
        dataLiens = resultat
mort = 'mort',
    else
['décès'] = 'mort',
        -- protection au cas ou le sous module serait mal modifié
julien = 'julien',
        dataLiens = { [''] = { mois = { aucun = 1000, tous = { 1938, 2013 } }, }, parametresPage = { } }
avJC = 'avJC',
    end
nolinks = 'nolinks',
   
}
    local annee, mois, numMois, jour
for n, v in pairs( listeParam ) do
    local decalage = 0
params[v] = params[v] or args[n]
    -- si pas de jour mais que args[2] est un mois on décale tout et on
end
    -- n'affiche pas l'année
resultat = fun._modeleDate( params )
    if trim( args[1] ) == nil and fun.valideMois( trim( args[3] ) ) ~= nil then
        decalage = 1
    end
   
    -- on traite l'année
    local bannee = trim( args[3 + decalage] )
    if bannee then
        annee = tonumber( bannee )
        if annee == nil then
            -- test si l'année contient av. J.-C.
            annee = mw.ustring.match( mw.ustring.upper( bannee ), '^(%d+)%sAV%.?%s?J%.?%-?C' )
            annee = tonumber( annee )
            if annee then
                annee = 0 - annee
            else
                return erreurDate( 'Année invalide (' .. bannee .. ')' )
            end
        end
    else
        annee = nil
    end


    -- on traite le mois
else
    local bmois = trim (args[2 + decalage] )
local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
    if bmois then
if namespaceCategorisation[ mw.title.getCurrentTitle().namespace ] and not Outils.notEmpty( args.nocat ) then
        mois, numMois = fun.valideMois( bmois )
cat = '[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée]]'
        if mois == nil then  
end
            -- on teste si le mois est fourni sous forme numérique
resultat = params .. cat
            numMois = tonumber( bmois )
end
            mois = fun.nomDuMois( numMois )
return resultat or ''
        end
end
        if mois == nil then
 
            return erreurDate( 'Mois invalide (' .. bmois .. ')' )
function fun._modeleDate( args )
        else
local annee, mois, numMois, jour = args.annee, args.mois, args.numMois, args.jour
       
local qualificatif = args.qualificatif
            -- on traite le jour si présent
            local bjour = trim( args[1 + decalage] )
if ( annee or mois or jour ) == nil then
            if bjour then
return
                jour = tonumber( bjour )
end
                if jour == nil then
                    if bjour == '1er'
-- on traite l'age, naissance et mort
                        or bjour == '<abbr class="abbr" title="Premier" >1<sup>er</sup></abbr>' -- correspond à {{1er}}
local age = trim( args['âge'] or args['age'] )
                    then
age = age and  fun.age( annee, numMois, jour )
                        jour = 1
local naissance = trim( args.naissance )
                    else
local mort = trim( args.mort )
                        return erreurDate( 'Jour invalide (' .. bjour .. ')' )
                    end
-- on traite le calendrier
                end
local gannee, gmois, gjour = annee, numMois, jour        -- date suivant le calendrier grégorien pour <time>
                -- on valide que le jour est correct
local jannee, jmois, jjour = annee, mois, jour  -- servira éventuellement à a affiché la date selon le calendrier julien
                if jour < 1 or jour > 31 then
local julien2, julien3 = nil, nil                        -- servira éventuellement à a affiché des parenthèses
                    return erreurDate( 'Jour invalide (' .. bjour .. ')' )
local julien = trim( string.lower( args.julien or '' ) )
                elseif jour > liste_mois[numMois].nJour then
if annee and jour then
                    return erreurDate( 'Jour invalide (' .. bjour .. ' ' .. mois .. ')' )
local amj = annee * 10000 + numMois * 100 + jour
                    -- l'année bisextile n'est pas testée pour accepter les dates juliennes.
if amj < 15821014 then
                end
if annee > 0 then
            else
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
                -- S'il n'y a pas de jour on regarde si la première lettre du mois est en majuscule
else
                if mw.ustring.match( bmois, '^%u' ) then
-- calendrier grégorien proleptique avec année 0.
                    -- oui, on passe la première lettre en majuscule
gannee, gmois, gjour = fun.julianToGregorian( annee + 1, numMois, jour )
                    mois = ucfirst( mois )
end
                end
elseif julien == 'oui' then
                -- s'il n'y a pas d'année non plus on retourne le mois simple
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
            end
annee, mois, jour = gannee, liste_mois[gmois][1], gjour
        end
end
    -- else
else
    --    return erreurDate("Le mois est obligatoire")
if annee and annee < 0 then
    end
gannee = gannee + 1
end
end
-- on génère le résultat
-- Déclarations des variables
local wikiListe = TableBuilder.new()  -- reçois le texte affiché pour chaque paramètre
local iso = TableBuilder.new()        -- reçois le format date ISO de ce paramètre
local dataQualificatif, dataCat
if not args.nolinks then
dataQualificatif = dataLiens[qualificatif or '']
if type( dataQualificatif ) ~= 'table' then
-- si le qualifiquatif n'est pas dans la base de donnée, on crée une table minimum,
-- qui imposera un test sur l'annee, mais considère qu'il n'y a pas de lien sur le jour ou le mois
dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } }
end
dataCat = dataLiens[dataQualificatif.cat]
if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then
dataCat = { qualificatif = '' }
end
end
local function wikiLien( lien, texte )
if lien == texte then
return texte
else
return texte
end
end
-- Date julienne
if jjour ~= jour then
if jjour == 1 then
jjour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
end
if jannee ~= annee then
julien3 = '(<abbr class=abbr title="selon le calendrier julien">' .. jjour .. ' ' .. jmois .. ' ' .. jannee .. '</abbr>)'
else
julien2 = '(<abbr class=abbr title="selon le calendrier julien">' .. jjour .. ' ' .. jmois .. '</abbr>)'
end
end


    -- on traite le champs optionnel
-- le jour si présent
    local qualificatif = trim( args[4 + decalage] ) or dataLiens.parametresPage.qualificatif
local qualifJour = ''
   
if jour then
    -- on traite l'age
local texteJour = jour
    local age = trim( args['âge'] or args['age'] )
if args.nolinks then
    age = age and  fun.age( annee, numMois, jour )
if jour == 1 then
   
jour = modelePremier
    -- on traite le calendrier
end
    local gannee, gmois, gjour = annee, numMois, jour      -- date suivant le calendrier grégorien pour <time>
wikiListe.insert( jour )
    local jannee, jmois, jjour, julien = annee, mois, jour  -- servira éventuellement à a affiché la date selon le calendrier julien
else
    local julien1, julien2, julien3 = '', '', ''           -- servira éventuellement à a affiché des parenthèses
qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif
    local julien = trim( string.lower( args.julien or dataLiens.parametresPage.julien or '' ) )
or dataCat.jour and dataCat.qualificatif
    if annee and jour then
or ''
        local amj = annee * 10000 + numMois * 100 + jour
local lien = jour .. ' ' .. mois .. ' ' .. qualifJour
        if amj < 15821014 then
if jour == 1 then
            gannee, gmois, gjour = fun.jullianToGregorian( annee, numMois, jour )
jour = '1<sup>er</sup>'
        elseif julien == 'oui' then
lien = '1er ' .. mois .. ' ' .. qualifJour
            gannee, gmois, gjour = fun.jullianToGregorian( annee, numMois, jour )
end
            annee, numMois, jour = gannee, liste_mois[gmois][1], gjour
-- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour.
        end
wikiListe.insert( wikiLien( lien, jour ) )
    end
wikiListe.insert( wikiLien( lien, jour .. ' '.. mois ) )
end
iso.insert( 1, string.sub( '0' .. gjour, -2 ) )
end
-- le mois
if mois then
if #wikiListe == 0 and annee == nil then
return mois
end
if args.nolinks then
if not args.masquerMois then
wikiListe.insert( mois )
end
else
local lien
if annee then
lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois )
if lien == nil and qualificatif and qualifJour == '' then
-- test nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif.
lien = existDate( dataLiens[''], annee, mois )
end
end
if lien or args.masquerMois then
-- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois']]
wikiListe.remove()
if not args.masquerMois then
wikiListe.insert( wikiLien( lien, mois ) )
end
elseif #wikiListe > 0 then
-- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois'
wikiListe.remove( #wikiListe - 1 )
elseif args.masquerAnnee then
-- s'il n'y a pas de jour et que l'année n'est pas affichée, on insère le mois seul.
wikiListe.insert( mois )
end
end
if gmois then
iso.insert( 1, string.sub( '0' .. gmois, -2 ) )
end
end
if( julien2 ) then
wikiListe.insert( julien2 )
end
-- l'année
if annee then
if not args.masquerAnnee then
local texteAnnee = annee
local lien
if annee < 0 then
local annneeAvJc = 0 - annee
lien = lien or ( annneeAvJc .. ' av. J.-C.' )
local avJC = trim( string.lower( args.avJC or '' ) )
if args.avJC == 'non' then
texteAnnee = annneeAvJc
else
texteAnnee = annneeAvJc .. ' <abbr class="abbr" title="'
.. annneeAvJc .. ' avant Jésus-Christ">av. J.-C.</abbr>'
end
end
if args.nolinks then -- seulement si on doit l'affichée
wikiListe.insert( texteAnnee )
else
lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee ) or lien or annee
if mois and #wikiListe == 0 then
-- si le mois n'a pas de lien et n'est pas affiché avec le jour, il est affiché avec l'année.
texteAnnee = mois .. ' ' .. texteAnnee
end
wikiListe.insert( wikiLien( lien, texteAnnee ) )
end
end
if gannee > 999 then
iso.insert( 1, gannee )
elseif gannee > -1 then
iso.insert( 1, string.sub( '000' .. gannee , -4 ) )
elseif gannee > -999 then
-- calendrier grégorien proleptique avec année 0.
iso.insert( 1, 'U-' .. string.sub( '000' .. ( 0 - gannee ), -4 ) )
else
iso.insert( 1, 'U' .. gannee )
end
end
if( julien3 ) then
wikiListe.insert( julien3 )
end


   
    -- on génère le résultat
-- l'age
   
if type( age ) == 'number' and age >= 0 and ( not naissance or age < 120 ) then
    -- Déclarations des variables
if age == 0 then
    local wikiListe = TableBuilder.new()   -- reçois le texte affiché pour chaque paramètre
age = '(moins d’un&nbsp;an)'
    local iso = TableBuilder.new()         -- reçois le format date ISO de ce paramètre
elseif age == 1 then
age = '(1&nbsp;an)'
else
age = '(' .. age .. '&nbsp;ans)'
end
else
age = false
end
-- compilation du résultat
local wikiTexte = wikiListe.concat( ' ' )
local isoTexte = iso.concat( '-' )
-- On ajoute un peu de sémantique.
local wikiHtml = mw.html.create( '' )
local dateHtml = wikiHtml:tag( 'time' )
:wikitext( wikiTexte )
if wikiTexte:match( ' ' ) then
dateHtml:addClass( 'nowrap' )
end
if isoTexte ~= wikiTexte then
dateHtml:attr( 'datetime', isoTexte )
end
if not args.nolinks then
dateHtml:addClass( 'date-lien' )
end
if naissance then
dateHtml:addClass( 'bday' )
elseif mort then
dateHtml:addClass( 'dday' )
end
if age then
wikiHtml:wikitext( ' ' )
:tag( 'span' )
:addClass( 'noprint')
:wikitext( age )
:done()
end
return tostring( wikiHtml )
end


    local dataQualificatif = dataLiens[qualificatif or '']
    if type( dataQualificatif ) ~= 'table' then
        -- si le qualifiquatif n'est pas dans la base de donnée, on crée une table minimum,
        -- qui imposera un test sur l'annee, mais considère qu'il n'y a pas de lien sur le jour ou le mois
        dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } }
    end
    local dataCat = dataLiens[dataQualificatif.cat]
    if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then
        dataCat = { qualificatif = '' }
    end
   
    -- Date julienne
    if jjour ~= jour then
        wikiListe.insert( '<abbr title="selon le calendrier julien" >' .. jjour )
        julien1 = '('
        if jannee ~= annee then
            wikiListe.insert( jmois )
            wikiListe.insert( jannee .. '</abbr>' )
            julien3 = ')'
        else
            wikiListe.insert( jmois .. '</abbr>' )
            julien2 = ')'
        end
    end
   
    -- le jour si présent
    local qualifJour = ''
    if jour then
        -- éphémérides du qualificatif, ou de sa catégorie, ou générale.
        qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif
            or dataCat.jour and dataCat.qualificatif
            or ''
        local lien = jour .. ' ' .. mois .. ' ' .. qualifJour
        if jour == 1 then
            jour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
            lien = '1er ' .. mois .. ' ' .. qualifJour
        end
        -- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour.
        wikiListe.insert( julien1 .. '[[' .. lien .. '|' .. jour .. ']]' )
        wikiListe.insert( julien1 .. '[[' .. lien .. '|' .. jour .. ' '.. mois .. ']]' .. julien2 )
        iso.insert( 1, string.sub( '0' .. gjour, -2 ) )
    end


    -- le mois
---
    if mois then
-- fonction destinée aux infobox, notamment pour afficher les dates de naissance et de mort
        local lien
-- les liens présent dans les dates fournies sont automatiquement supprimées pour gérer les cas ou
        if annee then
-- le paramètre contient déjà un modèle date.
            lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois )
-- Paramètres :
            if lien == nil and qualificatif and qualifJour == '' then
-- 1 : type de date à afficher (naissance / n, mort / m, ou date / d)
                -- test nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif.
-- 1 : Date ou date de naissance
                lien = existDate( dataLiens[''], annee, mois )
-- 2 : Date de mort si type n ou m
            end
-- qualificatif = suffixe des page de date à lier (exemple : en musique)
        end
-- nolinks : n'affiche pas de lien
        if lien then
function fun.dateInfobox( frame )
            -- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois']]
local args = frame.args
            wikiListe.remove()
if type( args ) ~= 'table' or not ( args[1] and args[2] ) then
            wikiListe.insert( '[[' .. lien .. '|' .. mois .. ']]' .. julien2 )
return
        else
end
            -- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois'
            wikiListe.remove( #wikiListe - 1 )
local function analyseDate( d )
            -- s'il n'y avait pas je jour, la liste est vide mais ça ne pose pas de problème
if trim( d ) then
            -- sauf si l'année n'est pas affichée :
-- supprime les liens
            if #wikiListe == 0 and ( annee == nil or decalage > 0 ) then
local analyse = d:match( ' ou ') or d:match( 'entre ' ) or d:match( 'vers ' ) or d:match( 'après ' ) or d:match( 'avant ' )
                return mois
if analyse then
            end
return d
        end
end
        iso.insert( 1, string.sub( '0' .. gmois, -2 ) )
analyse = d:match( 'datetime="([%d-]+)"' ) or d
    end
local debut, fin = analyse:match( '(.-%d%d%d%]*%-?)([\127 ].+)' )
   
if not debut then
    -- l'année
debut, fin = analyse:match( '(.-%d%d%d%]*%-?)(<br ?/?>.+)' )
    if annee and decalage == 0 then -- seulement si on doit l'affichée
end
        local lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee )
analyse = debut or analyse
        local texte = annee
analyse = analyse:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end )
        if annee < 0 then
local t, r = fun.separationJourMoisAnnee( analyse )
            local annneeAvJc = 0 - annee
if t then
            lien = lien or ( annneeAvJc .. ' av. J.-C.' )
return r, fin
            local avJC = trim( string.lower( args.avJC or dataLiens.parametresPage.avJC or '' ) )
else
            if args.avJC == 'non' then
return d, fin
                texte = annneeAvJc
end
            else
end
                texte = annneeAvJc .. '&nbsp;<abbr class="abbr" title="'
end
                    .. annneeAvJc .. ' avant Jésus-Christ">av.&nbsp;J.-C.</abbr>'
            end
local naissance = args[1]:match( '^n' )
        end
local mort = args[1]:match( '^m' ) or args[1]:match( 'décès' )
        lien = lien or annee
local affichageDate, qualificatif = args[2], args[4]
        if mois and #wikiListe == 0 then
local affichageDateTab, resultatDate, complementDate
            -- si le mois n'a pas de lien et n'est pas affiché avec le jour, il est affiché avec l'année.
local dateNaissance, dateMort
            texte = mois .. ' ' .. texte
if mort then
        end
affichageDate = args[3]
        wikiListe.insert( '[[' .. lien .. '|' .. texte .. ']]' .. julien3 )
end
        iso.insert( 1, string.sub( '000' .. annee , -4 ) )
if not trim( affichageDate ) then
    end
return
   
end
    -- l'age
if affichageDate:match( '</time>' ) then
        local classAge
-- 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 type( age ) == 'number' and age >= 0 then
if ( naissance or mort ) and ( affichageDate:match( 'wikidata%-linkback' ))  then
        if age == 0 then
dateNaissance = analyseDate( args[2] )
            age = '<span class="noprint"> (moins d\'1 an)</span>'
dateMort = analyseDate( args[3] )
        elseif age == 1 then
resultatDate = affichageDate
            age = '<span class="noprint"> (1 an)</span>'
else
        else
return affichageDate
            age = '<span class="noprint"> (' .. age .. ' ans)</span>'
end
        end
else
        classAge = 'class="bday" '
affichageDateTab, complementDate = analyseDate( affichageDate )
    else
if type( affichageDateTab ) ~= 'table' then  
        age = ''
return affichageDateTab
        classAge = ''
else
    end
if naissance then
   
dateNaissance = affichageDateTab
    -- compilation du résultat
dateMort = analyseDate( args[3] )
    local wikiText = wikiListe.concat( '&nbsp;' )
elseif mort then
    -- On ajoute un peu de sémantique. La date doit être dans le calendrier grégorien proleptique
dateNaissance = analyseDate( args[2] )
    -- ce formatage ne s'applique pas avant J.C.
dateMort = affichageDateTab
    if wikiText ~= '' and ( gannee == nil or gannee > 0) then  
else
        wikiText = '<time '.. classAge .. 'datetime="' .. iso.concat( '-' ) .. '">' .. wikiText .. '</time>'
qualificatif = args[3]
    end
end
   
affichageDateTab.naissance = naissance
    return wikiText .. age
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 .. '&nbsp;ans' .. suffixAge
elseif calculAge == 1 then
age = prefixAge .. 'un&nbsp;an' .. suffixAge
elseif calculAge == 0 then
age = prefixAge .. 'moins d’un&nbsp;an' .. suffixAge
end
if complementDate and complementDate:match( 'ans?%)' ) then
complementDate = ''
end
end
return resultatDate .. ( complementDate or '' ) .. age
end
end


-- voir émule le modèle:Inscription date
-- la détection des arguments permet d'utilisé la fonction depuis un modèle, depuis invoke, ou depuis une autre fonction.
-- pour facilité l'écriture de lua, annee (sans accent) est accepté lors de l'appel depuis lua.
function fun.modeleInscriptionDate(frame)
    local annee, mois, jour
    if frame.getParent then
        annee = frame:getParent().args ['année'] or frame.args ['année'] or frame.args.annee or ''
        mois = frame:getParent().args.mois or frame.args.mois or ''
        jour = frame:getParent().args.jour or frame:getParent().args ['quantième'] or frame.args.jour or ''
    else
        annee = frame ['année'] or frame.annee or ''
        mois = frame.mois or ''
        jour = frame.jour or ''
    end
    if annee == '' then
        -- si annee n'est pas précisé, on utilise la paramètre date
        annee = frame.getParent and (frame:getParent().args.date or frame.args.date) or frame.date or ''
        if annee == '' then
            return ''
        else
            return '<span class="nowrap">' .. annee .. '</span>'
        end
    else
        -- si l'année est renseigné, on essaye de trouver le mois
        mois = fun.determinationMois (mois, frame)
        if mois then
            mois = mois .. '&nbsp;'
            if jour ~= '' then
                -- si le mois est valide on détermine le jour
                jour = tonumber(jour) or jour      -- suppresion des 0 qui trainent
                if jour == 1 or jour == '1er' then
                    jour = '<abbr class="abbr" title="Premier">1<sup>er</sup></abbr>'
                end
                jour = jour .. '&nbsp;'
            end
            return jour .. mois .. annee
        else
            return annee
        end
    end
end


---
-- 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]]
Ligne 428 : Ligne 844 :
-- 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 )
    local annee, mois, jour
local args = Outils.extractArgs( frame )
    if frame.getParent then
local annee = Outils.notEmpty( args['année'], args.annee, args.year, args.date )
        annee = frame:getParent().args ['année'] or frame.args ['année'] or frame.args.annee or ''
-- extraction de l'année
        mois = frame:getParent().args.mois or frame.args.mois or ''
if type( annee ) == 'string' then
        jour = frame:getParent().args.jour or frame:getParent().args ['quantième'] or frame.args.jour or ''
annee = ( tonumber( annee ) -- match '2013'
    else
or string.match ( annee, '%D(%d%d%d%d)%D' ) -- match  '[[2013 en musique|2013]]'
        annee = frame ['année'] or frame.annee or ''
or string.match ( annee, '%D(%d%d%d%d)$' )  -- match '17 septembre 2013'
        mois = frame.mois or ''
or string.match ( annee, '^(%d%d%d%d)%D' ) -- match '2013-09-17'
        jour = frame.jour or ''
)
    end
end
    annee = tonumber ( annee ) or string.match ( annee, '%D(-? ?%d?%d?%d%d)%D' )
annee = tonumber( annee )
    if annee then
        annee = string.sub( '000' .. annee, -4 )
-- le format de date iso est défini suivant le calendrier grégorien.
        local nomMois, numMois = fun.determinationMois( mois, frame )
-- Avant l'année 1583 la date est calendrier est probablement du calendrier julien,
        if numMois then
-- donc autant s'abstenir.
            if numMois < 10 then
if annee and annee > 1582  then
                mois = '-0' .. numMois
local mois = Outils.notEmpty( args.mois, args.month )
            else
-- num mois trouve le numéro du mois, qu'il soit numérique ou texte, complet ou abrégé.
                mois = '-' .. numMois
local nomMois, numMois = fun.determinationMois( mois )
            end
if numMois then
            local bjour = tonumber( jour ) or tonumber( string.match ( jour, '%D?(%d?%d)%D?') )
mois = '-' .. string.sub( '0' .. numMois, -2 )
            if bjour then
                if bjour < 10 then
local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
                    jour = '-0' .. bjour
if type( jour ) == 'string' then
                else
jour = tonumber( jour ) or tonumber( string.match ( jour, '%d+') )
                    jour = '-' .. bjour
end
                end
jour = tonumber( jour )
            else
if jour and jour <= liste_mois[numMois].nJour then
                jour = ''
jour = '-' .. string.sub( '0' .. jour, -2 )
            end
return annee .. mois .. jour
        else
else
            mois = ''
return annee .. mois
            jour = ''
end
        end
else
        return annee .. mois .. jour
return tostring( annee )
    end
end
end
end
end


 
---
-- Rang du jour dans l'année
-- Rang du jour dans l'année
-- Usage : do_dayRank{année,mois,jour}
-- Usage : do_dayRank{année,mois,jour}
function fun.do_dayRank(arguments)
function fun.do_dayRank(arguments)
    local yr = tonumber(arguments.year or arguments[1]) or 1
local yr = tonumber(arguments.year or arguments[1]) or 1
local mt = tonumber(arguments.month or arguments[2]) or 1
local mt = tonumber(arguments.month or arguments[2]) or 1
local dy = tonumber(arguments.day or arguments[3]) or 1
local dy = tonumber(arguments.day or arguments[3]) or 1
Ligne 498 : Ligne 915 :
end
end


-- Test d'année bissextile
-- Test d'année bissextile (Suit le calendrier grégorien)
function fun.isLeapYear(year)
function fun.isLeapYear(year)
local yr = tonumber(year) or 1
local yr = tonumber(year) or 1
Ligne 514 : Ligne 931 :
else
else
for i=1,7,2 do
for i=1,7,2 do
p = pattern[n%10 + 1]
local p = pattern[n%10 + 1]
for j=0,2 do
for j=0,2 do
p = string.gsub(p,tostring(j),letters[i+j])
p = string.gsub(p,tostring(j),letters[i+j])
Ligne 528 : Ligne 945 :
function fun.dateRepublicain(frame)
function fun.dateRepublicain(frame)
local pframe = frame:getParent()
local pframe = frame:getParent()
    local arguments = pframe.args
local arguments = pframe.args
    return fun.formatRepCal(fun.do_toRepCal(arguments))
return fun.formatRepCal(fun.do_toRepCal(arguments))
end
end


---
-- Calcul d'une date dans le calendrier républicain
-- Calcul d'une date dans le calendrier républicain
-- On suppose que les années 4n+3 sont sextiles (3, 7, 11...)
-- On suppose que les années 4n+3 sont sextiles (3, 7, 11...)
Ligne 544 : Ligne 962 :
end
end


---
-- Formatage d'une date selon le calendrier républicain
-- Formatage d'une date selon le calendrier républicain
-- Usage : fun.formatRepCal{année,mois,jour}
-- Usage : fun.formatRepCal{année,mois,jour}
Ligne 559 : Ligne 978 :
end
end


---
-- 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'age en fonction de la ou les dates fournies. La valeur retounée est de type 'number'
Ligne 565 : Ligne 985 :
-- 4, 5, 6 : année, mois, joue du calcul (facultatif, par défaut la date UTC courante).
-- 4, 5, 6 : année, mois, joue du calcul (facultatif, par défaut la date UTC courante).
function fun.age( an, mn, jn, ac, mc, jc )
function fun.age( an, mn, jn, ac, mc, jc )
    local an = tonumber( an )  
if ac == nil then
    if an == nil then  
local today = os.date( '!*t' )
        -- pas de message d'erreur qui risque de faire planter la fonction appelante
ac = today.year
        -- à elle de gérer ce retour.
mc = today.month
        return  
jc = today.day
    end
else
    -- les jours et mois sont par défaut égal à 1, pour pouvoir calculer un age même si la date est incompète.
ac = tonumber( ac )
    local mn = tonumber( mn ) or 1
mc = tonumber( mc )
    local jn = tonumber( jn ) or 1
jc = tonumber( jc )
   
end
    local today = os.date( '!*t' )
 
    local ac = tonumber( ac ) or today.year
local an = tonumber( an )
    local mc = tonumber( mc ) or today.month
local mn = tonumber( mn )
    local jc = tonumber( jc ) or today.day
local jn = tonumber( jn )
   
 
    local age = ac - an
if an == nil or ac == nil or mn == nil or mc == nil then
    if mc < mn or ( mc == mn and jc < jn ) then
-- pas de message d'erreur qui risque de faire planter la fonction appelante
        age = age - 1
-- à elle de gérer ce retour.
    end
return
    return age
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
end


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


---
-- calcul du jour julien à partir d'une date du calendrier grégorien
-- calcul du jour julien à partir d'une date du calendrier grégorien
function fun.julianDay( year, month, day, hour, minute, second )
function fun.julianDay( year, month, day, hour, min, sec )
    local julian  
local julian
    julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 )
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 ) / 100 )
          + math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 400 )
+ math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 400 )
          + math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
+ math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
          + day + ( hour or 12 ) / 24 + ( minute or 0 ) / 1440 + ( second or 0 ) / 86400
+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400
          - 32167.5
- 32167.5
    return julian
return julian
end
end


-- calcul du jour julien à partir d'une date du calendrier julier
---
function fun.julianDayJulian( year, month, day, hour, minute, second )
-- calcul du jour julien à partir d'une date du calendrier julien
    local julian  
function fun.julianDayJulian( year, month, day, hour, min, sec )
    julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 )
local julian
          + math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 )
          + day + ( hour or 12 ) / 24 + ( minute or 0 ) / 1440 + ( second or 0 ) / 86400
+ math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
          - 32205.5
+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400
    return julian
- 32205.5
return julian
end
end


-- calcul d'une date dans le calendrier Grégorien à partir du jour julien
---
function fun.jullianDayToGregorian( jd )
-- calcul d'une date dans le calendrier grégorien à partir du jour julien
    local base = math.floor( jd + 32044.5 )  -- 1 March -4800 (proleptic Gregorian date)
function fun.julianDayToGregorian( julianDay )
    local nCentury = math.floor( ( base * 4 + 3 ) / 146097 )
local base = math.floor( julianDay + 32044.5 )  -- 1 March -4800 (proleptic Gregorian date)
    local sinceCentury = base - math.floor( nCentury * 146097 / 4 )
local nCentury = math.floor( ( base * 4 + 3 ) / 146097 )
    local nYear = math.floor( ( sinceCentury * 4 + 3 ) / 1461 )
local sinceCentury = base - math.floor( nCentury * 146097 / 4 )
    local sinceYear = sinceCentury - math.floor( nYear * 1461 / 4 )
local nYear = math.floor( ( sinceCentury * 4 + 3 ) / 1461 )
    local nMonth = math.floor( ( sinceYear * 5 + 2 ) / 153 )
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 day = sinceYear - math.floor( (  nMonth  * 153 + 2 ) / 5 ) + 1
    local year = math.floor( sinceYear / 306 ) + nYear + 100 * nCentury - 4800
local month = nMonth  - math.floor(  nMonth  / 10 ) * 12 + 3
   
local year = math.floor( sinceYear / 306 ) + nYear + 100 * nCentury - 4800
    return year, month, day
return year, month, day
end
end


function fun.jullianToGregorian( year, month, day )
---
    return fun.jullianDayToGregorian( fun.julianDayJulian( year, month, day ) )
-- calcul d'une date dans le calendrier julien à partir du jour julien
-- calcul basé sur l'algorythme de la page fr.wikipedia.org/wiki/Jour_julien (1/10/2013)
function fun.julianDayToJulian( julianDay )
local year = math.modf( ( julianDay * 4 - 6884469 ) / 1461 )
local r2 = julianDay - math.modf( ( 1461 * year + 6884472 ) / 4 )
local month = math.modf( ( 5 * r2 + 461 ) / 153 )
local day = r2 - math.modf( ( 153 * month - 457 ) / 5 ) + 1
if month > 12 then
year = year + 1
month = month - 12
end
return year, month, day
end
end


---
-- calcul d'une date dans le calendrier grégorien à partir d'une date dans le calendrier julien
function fun.julianToGregorian( year, month, day )
return fun.julianDayToGregorian( fun.julianDayJulian( year, month, day ) )
end
---
-- calcul d'une date dans le calendrier julien à partir d'une date dans le calendrier grégorien
function fun.gregorianToJulian( year, month, day )
year = tonumber(year)
if month then month = tonumber(month) else month = 6 end --prend une valeur centrale pour donner un best "guess"
if day then day = tonumber(day) else day = 15 end
return fun.julianDayToJulian( fun.julianDay( year, month, day ) )
end
---
-- erreurModuleData affiche  d'un message d'erreur si le Module:Langue/Data n'a pas été chargé correctement,
-- erreurModuleData affiche  d'un message d'erreur si le Module:Langue/Data n'a pas été chargé correctement,
-- pour la page de discussion de la base de donnée et ceux qui veulent surveiller cette page.
-- pour la page de discussion de la base de donnée et ceux qui veulent surveiller cette page.
function fun.erreurModuleData()
function fun.erreurModuleData()
    local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' )
local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' )
    if success == false then  
if success == false then
        local message = [[<strong class="error">Le chargement du module Date/Data génère une erreur : </strong><br />%s<br />
local message = [[<strong class="error">Le chargement du module Date/Data génère une erreur : </strong><br />%s<br />


<span class="error">Cette erreur doit être corrigée au plus vite car des milliers de page ne s'affichent pas correctement</span>
<span class="error">Cette erreur doit être corrigée au plus vite car des milliers de page ne s'affichent pas correctement</span>
]]
]]
        return string.format( message, resultat )
return string.format( message, resultat )
    end
end
end
end


---
-- checkDataCat génère des liens vers les pages annuelles, mensuelles et d'éphémérides liè aux
-- catégories du Module:Date/Data. La date la plus ancienne dépend de 'aucun' et 'seul[1]'
-- Paramètres :
-- 1 : la catégorie. Il y aura une section par qualificatif de cette catégorie.
-- mois : oui pour avoir les liens vers les pages mensuelles et éphémérides (4 jours dans l'année)
-- alias : pour avoir des lien pour les alias en plus des qualificatif
function fun.checkDataCat( frame )
function fun.checkDataCat( frame )
    local category = trim(frame.args[1]) or ''
local category = trim(frame.args[1])
    local monthLinks = frame .args[2] == 'oui'
local monthLinks = frame .args.mois == 'oui'
    local all = frame.args[3] == 'oui'
local alias = frame.args.alias == 'oui'
    local dataLink = mw.loadData( 'Module:Date/Data' )
local dataLink = mw.loadData( 'Module:Date/Data' )
    local wikiList =  TableBuilder.new()
local wikiList =  TableBuilder.new()
    local currentYear = tonumber( os.date( '%Y' ) )
local currentYear = tonumber( os.date( '%Y' ) )
    local columns = '<div style="-moz-column-width:5em;-webkit-column-width:5em;column-width:5em;-moz-column-gap:1em;-webkit-column-gap:1em;column-gap:1em;text-align:left;">'
local columns = '<div style="-moz-column-width:5em;-webkit-column-width:5em;column-width:5em;-moz-column-gap:1em;-webkit-column-gap:1em;column-gap:1em;text-align:left;">'
    local newSection  
local newSection
    if monthLink then  
if monthLinks then
        newSection = '\n\n== %s ==\n\n=== Années ===\n' .. columns
newSection = '\n\n== %s ==\n\n=== Années ===\n' .. columns
    else
else
        newSection ='\n\n== %s ==\n' .. columns
newSection ='\n\n== %s ==\n' .. columns
    end
end
    for field, dataField in pairs( dataLink ) do
for field, dataField in pairs( dataLink ) do
        -- boucle sur tous les qualificatif ayant pour catégorie le premier paramère
-- boucle sur tous les qualificatif ayant pour catégorie le premier paramère
        if dataField.cat == category or ( category == 'cat' and dataField.cat == field ) then
if dataField.cat == category or ( category == 'cat' and dataField.cat == field ) then
            local monthInitialYear, initialYear
local monthInitialYear, initialYear
            -- définition de l'année à partir de laquelle on va tester toutes les année / mois
-- définition de l'année à partir de laquelle on va tester toutes les année / mois
            if dataField.qualificatif == field or ( category == 'cat' and dataField.cat == field ) then
if dataField.qualificatif == field or ( category == 'cat' and dataField.cat == field ) then
                if dataField.annee and dataField.annee.aucun and dataField.annee.aucun < currentYear then
if dataField.annee and dataField.annee.aucun and dataField.annee.aucun < currentYear then
                    local aucun = dataField.annee.aucun
local aucun = ( dataField.annee.seul and dataField.annee.seul[1] ) or dataField.annee.aucun
                    initialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), 1980 )
initialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), currentYear - 50 )
                else  
else
                    initialYear = currentYear - 50
initialYear = currentYear - 50
                end
end
                if dataField.mois and dataField.mois.aucun and dataField.mois.aucun < currentYear then
if dataField.mois and tonumber( dataField.mois.aucun ) and ( tonumber( dataField.mois.aucun ) < currentYear ) then
                    local aucun = dataField.mois.aucun
local aucun = dataField.mois.aucun
                    monthInitialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), currentYear - 8 )
monthInitialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), currentYear - 8 )
                else  
else
                    monthInitialYear = currentYear - 8
monthInitialYear = currentYear - 8
                end
end
            elseif all then
elseif alias then
                -- si le paramètre 2 est défini on teste aussi tous les alias depuis 1980.
-- si le paramètre alias est défini on teste aussi tous les alias, sinon ils sont ignorés
                initialYear = currentYear - 50
initialYear = currentYear - 50
                monthInitialYear = currentYear - 8
monthInitialYear = currentYear - 8
            end
end
           
            -- création de l'ensembles des liens
-- création de l'ensembles des liens
            if initialYear then
if initialYear then
                -- ajout de lien vers les pages annuelles de l'année en court + 5 jusqu'à initialYear
-- ajout de lien vers les pages annuelles de l'année en court + 5 jusqu'à initialYear
                wikiList.insert( string.format( newSection, field ) )
wikiList.insert( string.format( newSection, field ) )
                local fieldLink = ' ' .. field
local fieldLink = ' ' .. field
                if category == 'cat' then  
if category == 'cat' then
                    fieldLink = ' ' .. dataField.qualificatif
fieldLink = ' ' .. dataField.qualificatif
                end
end
                for year = ( currentYear + 5 ), initialYear, -1  do
for year = ( currentYear + 5 ), initialYear, -1  do
                    wikiList.insert( '\n* [[' .. year .. fieldLink ..'|' .. year .. ']]' )
wikiList.insert( '\n* [[' .. year .. fieldLink ..'|' .. year .. ']]' )
                end
end
                wikiList.insert( '\n</div>' )
wikiList.insert( '\n</div>' )
               
                if monthLinks then
if monthLinks then
                    -- insertstion de liens vers les mois de l'année en court + 1 jusqu'à monthInitialYear
-- insertstion de liens vers les mois de l'année en court + 1 jusqu'à monthInitialYear
                    wikiList.insert( '\n\n=== Mois ==='  )
wikiList.insert( '\n\n=== Mois ==='  )
                    local month, sep
local month, sep
                    for year = ( currentYear + 1 ), monthInitialYear, -1  do
for year = ( currentYear + 1 ), monthInitialYear, -1  do
                        wikiList.insert( '\n* ' .. year .. ' : ' )
wikiList.insert( '\n* ' .. year .. ' : ' )
                        sep = ' • '
sep = ' • '
                        for j = 1, 12 do  
for j = 1, 12 do
                            month = ucfirst( liste_mois[j][1] ) .. ' '
month = ucfirst( liste_mois[j][1] ) .. ' '
                            if j == 12 then sep = ''
if j == 12 then sep = ''
                            end
end
                            wikiList.insert( '[[' .. month .. year .. ' ' .. fieldLink .. '|' .. month .. ']]' .. sep )
wikiList.insert( '[[' .. month .. year .. ' ' .. fieldLink .. '|' .. month .. ']]' .. sep )
                        end
end
                    end
end
                   
                    -- insertion de quelques date pour tester les éphémérides
-- insertion de quelques date pour tester les éphémérides
                    wikiList.insert( '\n\n=== Jours ==='  )
wikiList.insert( '\n\n=== Jours ==='  )
                    wikiList.insert( '\n* [[1er janvier ' .. fieldLink .. ']]' )
wikiList.insert( '\n* [[1er janvier ' .. fieldLink .. ']]' )
                    wikiList.insert( '\n* [[14 mars ' .. fieldLink .. ']]' )
wikiList.insert( '\n* [[14 mars ' .. fieldLink .. ']]' )
                    wikiList.insert( '\n* [[22 juin ' .. fieldLink .. ']]' )
wikiList.insert( '\n* [[22 juin ' .. fieldLink .. ']]' )
                    wikiList.insert( '\n* [[3 septembre ' .. fieldLink .. ']]' )
wikiList.insert( '\n* [[3 septembre ' .. fieldLink .. ']]' )
                end
end
            end
end
        end
end
    end
end
   
    return table.concat( wikiList )
return table.concat( wikiList )
end
 
--[[
  Cette fonction retourne "CET" ou "CEST" selon que dans la pseudo-timezone en cours
    c'est l'heure d'été ou l'heure d'hiver.
  Cette fonction n'a de sens a priori que pour des modèles utilisés en Europe
 
  Paramètre optionnel non nommé : "sans lien" : retourne le texte CET/CEST. sinon
    retourne ce même texte avec un wikilien vers les articles correspondant
--]]
function fun.CEST(frame)
-- option : ne pas créer de wikilien
local opt = trim(frame.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
end


return fun
return fun

Dernière version du 23 septembre 2017 à 19:33

local fun = {}

local TableBuilder = require( 'Module:TableBuilder' ) local Outils = require( 'Module:Outils' ) -- chargement de la base de donnée répertoriant certaines pages existant ou n'existant pas pour éviter les "ifexist". local dataLiens local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' ) if success then dataLiens = resultat else -- protection au cas ou le sous module serait mal modifié dataLiens = { [] = { mois = { aucun = 1000, tous = { 1773, 2014 } }, } } end

-- nettoie un paramètre non nommé (vire les espaces au début et à la fin) -- retourne nil si le texte est vide ou n'est pas du texte. Attention c'est important pour les fonction qui l'utilise. local trim = Outils.trim

-- Fonction destiné à mettre la première lettre du mois en majuscule du mois : -- utilisation de string car aucun mois ne commance par une lettre non ascii en français ou anglais. local function ucfirst( str ) return str:sub( 1, 1 ):upper() .. str:sub( 2 ) end

local modelePremier = '1er'


-- liste des mois, écriture exacte et simplifiée, en minuscule local liste_mois = { { "janvier", "jan.", "janv.", "jan", "janv", "january", nJour = 31 }, { "février", "fevrier", "fev.", "fev", "fév.", "fév", "february", nJour = 29 }, { "mars", "mar.", "mar", "march", nJour = 31 }, { "avril", "avr.", "avr", "apr", "april", nJour = 30 }, { "mai", "may", nJour = 31 }, { "juin", "jun", "june", nJour = 30 }, { "juillet", "juil.", "juil", "juill.", "juill", "jul", "july", nJour = 31 }, { "août", "aout", "aou", "aug", "august", nJour = 31 }, { "septembre", "sept.", "sept", "sep.", "sep", "september", nJour = 30 }, { "octobre", "oct.", "oct", "october", nJour = 31 }, { "novembre", "nov.", "nov", "november", nJour = 30 }, { "décembre", "decembre", "déc.", "dec.", "dec", "déc", "december", nJour = 31 }, } fun.liste_mois = liste_mois

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

-- nom du mois à partir du numéro function fun.nomDuMois( num ) if type( num ) ~= "number" or num < 1 or num > 12 then return nil end return liste_mois[num][1] end

--- -- valide que la chaîne passée est un mois valide. -- retourne le nom complet ou nil si non reconnu -- si reconnu, retourne aussi le numéro du mois [1-12] function fun.valideMois( mois ) local m = trim( mois ) if m then m = mw.ustring.lower( m ) for i = 1, 12 do local j = 1 while liste_mois[i][j] do if liste_mois[i][j] == m then return liste_mois[i][1], i end j = j + 1 end end end -- pas trouvé = return nil end

--- -- valide que la chaîne passée est un mois valide. -- retourne le nom complet ou nil si non reconnu -- si reconnu, retourne aussi le numéro du mois [1-12] function fun.valideSaison( saison ) if type( saison ) ~= "string" then return nil end

local m = mw.ustring.lower( trim( saison ) )

for i = 1, 4 do local j = 1 while liste_saison[i][j] ~= nil do if liste_saison[i][j] == m then return liste_saison[i][1] end j = j + 1 end end -- pas trouvé = return nil end

--- -- determinationMois trouve le numéro du mois et son nom, -- à partir de son nom, de son numéro ou d'une expression mathématique. -- Si le deuxième paramètre est vrai, les nombres supérieur à 12 ou non entiers sont acceptés. function fun.determinationMois( mois, mod, boucle ) local num, nom if tonumber( mois ) then num = math.floor( tonumber( mois ) ) if mod then -- si le nombre du mois est calculé par une exression, le résultat peut être supérieur à 12, ou inférieur à 1 num = math.fmod( num + 239, 12 ) + 1 -- +239 car fmod(-1) = -1 et non 11 elseif num < 1 or num > 12 then num = nil end elseif trim( mois ) then nom, num = fun.valideMois( mois ) if nom == nil and boucle == nil then -- essai de détermination d'un nombre avec le parser #expr de Mediawiki. -- le paramètre boucle évite de tourner en boucle. nom, num = fun.determinationMois( mw.getCurrentFrame():callParserFunction( '#expr', mois ), true, true ) end end if num and not nom then nom = liste_mois[num][1] end return nom, num end


-- fonction interne à modeleDate, pour déterminer si on peut se passer de faire un ifexit local function existDate( dataQualificatif, annee, mois ) local data if mois then data = dataQualificatif.mois else data = dataQualificatif.annee end if type( data ) ~= 'table' then -- si data n'existe pas c'est que l'on considère qu'il n'y a pas de lien. return end -- le qualificatif est remplacer par celui de la base de donnée, ce qui permet des alias. local lien = annee .. ' ' .. ( dataQualificatif.qualificatif or ) local seul = annee if mois then lien = mois .. ' ' .. lien seul = ucfirst( mois ) .. ' ' .. annee end local aucun = tonumber( data.aucun ) if aucun and annee <= aucun then -- si la l'année est dans la partie 'aucun' on teste s'il y a malgré tout un lien isolé if type( data.seul ) == 'table' then for i, v in ipairs( data.seul ) do if seul == v or seul == tonumber( v ) then return lien end end end -- partie aucun et pas de lien => nil return nil elseif type( data.tous ) == 'table' then local tous1, tous2 = tonumber( data.tous[1] ), tonumber( data.tous[2] ) if tous1 and tous2 and annee >= tous1 and annee <= tous2 then -- l'année est dans la partie 'tous' donc on retourne le lien return lien end end -- l'annee n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe. 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 = { '1er', '1er', '1er' } for i, v in ipairs( nomJour ) do jour = jour:gsub( v, ) end for i, v in ipairs( premier ) do jour = jour:gsub( v, '1' ) end jour = trim( jour ) end return jour end

--- -- Sépare une chaine date en un table contenant les champs jour, mois et annee. -- la date doit contenir le mois. function fun.separationJourMoisAnnee( date ) date = trim( date ) if date then local function erreur( periode, valeur ) return false, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' ) end local jour, mois, annee, masquerMois, masquerAnnee, 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 aj = '(%-?%d+)' -- année ou jour local s = '[ ./-]+' -- séparateur simple local sep = '([ ./-]+)' -- séparateur avec capture, pour le détecter deux fois local moins = '(%-?)' -- signe moins pour signifier qu'il ne faut pas afficher cette donnée local regexb = { jmmm = '^'..j..s..mmm..moins..'$', mmmjva = '^'..mmm..s..j..', ?'..aj..'$', }

date = fun.nettoyageJour( date ) -- 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 :gsub( '\194\160', ' ' ) :gsub( ' ', ' ' ) :gsub( '\226\128\175', ' ' ) :gsub( '&nnbsp;', ' ' ) :gsub( '\226\128\137', ' ' ) :gsub( ' ', ' ' ) :gsub( ' ', ' ' ) :gsub( ' +', ' ' ) -- réduction av. J-C pour simplifier un peu les regex : :gsub( '(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?', '-%1' ) -- supression de l'heure dans les date 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( '^'..j..s..mmm..moins..'$' ) then -- jj mmmm jour, mois, masquerMois = date:match( '^'..j..s..mmm..moins..'$' ) elseif date:match( '^'..mmm..s..j..', ?'..aj..'$') then -- mmm jj, aaaa (format anglo-saxon) mois, jour, annee = date:match( '^'..mmm..s..j..', ?'..aj..'$') elseif date:match( '^'..mmm..'$' ) then mois = date else return erreur( 'Date', date ) end local jn, an = tonumber( jour ), tonumber( annee ) if jn and an and ( jn > 31 or jn < 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


--- -- separationJourMoisAnnee prend jusqu'a cinq paramètre et essaie de les séparer en jour, mois, annee et qualificatif -- la date peut être dans le premier paramètre ou séparée dans les paramètre 1 à 3 ou 2 à 4. -- Le qualificatif est cherché dans le paramètre suivant. -- La fonction retourne true suivit d'une table avec la date en paramètres nommé (sans accent sur année) -- ou false suivit d'un message d'erreur. function fun.validationJourMoisAnnee( frame, ... ) local args = Outils.extractArgs( frame, ... ) local jour, mois, numMois, annee, erreur 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, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' ) 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 = string.match( string.upper( bannee ), '^(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?' ) annee = tonumber( annee ) if annee then annee = 0 - annee else return erreur( 'Année', bannee ) end 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.valideSaison( bmois ) if mois == nil then return erreur( 'Mois', bmois ) end else -- on traite le jour si présent if Outils.notEmpty( bjour ) then jour = tonumber( bjour ) if jour == nil then jour = tonumber( fun.nettoyageJour( bjour ) ) end if jour == nil then return erreur( 'Jour', bjour ) end -- on valide que le jour est correct if jour < 1 or jour > 31 then return erreur( 'Jour', bjour ) elseif jour > liste_mois[numMois].nJour then return erreur( 'Jour', bjour .. ' ' .. mois ) elseif jour == 29 and numMois == 2 and annee and ( math.fmod( annee, 4 ) ~= 0 ) then -- l'année bisextile sur les siècles est toujours acceptée pour être compatible avec les dates juliennes. return erreur( 'Jour', '29 février ' .. annee ) end else -- S'il n'y a pas de jour on regarde si la première lettre du mois est en majuscule if bmois:match( '^%u' ) then -- oui, on passe la première lettre en majuscule mois = ucfirst( mois ) end -- s'il n'y a pas d'année non plus on retourne le mois simple end end else -- on teste le jour si présent 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, Outils.erreur( 'année improbable (' .. annee .. ')' ) end

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

function fun.modeleDateAnalyseJMA( args ) local function masquerParam( p ) -- sépare le signe moins final éventuel signifiant que le paramètre ne soit pas être affiché. local s if trim( p ) then p, s = p:match( '^(.-)(%-?)%s*$' ) end return p, ( s == '-' or nil ) end

local test, resultat 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, resultat = fun.separationJourMoisAnnee( arg1 ) if test then resultat.qualificatif = arg2 end else local param, masquerM, masquerA param = { args[1] or args.jour } param[2], masquerM = masquerParam( args[2] or args.mois ) param[3], masquerA = masquerParam( args[3] or args.annee or args['annee'] ) param[4] = masquerParam( args[4] ) test, resultat = fun.validationJourMoisAnnee( param ) if test then resultat.masquerAnnee = masquerA resultat.masquerMois = masquerM resultat.qualificatif = trim( args[4] ) end end

return test, resultat end

--- -- émule le modèle {{Date}}. -- Paramètres : -- 1 : jour (numéro ou "1er"). optionnel, si absent pas de jour -- 2 : mois (en toutes lettres) -- 3 : année (nombre) -- 4 : optionnel, spécialité de l'année -- Comportement spécial ("truc à deux balles au lieu d'utiliser un -- paramètre nommé du genre "sans année=oui"...") : si 1 est vide -- mais que le reste est complet → on n'affiche pas l'année function fun.modeleDate( frame ) local args = Outils.extractArgs( frame ) local test, params = fun.modeleDateAnalyseJMA( args ) local cat, resultat = if test then local listeParam = { qualificatif = 'qualificatif', age = 'âge', ['âge'] = 'âge', naissance = 'naissance', mort = 'mort', ['décès'] = 'mort', julien = 'julien', avJC = 'avJC', nolinks = 'nolinks', } for n, v in pairs( listeParam ) do params[v] = params[v] or args[n] end resultat = fun._modeleDate( params )

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 = 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'age, naissance et mort local age = trim( args['âge'] or args['age'] ) age = age and fun.age( annee, numMois, jour ) local naissance = trim( args.naissance ) local mort = trim( args.mort )

-- on traite le calendrier local gannee, gmois, gjour = annee, numMois, jour -- date suivant le calendrier grégorien pour


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

-- Déclarations des variables local wikiListe = TableBuilder.new() -- reçois le texte affiché pour chaque paramètre local iso = TableBuilder.new() -- reçois le format date ISO de ce paramètre

local dataQualificatif, dataCat if not args.nolinks then dataQualificatif = dataLiens[qualificatif or ] if type( dataQualificatif ) ~= 'table' then -- si le qualifiquatif n'est pas dans la base de donnée, on crée une table minimum, -- qui imposera un test sur l'annee, mais considère qu'il n'y a pas de lien sur le jour ou le mois dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } } end dataCat = dataLiens[dataQualificatif.cat] if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then dataCat = { qualificatif = } end end local function wikiLien( lien, texte ) if lien == texte then return texte else return texte end end

-- Date julienne if jjour ~= jour then if jjour == 1 then jjour = '1er' end if jannee ~= annee then julien3 = '(' .. jjour .. ' ' .. jmois .. ' ' .. jannee .. ')' else julien2 = '(' .. jjour .. ' ' .. jmois .. ')' end end


-- le jour si présent local qualifJour = if jour then local texteJour = jour if args.nolinks then if jour == 1 then jour = modelePremier end wikiListe.insert( jour ) else qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif or dataCat.jour and dataCat.qualificatif or local lien = jour .. ' ' .. mois .. ' ' .. qualifJour if jour == 1 then jour = '1er' lien = '1er ' .. mois .. ' ' .. qualifJour end -- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour. wikiListe.insert( wikiLien( lien, jour ) ) wikiListe.insert( wikiLien( lien, jour .. ' '.. mois ) ) end iso.insert( 1, string.sub( '0' .. gjour, -2 ) ) end

-- le mois if mois then if #wikiListe == 0 and annee == nil then return mois end if args.nolinks then if not args.masquerMois then wikiListe.insert( mois ) end else local lien if annee then lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois ) if lien == nil and qualificatif and qualifJour == then -- test nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif. lien = existDate( dataLiens[], annee, mois ) end end if lien or args.masquerMois then -- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter 'mois' wikiListe.remove() if not args.masquerMois then wikiListe.insert( wikiLien( lien, mois ) ) end elseif #wikiListe > 0 then -- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois' wikiListe.remove( #wikiListe - 1 ) elseif args.masquerAnnee then -- s'il n'y a pas de jour et que l'année n'est pas affichée, on insère le mois seul. wikiListe.insert( mois ) end end if gmois then iso.insert( 1, string.sub( '0' .. gmois, -2 ) ) end end if( julien2 ) then wikiListe.insert( julien2 ) end

-- l'année if annee then if not args.masquerAnnee then local texteAnnee = annee local lien if annee < 0 then local annneeAvJc = 0 - annee lien = lien or ( annneeAvJc .. ' av. J.-C.' ) local avJC = trim( string.lower( args.avJC or ) ) if args.avJC == 'non' then texteAnnee = annneeAvJc else texteAnnee = annneeAvJc .. ' av. J.-C.' end end if args.nolinks then -- seulement si on doit l'affichée wikiListe.insert( texteAnnee ) else lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee ) or lien or annee if mois and #wikiListe == 0 then -- si le mois n'a pas de lien et n'est pas affiché avec le jour, il est affiché avec l'année. texteAnnee = mois .. ' ' .. texteAnnee end wikiListe.insert( wikiLien( lien, texteAnnee ) ) end end if gannee > 999 then iso.insert( 1, gannee ) elseif gannee > -1 then iso.insert( 1, string.sub( '000' .. gannee , -4 ) ) elseif gannee > -999 then -- calendrier grégorien proleptique avec année 0. iso.insert( 1, 'U-' .. string.sub( '000' .. ( 0 - gannee ), -4 ) ) else iso.insert( 1, 'U' .. gannee ) end end if( julien3 ) then wikiListe.insert( julien3 ) end


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


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

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

local dateHtml = wikiHtml:tag( 'time' ) :wikitext( wikiTexte ) if wikiTexte:match( ' ' ) then dateHtml:addClass( 'nowrap' ) end if isoTexte ~= wikiTexte then dateHtml:attr( 'datetime', isoTexte ) end if not args.nolinks then dateHtml:addClass( 'date-lien' ) end if naissance then dateHtml:addClass( 'bday' ) elseif mort then dateHtml:addClass( 'dday' ) end if age then wikiHtml:wikitext( ' ' ) :tag( 'span' ) :addClass( 'noprint') :wikitext( age ) :done() end

return tostring( wikiHtml ) end


--- -- fonction destinée aux infobox, notamment pour afficher les dates de naissance et de mort -- les liens présent dans les dates fournies sont automatiquement supprimées pour gérer les cas ou -- le paramètre contient déjà un modèle date. -- Paramètres : -- 1 : type de date à afficher (naissance / n, mort / m, ou date / d) -- 1 : Date ou date de naissance -- 2 : Date de mort si type n ou m -- qualificatif = suffixe des page de date à lier (exemple : en musique) -- nolinks : n'affiche pas de lien function fun.dateInfobox( frame ) local args = frame.args if type( args ) ~= 'table' or not ( args[1] and args[2] ) then return end

local function analyseDate( d ) if trim( d ) then -- supprime les liens 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 local debut, fin = analyse:match( '(.-%d%d%d%]*%-?)([\127 ].+)' ) if not debut then debut, fin = analyse:match( '(.-%d%d%d%]*%-?)(
.+)' ) end analyse = debut or analyse 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

local naissance = args[1]:match( '^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( '' ) 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 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 = , ' (', ')', 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 .. ' ans' .. suffixAge elseif calculAge == 1 then age = prefixAge .. 'un an' .. suffixAge elseif calculAge == 0 then age = prefixAge .. 'moins d’un an' .. suffixAge end if complementDate and complementDate:match( 'ans?%)' ) then complementDate = end end

return 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 -- le mois peut être en lettre ou en chiffres -- le jour peut être sous la forme '05', '1er' ou 'vendredi 13' function fun.dateISO( frame ) local args = Outils.extractArgs( frame ) local annee = Outils.notEmpty( args['année'], args.annee, args.year, args.date ) -- extraction de l'année if type( annee ) == 'string' then annee = ( tonumber( annee ) -- match '2013' or string.match ( annee, '%D(%d%d%d%d)%D' ) -- match '2013' or string.match ( annee, '%D(%d%d%d%d)$' ) -- match '17 septembre 2013' or string.match ( annee, '^(%d%d%d%d)%D' ) -- match '2013-09-17' ) end annee = tonumber( annee )

-- le format de date iso est défini suivant le calendrier grégorien. -- Avant l'année 1583 la date est calendrier est probablement du calendrier julien, -- donc autant s'abstenir. if annee and annee > 1582 then local mois = Outils.notEmpty( args.mois, args.month ) -- num mois trouve le numéro du mois, qu'il soit numérique ou texte, complet ou abrégé. local nomMois, numMois = fun.determinationMois( mois ) if numMois then mois = '-' .. string.sub( '0' .. numMois, -2 )

local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] ) if type( jour ) == 'string' then jour = tonumber( jour ) or tonumber( string.match ( jour, '%d+') ) end jour = tonumber( jour ) if jour and jour <= liste_mois[numMois].nJour then jour = '-' .. string.sub( '0' .. jour, -2 ) return annee .. mois .. jour else return annee .. mois end else return tostring( annee ) end end end

--- -- Rang du jour dans l'année -- Usage : do_dayRank{année,mois,jour} function fun.do_dayRank(arguments) local yr = tonumber(arguments.year or arguments[1]) or 1 local mt = tonumber(arguments.month or arguments[2]) or 1 local dy = tonumber(arguments.day or arguments[3]) or 1 -- Rangs des premiers des mois local ranks = {0,31,59,90,120,151,181,212,243,273,304,334}

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

-- Nombre de jours entre deux années (du 1er janvier au 1er janvier) -- Suit le calendrier grégorien function fun.do_daysBetween(arguments) local yr1 = tonumber(arguments[1]) or 0 local yr2 = tonumber(arguments[2]) or 0

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

-- Nombre de jours depuis l'année 1 (du 1er janvier au 1er janvier) function fun.daysSinceOrigin(year) local yr = year-1 return 365*yr + math.floor(yr/4) - math.floor(yr/100) + math.floor(yr/400) end

-- Test d'année bissextile (Suit le calendrier grégorien) function fun.isLeapYear(year) local yr = tonumber(year) or 1 return (yr%4 == 0) and ((yr%100 ~= 0) or (yr%400 == 0)) end

-- Conversion d'un nombre en chiffres romains function fun.toRoman(number) local n = math.floor(number) local letters = {"I","V","X","L","C","D","M","",""} local pattern = {"","0","00","000","01","1","10","100","1000","02"} local result = "" if(n<=0 or n>=4000) then result = "---" else for i=1,7,2 do 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]) .. " " .. months[arguments[2]] else result = result .. "jour " .. extras[arguments[3]] end result = result .. " de l'an " .. fun.toRoman(arguments[1]) return result end

--- -- Voir Modèle:Âge -- retourne l'age en fonction de la ou les dates fournies. La valeur retounée est de type 'number' -- Parammètres : -- 1, 2, 3 : année, mois jour de naissance (supposé dans le calendrier grégorien) -- 4, 5, 6 : année, mois, joue du calcul (facultatif, par défaut la date UTC courante). function fun.age( an, mn, jn, ac, mc, jc ) 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 Outils.erreur("Paramètres incorrects ou insuffisants ou pour calculer l'âge précis" ) end end

--- -- calcul du jour julien à partir d'une date du calendrier grégorien function fun.julianDay( year, month, day, hour, min, sec ) local julian julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 ) - math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 100 ) + math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 400 ) + math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 ) + day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400 - 32167.5 return julian end

--- -- calcul du jour julien à partir d'une date du calendrier julien function fun.julianDayJulian( year, month, day, hour, min, sec ) local julian julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 ) + math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 ) + day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400 - 32205.5 return julian end

--- -- calcul d'une date dans le calendrier grégorien à partir du jour julien function fun.julianDayToGregorian( julianDay ) local base = math.floor( julianDay + 32044.5 ) -- 1 March -4800 (proleptic Gregorian date) local nCentury = math.floor( ( base * 4 + 3 ) / 146097 ) local sinceCentury = base - math.floor( nCentury * 146097 / 4 ) local nYear = math.floor( ( sinceCentury * 4 + 3 ) / 1461 ) local sinceYear = sinceCentury - math.floor( nYear * 1461 / 4 ) local nMonth = math.floor( ( sinceYear * 5 + 2 ) / 153 )

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

return year, month, day end

--- -- calcul d'une date dans le calendrier julien à partir du jour julien -- calcul basé sur l'algorythme de la page fr.wikipedia.org/wiki/Jour_julien (1/10/2013) function fun.julianDayToJulian( julianDay ) local year = math.modf( ( julianDay * 4 - 6884469 ) / 1461 ) local r2 = julianDay - math.modf( ( 1461 * year + 6884472 ) / 4 ) local month = math.modf( ( 5 * r2 + 461 ) / 153 ) local day = r2 - math.modf( ( 153 * month - 457 ) / 5 ) + 1 if month > 12 then year = year + 1 month = month - 12 end return year, month, day end

--- -- calcul d'une date dans le calendrier grégorien à partir d'une date dans le calendrier julien function fun.julianToGregorian( year, month, day ) return fun.julianDayToGregorian( fun.julianDayJulian( year, month, day ) ) end

--- -- calcul d'une date dans le calendrier julien à partir d'une date dans le calendrier grégorien function fun.gregorianToJulian( year, month, day ) year = tonumber(year) if month then month = tonumber(month) else month = 6 end --prend une valeur centrale pour donner un best "guess" if day then day = tonumber(day) else day = 15 end return fun.julianDayToJulian( fun.julianDay( year, month, day ) ) end


--- -- erreurModuleData affiche d'un message d'erreur si le Module:Langue/Data n'a pas été chargé correctement, -- pour la page de discussion de la base de donnée et ceux qui veulent surveiller cette page. function fun.erreurModuleData() local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' ) if success == false then local message = [[Le chargement du module Date/Data génère une erreur :
%s

Cette erreur doit être corrigée au plus vite car des milliers de page ne s'affichent pas correctement ]] return string.format( message, resultat ) end end

--- -- checkDataCat génère des liens vers les pages annuelles, mensuelles et d'éphémérides liè aux -- catégories du Module:Date/Data. La date la plus ancienne dépend de 'aucun' et 'seul[1]' -- Paramètres : -- 1 : la catégorie. Il y aura une section par qualificatif de cette catégorie. -- mois : oui pour avoir les liens vers les pages mensuelles et éphémérides (4 jours dans l'année) -- alias : pour avoir des lien pour les alias en plus des qualificatif function fun.checkDataCat( frame ) local category = trim(frame.args[1]) local monthLinks = frame .args.mois == 'oui' local alias = frame.args.alias == 'oui' local dataLink = mw.loadData( 'Module:Date/Data' ) local wikiList = TableBuilder.new() local currentYear = tonumber( os.date( '%Y' ) )

local columns = '

'

local newSection if monthLinks then newSection = '\n\n== %s ==\n\n=== Années ===\n' .. columns else newSection ='\n\n== %s ==\n' .. columns end for field, dataField in pairs( dataLink ) do -- boucle sur tous les qualificatif ayant pour catégorie le premier paramère if dataField.cat == category or ( category == 'cat' and dataField.cat == field ) then local monthInitialYear, initialYear -- définition de l'année à partir de laquelle on va tester toutes les année / mois if dataField.qualificatif == field or ( category == 'cat' and dataField.cat == field ) then if dataField.annee and dataField.annee.aucun and dataField.annee.aucun < currentYear then local aucun = ( dataField.annee.seul and dataField.annee.seul[1] ) or dataField.annee.aucun initialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), currentYear - 50 ) else initialYear = currentYear - 50 end if dataField.mois and tonumber( dataField.mois.aucun ) and ( tonumber( dataField.mois.aucun ) < currentYear ) then local aucun = dataField.mois.aucun monthInitialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), currentYear - 8 ) else monthInitialYear = currentYear - 8 end elseif alias then -- si le paramètre alias est défini on teste aussi tous les alias, sinon ils sont ignorés initialYear = currentYear - 50 monthInitialYear = currentYear - 8 end

-- création de l'ensembles des liens if initialYear then -- ajout de lien vers les pages annuelles de l'année en court + 5 jusqu'à initialYear wikiList.insert( string.format( newSection, field ) ) local fieldLink = ' ' .. field if category == 'cat' then fieldLink = ' ' .. dataField.qualificatif end for year = ( currentYear + 5 ), initialYear, -1 do wikiList.insert( '\n* ' .. year .. '' ) end

wikiList.insert( '\n

' )

if monthLinks then -- insertstion de liens vers les mois de l'année en court + 1 jusqu'à monthInitialYear wikiList.insert( '\n\n=== Mois ===' ) local month, sep for year = ( currentYear + 1 ), monthInitialYear, -1 do wikiList.insert( '\n* ' .. year .. ' : ' ) sep = ' • ' for j = 1, 12 do month = ucfirst( liste_mois[j][1] ) .. ' ' if j == 12 then sep = end wikiList.insert( '' .. month .. '' .. sep ) end end

-- insertion de quelques date pour tester les éphémérides wikiList.insert( '\n\n=== Jours ===' ) wikiList.insert( '\n* 1er janvier ' .. fieldLink .. '' ) wikiList.insert( '\n* 14 mars ' .. fieldLink .. '' ) wikiList.insert( '\n* 22 juin ' .. fieldLink .. '' ) wikiList.insert( '\n* 3 septembre ' .. fieldLink .. '' ) end end end end

return table.concat( wikiList ) end

--[[

 Cette fonction retourne "CET" ou "CEST" selon que dans la pseudo-timezone en cours
   c'est l'heure d'été ou l'heure d'hiver.
 Cette fonction n'a de sens a priori que pour des modèles utilisés en Europe
 
 Paramètre optionnel non nommé : "sans lien" : retourne le texte CET/CEST. sinon
   retourne ce même texte avec un wikilien vers les articles correspondant

--]] function fun.CEST(frame) -- option : ne pas créer de wikilien local opt = trim(frame.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 "CEST" end else -- heure d'hiver (ou autre zone où ça ne s'applique pas) if (opt == "sans lien") then return "CET" elseif (opt == "décalage") then return "1" else return "CET" end end end

return fun