Difference between revisions of "Module:Date"
wiki_crypto>Johnuniq (update from sandbox: implement show=M (minutes) and show=s (seconds); better method to fill a partial date) |
wiki_crypto>LD |
||
Line 1: | Line 1: | ||
local fun = {} | |||
local | local Outils = require 'Module:Outils' | ||
local | -- chargement de la base de données répertoriant certaines pages existant ou n'existant pas pour éviter les "ifexist". | ||
local dataLiens | |||
local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' ) | |||
if success then | |||
dataLiens = resultat | |||
else | |||
-- protection au cas où le sous-module serait mal modifié | |||
dataLiens = { [''] = { mois = { aucun = 1000, tous = { 1773, 2014 } }, } } | |||
end | |||
-- nettoie un paramètre non nommé (vire les espaces au début et à la fin) | |||
local | -- retourne nil si le texte est vide ou n'est pas du texte. Attention c'est important pour les fonctions qui l'utilisent. | ||
local trim = Outils.trim | |||
-- Fonction destinée à mettre la première lettre du mois en majuscule : | |||
-- utilisation de string car aucun mois ne commence par une lettre non ascii en français ou anglais. | |||
local function ucfirst( str ) | |||
return | return str:sub( 1, 1 ):upper() .. str:sub( 2 ) | ||
end | end | ||
local | local modelePremier = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>' | ||
local | -- liste des mois, écriture exacte et alias, en minuscule | ||
local listeMois = { | |||
{ num = 1, nJour = 31, abrev = 'janv.', nom = 'janvier', alias = { 'jan.', 'janv.', 'jan', 'janv', 'january' } }, | |||
{ num = 2, nJour = 29, abrev = 'fév.', nom = 'février', alias = { 'fevrier', 'fev.', 'fev', 'fév.', 'fév', 'févr', 'févr.', 'february', 'feb', 'feb.' } }, | |||
{ num = 3, nJour = 31, abrev = 'mars', nom = 'mars', alias = { 'mar.', 'mar', 'march' } }, | |||
{ num = 4, nJour = 30, abrev = 'avr.', nom = 'avril', alias = { 'avr.', 'avr', 'apr', 'april'} }, | |||
{ num = 5, nJour = 31, abrev = 'mai', nom = 'mai', alias = { 'may' } }, | |||
{ num = 6, nJour = 30, abrev = 'juin', nom = 'juin', alias = { 'jun', 'june' } }, | |||
{ num = 7, nJour = 31, abrev = 'juill.', nom = 'juillet', alias = { 'juil.', 'juil', 'juill.', 'juill', 'jul', 'july' } }, | |||
} | { num = 8, nJour = 31, abrev = 'août', nom = 'août', alias = { 'aoû', 'aug', 'august' } }, | ||
{ num = 9, nJour = 30, abrev = 'sept.', nom = 'septembre', alias = { 'sept.', 'sept', 'sep.', 'sep', 'september' } }, | |||
{ num = 10, nJour = 31, abrev = 'oct.', nom = 'octobre', alias = { 'oct.', 'oct', 'october' } }, | |||
{ num = 11, nJour = 30, abrev = 'nov.', nom = 'novembre', alias = { 'nov.', 'nov', 'november' } }, | |||
{ num = 12, nJour = 31, abrev = 'déc.', nom = 'décembre', alias = { 'decembre', 'déc.', 'dec.', 'dec', 'déc', 'december' } }, | |||
aout = { num = 8, nJour = 31, abrev = 'aout', nom = 'aout', alias = { 'aou' } }, | |||
} | |||
local | -- ajoute les noms, abréviations et alias en tant que clés de listeMois | ||
for i = 1, 12 do | |||
local mois = listeMois[i] | |||
listeMois[tostring( i )] = mois | |||
if i < 10 then | |||
listeMois['0' .. i] = mois | |||
end | |||
listeMois[mois.nom] = mois | |||
listeMois[mois.abrev] = mois | |||
for j = 1, #mois.alias do | |||
listeMois[mois.alias[j]] = mois | |||
end | end | ||
end | |||
for i = 1, #listeMois.aout.alias do | |||
listeMois[listeMois.aout.alias[i]] = listeMois.aout | |||
end | end | ||
local | local liste_saisons = { | ||
{ 'printemps', 'spring', }, | |||
{ 'été', 'summer', }, | |||
{ 'automne', 'autumn', }, | |||
{ 'hiver', 'winter', }, | |||
} | |||
-- à partir d'un nom de saison (en français ou en anglais), | |||
-- retourne son nom canonique (exemple : "été") | |||
-- si non reconnu, retourne nil | |||
function fun.determinationSaison( saison ) | |||
local s = trim( saison ) | |||
if s then | |||
s = mw.ustring.lower( s ) | |||
for i = 1, 4 do | |||
for j = 1, #liste_saisons[i] do | |||
if s == liste_saisons[i][j] then | |||
return liste_saisons[i][1] | |||
end | |||
end | |||
end | |||
end | end | ||
end | end | ||
--- | |||
-- à partir d'un nom de mois (en français ou en anglais), de son numéro ou d'une abréviation, | |||
-- retourne son nom canonique (exemple : "juin") et son numéro (exemple : 6) | |||
-- si non reconnu, retourne nil, nil | |||
function fun.determinationMois( mois ) | |||
local result | |||
local | local num = tonumber( mois ) | ||
if num then | |||
result = listeMois[num] | |||
else | |||
end | local str = trim( mois ) | ||
if str then | |||
result = listeMois[str] | |||
if not result then | |||
result = listeMois[mw.ustring.lower( str )] | |||
end | |||
end | |||
end | |||
if result then | |||
return result.nom, result.num | |||
else | else | ||
return nil, nil | |||
end | end | ||
end | end | ||
-- fonction interne à modeleDate, pour déterminer si on peut se passer de faire un ifexist | |||
local function existDate( dataQualificatif, annee, mois ) | |||
local data | |||
if mois then | |||
data = dataQualificatif.mois | |||
local | |||
if | |||
else | 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 | return | ||
end | end | ||
local | -- le qualificatif est remplacé par celui de la base de données, ce qui permet des alias. | ||
if | local lien = annee | ||
if dataQualificatif.qualificatif ~= '' then | |||
lien = lien .. ' ' .. dataQualificatif.qualificatif | |||
end | |||
local seul = annee | |||
if mois then | |||
lien = mois .. ' ' .. lien | |||
seul = ucfirst( mois ) .. ' ' .. annee | |||
end | end | ||
local | local aucun = tonumber( data.aucun ) | ||
if | if aucun and annee <= aucun then | ||
-- si l'année est dans la partie 'aucun' on teste s'il y a malgré tout un lien isolé | |||
if | 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 | ||
end | end | ||
-- l'année n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe. | |||
local cibleLien = mw.title.new( lien ) | |||
if cibleLien and cibleLien.exists then | |||
return lien | |||
end | end | ||
end | end | ||
--- | |||
-- Supprime le jour de la semaine, et "le" avant une date | |||
function fun.nettoyageJour( jour ) | |||
if type( jour ) == 'string' then | |||
local nomJour = { '[Ll]undi', '[Mm]ardi', '[Mm]ercredi', '[Jj]eudi', '[Vv]endredi', | |||
'[Ss]amedi', '[Dd]imanche', '^ *[Ll]e' } | |||
local premier = { '<abbr class="abbr ?" title="[Pp]remier" ?>1<sup>er</sup></abbr>', '1<sup>er</sup>', '1er' } | |||
if | for i = 1, #nomJour do | ||
jour = jour:gsub( nomJour[i], '' ) | |||
end | end | ||
for i = 1, #premier do | |||
jour = jour:gsub( premier[i], '1' ) | |||
end | end | ||
jour = trim( jour ) | |||
end | end | ||
return jour | |||
end | end | ||
--- | |||
-- Sépare une chaine date en une table contenant les champs jour, mois et annee. | |||
-- la date doit contenir le mois. | |||
function fun.separationJourMoisAnnee( date ) | |||
date = trim( date ) | |||
if date then | |||
local function erreur( periode, valeur ) | |||
return false, '<span class="error">' .. periode .. ' invalide (' .. valeur .. ')</span>' | |||
if | |||
end | end | ||
local dateAvantCleanup = date | |||
return | local jour, mois, annee, masquerMois, masquerAnnee, separateur | ||
-- variable pour construire les regex | |||
local j = '([0-3]?%d)' -- jour | |||
local m = '([01]?%d)' -- mois numérique | |||
local mmm = '([^%s%p%d]+[.]?)' -- mois en toute lettre | |||
local mmm2 = '([^%s%p%d]+[.]?[-/][^%s%p%d]+[.]?)' -- mois-mois en toute lettre | |||
local aj = '(%-?%d+)' -- année ou jour | |||
local s = '[ ./-]+' -- séparateur simple | |||
local sep = '([ ./-]+)' -- séparateur avec capture, pour le détecter deux fois | |||
local moins = '(%-?)' -- signe moins pour signifier qu'il ne faut pas afficher cette donnée | |||
date = fun.nettoyageJour( date ) | |||
if date == nil then | |||
return erreur( 'Date', dateAvantCleanup ) | |||
end | end | ||
if | -- suppression catégorie, liens, balises | ||
if | date = mw.ustring.gsub( date, '%[%[[Cc]at[ée]gor[yi]e?:.-%]%]', '' ) | ||
date = date :gsub( '%b<>', '' ) | |||
:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end ) | |||
-- suppression des espaces insécables | |||
-- nbsp | |||
:gsub( '\194\160', ' ' ) | |||
:gsub( ' ', ' ' ) | |||
:gsub( ' ', ' ' ) | |||
-- narrow nbsp | |||
:gsub( '\226\128\175', ' ' ) | |||
:gsub( ' ', ' ' ) | |||
-- thin space | |||
:gsub( '\226\128\137', ' ' ) | |||
:gsub( ' ', ' ' ) | |||
:gsub( ' ', ' ' ) | |||
-- simple space | |||
:gsub( ' ', ' ' ) | |||
-- plusieurs espaces | |||
:gsub( ' +', ' ' ) | |||
-- réduction av. J-C pour simplifier un peu les regex : | |||
:gsub( '(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?', '-%1' ) | |||
-- suppression de l'heure dans les dates ISO | |||
:gsub( '^+?([%d-]*%d%d%-%d%d)T%d%d[%d:,.+-]*Z?$' , '%1') | |||
-- test année seule | |||
if date:match( '^'..aj..'$' ) then | |||
annee = date:match( '^'..aj..'$' ) | |||
elseif date:match( '^'..aj..s..aj..moins..'$' ) then | |||
-- jj/mm, mm/aaaa ou aaaa/mm | |||
local a, separateur, b, sb = date:match( '^'..aj..sep..aj..moins..'$' ) | |||
a, b = tonumber( a ), tonumber( b ) | |||
if separateur:match( '^.+%-$' ) then | |||
-- probablement mm/-aaaa, année av.JC | |||
b = 0 - b | |||
end | |||
if a > 12 and ( b < 1 or b > 31 ) or | |||
b > 12 and ( a < 1 or a > 31 ) then | |||
return erreur( 'Date', dateAvantCleanup ) | |||
elseif b < 1 or b > 31 then | |||
mois, annee, masquerAnnee = a, b, sb | |||
elseif a < 1 or a > 31 then | |||
annee, mois = a, b | |||
elseif b > 12 then | |||
return erreur( 'Mois', b ) | |||
else | |||
jour, mois, masquerMois = a, b, sb | |||
end | |||
elseif date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' ) then | |||
-- jj/mm/aaaa ou aaaa/mm/jj | |||
jour, separateur, mois, masquerMois, annee, masquerAnnee = date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' ) | |||
if separateur == '-' and masquerMois == '-' and masquerAnnee == '' and tonumber( annee ) > 0 then | |||
-- date au format jj-mm--aaaa type 17-06--44 pour 17 juin 44 av. JC | |||
masquerMois = nil | |||
annee = 0 - annee | |||
end | |||
elseif date:match( '^'..j..sep..mmm..moins..'%2'..aj..moins..'$' ) then | |||
-- jj mmm aaaa | |||
jour, separateur, mois, masquerMois, annee, masquerAnnee = date:match( '^'..j..sep..mmm..moins..'%2'..aj..moins..'$' ) | |||
elseif date:match( '^'..mmm..s..aj..moins..'$' ) then | |||
-- mmm aaaa | |||
mois, separateur, annee, masquerAnnee = date:match( '^'..mmm..sep..aj..moins..'$' ) | |||
if separateur:match( '^.+%-$' ) then | |||
annee = '-' .. annee | |||
end | |||
elseif date:match( '^'..mmm2..s..aj..moins..'$' ) then | |||
-- mmm-mmm aaaa | |||
mois, separateur, annee, masquerAnnee = date:match( '^'..mmm2..sep..aj..moins..'$' ) | |||
if separateur:match( '^.+%-$' ) then | |||
annee = '-' .. annee | |||
end | end | ||
elseif date:match( '^'..j..s..mmm..moins..'$' ) then | |||
-- jj mmm | |||
jour, mois, masquerMois = date:match( '^'..j..s..mmm..moins..'$' ) | |||
elseif date:match( '^'..mmm..s..j..', ?'..aj..'$') then | |||
-- mmm jj, aaaa (format anglo-saxon) | |||
mois, jour, annee = date:match( '^'..mmm..s..j..', ?'..aj..'$') | |||
elseif date:match( '^'..mmm..'$' ) then | |||
mois = date | |||
else | |||
return erreur( 'Date', dateAvantCleanup ) | |||
end | 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 | else | ||
return | return true, {} | ||
end | |||
end | |||
--- | |||
-- validationJourMoisAnnee vérifie que les paramètres correspondent à une date valide. | |||
-- la date peut être dans les paramètres 1 à 3, ou dans des paramètres jour, mois et annee. | |||
-- La fonction retourne true suivi d'une table avec la date en paramètres nommés (sans accent sur année) | |||
-- ou false suivi d'un message d'erreur. | |||
function fun.validationJourMoisAnnee( frame ) | |||
local args = Outils.extractArgs( frame ) | |||
local jour, mois, numMois, annee | |||
local bjour = args[1] or args['jour'] or '' | |||
local bmois = tostring( args[2] or args['mois'] or '' ) | |||
local bannee = args[3] or args['annee'] or args['année'] or '' | |||
local function erreur( periode, valeur ) | |||
return false, '<span class="error">' .. periode .. ' invalide (' .. valeur .. ')</span>' | |||
end | end | ||
if | |||
-- on traite l'année | |||
if Outils.notEmpty( bannee ) then | |||
annee = tonumber( bannee ) | |||
if annee == nil and type( bannee ) == 'string' then | |||
-- test si l'année contient av. J.-C. | |||
-- | annee = bannee:upper():match( '^(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?' ) | ||
annee = tonumber( annee ) | |||
if annee then | |||
annee = 0 - annee | |||
if | |||
else | else | ||
return | return erreur( 'Année', bannee ) | ||
end | end | ||
elseif annee == 0 then | |||
return erreur( 'Année', 0 ) | |||
end | end | ||
else | |||
annee = nil | |||
end | end | ||
if | -- on traite le mois | ||
if Outils.notEmpty( bmois ) then | |||
mois, numMois = fun.determinationMois( bmois ) | |||
if mois == nil then | |||
mois = fun.determinationSaison( bmois ) | |||
if mois == nil then | |||
local mois1, sep, mois2 = bmois:match( '^([^%s%p%d]+[.]?)([-/])([^%s%p%d]+[.]?)$' ) | |||
if mois1 then | |||
mois1 = fun.determinationMois( mois1 ) | |||
mois2 = fun.determinationMois( mois2 ) | |||
if mois1 == nil or mois2 == nil then | |||
return erreur( 'Mois', bmois ) | |||
end | |||
mois = mois1 .. sep .. mois2 | |||
else | |||
return erreur( 'Mois', bmois ) | |||
end | |||
end | end | ||
end | end | ||
-- on traite le jour si présent | |||
if Outils.notEmpty( bjour ) then | |||
if not numMois then | |||
erreur( 'Date', 'jour avec saison ou plusieurs mois' ) | |||
end | |||
jour = tonumber( bjour ) | |||
if jour == nil then | |||
jour = tonumber( fun.nettoyageJour( bjour ) ) | |||
end | |||
if jour == nil then | |||
return erreur( 'Jour', bjour ) | |||
end | |||
-- on valide que le jour est correct | |||
if jour < 1 or jour > 31 then | |||
return erreur( 'Jour', bjour ) | |||
elseif jour > listeMois[numMois].nJour then | |||
return erreur( 'Jour', bjour .. ' ' .. mois ) | |||
elseif jour == 29 and numMois == 2 and annee and ( math.fmod( annee, 4 ) ~= 0 ) then | |||
-- l'année bisextile sur les siècles est toujours acceptée pour être compatible avec les dates juliennes. | |||
return erreur( 'Jour', '29 février ' .. annee ) | |||
end | |||
else | |||
-- S'il n'y a pas de jour on regarde si la première lettre du mois est en majuscule | |||
if bmois:match( '^%u' ) then | |||
-- oui, on passe la première lettre en majuscule | |||
mois = ucfirst( mois ) | |||
end | 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' ) | |||
if | |||
else | 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 | end | ||
end | end | ||
return | |||
-- vérification de l'absence d'un décalage | |||
if annee and annee < 13 and annee > 0 and not jour and ( tonumber( bmois ) or ( not mois and tonumber( args[4] ) ) ) then | |||
return false, '<span class="error">année improbable (' .. annee .. ')</span>' | |||
end | |||
local resultat = { | |||
jour = jour, | |||
mois = mois, | |||
numMois = numMois, | |||
annee = annee, | |||
masquerAnnee = args.masquerAnnee, | |||
masquerMois = args.masquerMois, | |||
} | |||
return true, resultat | |||
end | end | ||
--- | |||
-- émule le modèle {{m|Date}}. | |||
-- Paramètres : | |||
-- | -- 1 : jour (numéro ou "1er") ou la date complète | ||
-- 2 : mois (en toutes lettres) ou spécialité de l'année | |||
-- 3 : année (nombre) | |||
-- 4 : spécialité de l'année | |||
-- julien : date dans le calendrier julien | |||
-- compact : affiche le mois sous forme d'abréviation | |||
-- avJC : non pour désactiver l'affichage de « av. J.-C. » pour les dates négatives | |||
-- âge : ajoute la durée depuis cette date | |||
-- agePrefix : préfixe pour l'age, 'à ' par défaut pour les décès | |||
-- nolinks : ne met pas de lien sur la date | |||
-- afficherErreurs : en cas d'erreur, si défini à "non" ne retourne pas un message d'erreur, mais le 1er argument inchangé | |||
-- categoriserErreurs : en cas d'erreur, si défini à "non" ne catégorise pas ; peut aussi être défini avec une catégorie à utiliser à la place de celle par défaut | |||
-- naissance : ajoute la class "bday" | |||
-- mort : ajoute la class "dday" | |||
function fun.modeleDate( frame ) | |||
local Yesno = require 'Module:Yesno' | |||
local args = Outils.extractArgs( frame ) | |||
local resultat | |||
local | local dateNaissanceMort | ||
-- analyse des paramètres non nommés (ou paramètres de la date jour, mois, annee) | |||
local test, params | |||
local arg1, arg2, arg3 = fun.nettoyageJour( args[1] ), trim( args[2] ), trim( args[3] ) | |||
local | if type( arg1 ) == 'string' and arg3 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or arg2 == nil or dataLiens[arg2] or mw.ustring.match( arg2, '%a %a' ) ) then | ||
-- la date est dans le premier paramètre | |||
test, params = fun.separationJourMoisAnnee( arg1 ) | |||
if test then | |||
dateNaissanceMort = trim( arg2 ) | |||
params.qualificatif = trim( arg2 ) | |||
end | |||
elseif type( arg1 ) == 'string' and type( arg2 ) == 'string' and arg3 ~= nil and arg4 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or dataLiens[arg3] or mw.ustring.match( arg3, '%a %a' ) ) then | |||
-- la date est dans le premier paramètre | |||
test, params = fun.separationJourMoisAnnee( arg1 ) | |||
if test then | |||
dateNaissanceMort = trim( arg2 ) | |||
params.qualificatif = trim( arg3 ) | |||
end | |||
else | |||
local function masquerParam( p ) | |||
-- sépare le signe moins final éventuel signifiant que le paramètre ne doit pas être affiché. | |||
if type( p ) ~= 'string' then | |||
return p, nil | |||
if | |||
end | end | ||
local value = | local value, mask = p:match( '^%s*(.-)(%-?)%s*$' ) | ||
return value, ( mask == '-' or nil ) | |||
end | |||
local cleanArgs = { arg1 or args.jour } | |||
cleanArgs[2], cleanArgs.masquerMois = masquerParam( args[2] or args.mois ) | |||
cleanArgs[3], cleanArgs.masquerAnnee = masquerParam( args[3] or args.annee or args['année'] ) | |||
test, params = fun.validationJourMoisAnnee( cleanArgs ) | |||
if test then | |||
params.qualificatif = trim( args[4] ) | |||
end | end | ||
end | end | ||
if | -- analyse des paramètres nommés | ||
-- | if test then | ||
params.agePrefix = args.agePrefix | |||
if | if args.qualificatif and args.qualificatif ~= '' then | ||
params.qualificatif = args.qualificatif | |||
end | |||
-- julien peut avoir trois valeurs : inactif, format standard (true), format court | |||
params.julien = Yesno( args.julien, 'court', false ) | |||
params.avJC = Yesno( args.avJC ) | |||
if args['républicain'] and args['républicain'] ~= '' then | |||
if args['républicain'] == 'liens' then | |||
params.republicain = 'liens' | |||
else | |||
params.republicain = Yesno( args['républicain'], false ) | |||
end | end | ||
else | |||
params.republicain = false | |||
end | end | ||
if args.dateNaissanceMort and args.dateNaissanceMort ~= '' then | |||
dateNaissanceMort = args.dateNaissanceMort | |||
elseif args['dateNaissanceÉvénement'] and args['dateNaissanceÉvénement'] ~= '' then | |||
dateNaissanceMort = args['dateNaissanceÉvénement'] | |||
end | end | ||
if | if dateNaissanceMort then | ||
local testNaissanceMort, paramsNaissanceMort = fun.separationJourMoisAnnee( dateNaissanceMort ) | |||
if testNaissanceMort then | |||
params.anneeNaissanceMort, params.moisNaissanceMort, params.numMoisNaissanceMort, params.jourNaissanceMort = paramsNaissanceMort.annee, paramsNaissanceMort.mois, paramsNaissanceMort.numMois, paramsNaissanceMort.jour | |||
end | |||
end | end | ||
local listeParam = { | |||
age = 'âge', | |||
['âge'] = 'âge', | |||
naissance = 'naissance', | |||
mort = 'mort', | |||
['événement'] = 'événement', | |||
evenement = 'evenement', | |||
['décès'] = 'mort', | |||
apJC = 'apJC', | |||
nolinks = 'nolinks', | |||
compact = 'compact', | |||
compacte = 'compact', | |||
} | |||
for n, v in pairs( listeParam ) do | |||
params[v] = params[v] or Yesno( args[n], true, false ) or nil | |||
end | end | ||
-- sortie pour les tests unitaire, ou pour débugger | |||
if args.debug then | |||
return params | |||
if | |||
return | |||
end | end | ||
resultat = fun._modeleDate( params ) | |||
else | else | ||
local yn_afficherErreurs = Yesno( args.afficherErreurs ) | |||
if yn_afficherErreurs == nil or yn_afficherErreurs == true then | |||
resultat = params | |||
else | else | ||
resultat = args[1] | |||
end | end | ||
local | local currentTitle = mw.title.getCurrentTitle() | ||
if currentTitle:inNamespaces( 0, 4, 10, 14, 100 ) | |||
and not Outils.notEmpty( args.nocat ) | |||
and not currentTitle.prefixedText:match( '^Modèle:.+/Test$' ) then | |||
local categorie | |||
local yn_categoriserErreurs = Yesno( args.categoriserErreurs, 'custom', true ) | |||
if yn_categoriserErreurs == nil or yn_categoriserErreurs == true then | |||
categorie = '[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée]]' | |||
elseif yn_categoriserErreurs == false then | |||
categorie = '' | |||
else | |||
local nomCategorie = args.categoriserErreurs | |||
:gsub( '^%[%[', '' ) | |||
:gsub( '%]%]$', '' ) | |||
:gsub( '^:?[Cc]atégorie:', '' ) | |||
:gsub( '^:?[Cc]atégory:', '' ) | |||
categorie = '[[Catégorie:' .. nomCategorie .. ']]' | |||
end | |||
resultat = resultat .. categorie | |||
end | |||
end | end | ||
return resultat or '' | |||
return | |||
end | 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 | |||
if | |||
end | end | ||
local | |||
-- on traite l'âge, naissance et mort | |||
local agePrefix = args.agePrefix | |||
local age = args['âge'] and fun.age( annee, numMois, jour ) | |||
local naissance = args.naissance | |||
local mort = args.mort | |||
local evenement = args['événement'] or args.evenement | |||
if mort and args.anneeNaissanceMort then | |||
age = fun.age( args.anneeNaissanceMort, args.numMoisNaissanceMort, args.jourNaissanceMort, annee, numMois, jour ) | |||
agePrefix = agePrefix or 'à ' -- faut-il mettre \194\160 ? | |||
elseif evenement and args.anneeNaissanceMort then | |||
if naissance then | |||
age = fun.age( annee, numMois, jour, args.anneeNaissanceMort, args.numMoisNaissanceMort, args.jourNaissanceMort ) | |||
else | |||
age = fun.age(args.anneeNaissanceMort, args.numMoisNaissanceMort, args.jourNaissanceMort, annee, numMois, jour ) | |||
end | |||
end | end | ||
agePrefix = agePrefix or '' | |||
-- on traite le calendrier | |||
-- | local gannee, gmois, gjour = annee, numMois, jour -- date suivant le calendrier grégorien pour <time> | ||
-- | local jannee, jmois, jjour = annee, mois, jour -- date suivant le calendrier julien si necessaire | ||
local julienDate, julienSup, julienSep -- servira éventuellement à afficher la date selon le calendrier julien | |||
local gregAprMois, gregAprAn, gregFin -- message de calendrier grégorien lorsque la date est selon le calendrier julien | |||
if annee and jour then | |||
local amj = annee * 10000 + numMois * 100 + jour | |||
if amj < 15821014 then | |||
if annee > 0 then | |||
-- | gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour ) | ||
-- | else | ||
-- calendrier grégorien proleptique avec année 0. | |||
gannee, gmois, gjour = fun.julianToGregorian( annee + 1, numMois, jour ) | |||
if | |||
end | end | ||
elseif | args.julien = false | ||
if | elseif args.julien then | ||
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour ) | |||
annee, mois, jour = gannee, listeMois[gmois].nom, gjour | |||
if jjour == 1 then | |||
jjour = modelePremier | |||
end | end | ||
if | if args.compact then | ||
jmois = listeMois[jmois].abrev | |||
end | end | ||
if | if args.julien == 'court' then | ||
julienDate = jjour .. ' ' .. jmois .. ' ' | |||
julienSup = '<sup>[[calendrier julien|jul.]]</sup>' | |||
if jannee == annee then | |||
gregAprMois = '<sup>[[calendrier grégorien|grég.]]</sup>' | |||
else | |||
julienDate = julienDate .. jannee .. ' ' | |||
gregAprAn = '<sup>[[calendrier grégorien|grég.]]</sup>' | |||
end | end | ||
julienSep = ' / ' | |||
else | |||
julienDate = jjour .. ' ' .. jmois .. ' ' .. jannee | |||
julienSep = ' (' | |||
gregFin = ' [[Passage du calendrier julien au calendrier grégorien|dans le calendrier grégorien]])' | |||
end | end | ||
else | elseif args.republicain then | ||
local DateRep = require 'Module:Date républicaine' | |||
local RepSansLiens | |||
if args.republicain == 'liens' then | |||
RepSansLiens = false | |||
else | |||
RepSansLiens = true | |||
end | |||
dateRepublicaine = DateRep._date_republicaine( | |||
RepSansLiens, | |||
{ fun.formatRepCal( fun.do_toRepCal{gannee, gmois, gjour} ) } | |||
) | |||
end | |||
else | |||
if annee and annee < 0 then | |||
gannee = gannee + 1 | |||
end | end | ||
args.julien = false | |||
args.republicain = false | |||
end | end | ||
-- on génère le résultat | |||
local | |||
if | -- Déclarations des variables | ||
if | local wikiListe = {} -- reçoit le texte affiché pour chaque paramètre | ||
local iso = {} -- reçoit le format date ISO de ce paramètre | |||
local texteMois = mois -- texte du mois qui sera affiché (éventuellement l'abréviation) | |||
if args.compact then | |||
if not numMois then | |||
-- mois est autre chose qu'un simple mois : saison, mois-mois... auquel cas, pas d'abréviation (provoquait erreur Lua) | |||
-- (les abréviations pour le cas "mois[-/]mois" seraient théoriquement possibles, mais ça reste à implémenter) | |||
else | else | ||
if args.nolinks then | |||
texteMois = '<abbr class=abbr title="' .. mois .. '">' .. listeMois[mois].abrev .. '</abbr>' | |||
else | |||
texteMois = listeMois[mois].abrev | |||
end | end | ||
end | end | ||
end | |||
mois = mois and mois:gsub( 'aout', 'août' ) | |||
local dataQualificatif, dataCat | |||
if not args.nolinks then | |||
dataQualificatif = dataLiens[qualificatif or ''] | |||
if type( dataQualificatif ) ~= 'table' then | |||
-- si le qualificatif n'est pas dans la base de données, on crée une table minimum, | |||
-- qui imposera un test sur l'année, mais considère qu'il n'y a pas de lien sur le jour ou le mois | |||
dataQualificatif = { qualificatif = qualificatif, annee = { } } | |||
end | end | ||
dataCat = dataLiens[dataQualificatif.cat] | |||
if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then | |||
dataCat = { qualificatif = '' } | |||
end | end | ||
end | end | ||
if | local function wikiLien( lien, texte ) | ||
if lien == texte then | |||
return | return '[[' .. texte .. ']]' | ||
else | |||
return '[[' .. lien .. '|' .. texte .. ']]' | |||
end | end | ||
end | end | ||
-- le jour si présent | |||
-- | local qualifJour = '' | ||
if jour then | |||
if args.nolinks then | |||
if jour == 1 then | |||
jour = modelePremier | |||
if | |||
if | |||
end | end | ||
if | table.insert( wikiListe, jour ) | ||
else | |||
qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif | |||
or dataCat.jour and dataCat.qualificatif | |||
or '' | |||
local texteJour, lien | |||
if jour == 1 then | |||
texteJour = '1<sup>er</sup>' | |||
lien = '1er ' .. mois | |||
else | |||
texteJour = jour | |||
lien = jour .. ' ' .. mois | |||
end | end | ||
if qualifJour ~= '' then | |||
lien = lien .. ' ' .. qualifJour | |||
if | |||
end | end | ||
-- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour. | |||
table.insert( wikiListe, wikiLien( lien, texteJour ) ) | |||
table.insert( wikiListe, wikiLien( lien, texteJour .. ' '.. texteMois ) ) | |||
end | end | ||
table.insert( iso, 1, string.sub( '0' .. gjour, -2 ) ) | |||
end | end | ||
-- le mois | |||
if mois then | |||
if | if #wikiListe == 0 and annee == nil then | ||
if | return texteMois | ||
end | |||
if args.nolinks then | |||
if not args.masquerMois then | |||
table.insert( wikiListe, texteMois ) | |||
end | end | ||
if | else | ||
local lien | |||
if annee then | |||
if not numMois then | |||
-- mois est autre chose qu'un simple mois : saison, mois-mois... auquel cas, pas de lien | |||
else | |||
lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois ) | |||
if lien == nil and qualificatif and qualifJour == '' then | |||
-- nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif. | |||
lien = existDate( dataLiens[''], annee, mois ) | |||
end | |||
end | |||
end | end | ||
if lien or args.masquerMois then | |||
-- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois]]' | |||
table.remove( wikiListe ) | |||
if not args.masquerMois then | |||
table.insert( wikiListe, wikiLien( lien, texteMois ) ) | |||
if | |||
end | end | ||
elseif | elseif #wikiListe > 0 then | ||
-- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois' | |||
table.remove( wikiListe, #wikiListe - 1 ) | |||
elseif args.masquerAnnee then | |||
-- s'il n'y a pas de jour et que l'année n'est pas affichée, on insère le mois seul. | |||
table.insert( wikiListe, texteMois ) | |||
end | end | ||
end | end | ||
if gmois then | |||
table.insert( iso, 1, string.sub( '0' .. gmois, -2 ) ) | |||
if | |||
end | end | ||
table.insert( wikiListe, gregAprMois ) | |||
end | end | ||
local | -- l'année | ||
if annee and not (args.julien == true and args.nolinks and jannee == annee ) then | |||
if not args.masquerAnnee then | |||
local texteAnnee = annee | |||
local lien | |||
if annee < 0 then | |||
local annneeAvJc = 0 - annee | |||
lien = lien or ( annneeAvJc .. ' av. J.-C.' ) | |||
if args.avJC == false then | |||
texteAnnee = annneeAvJc | |||
else | |||
texteAnnee = annneeAvJc .. ' <abbr class="abbr" title="' | |||
.. annneeAvJc .. ' avant Jésus-Christ">av. J.-C.</abbr>' | |||
end | |||
elseif args.apJC then | |||
texteAnnee = texteAnnee .. ' <abbr class="abbr" title="' | |||
.. texteAnnee .. ' après Jésus-Christ">apr. J.-C.</abbr>' | |||
end | end | ||
if args.nolinks then -- seulement si on doit l'afficher | |||
table.insert( wikiListe, texteAnnee ) | |||
else | 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. | |||
if | texteAnnee = texteMois .. ' ' .. texteAnnee | ||
end | end | ||
table.insert( wikiListe, wikiLien( lien, texteAnnee ) ) | |||
end | end | ||
end | end | ||
end | end | ||
if annee then | |||
if gannee > 999 then | |||
table.insert( iso, 1, gannee ) | |||
elseif gannee > -1 then | |||
table.insert( iso, 1, string.sub( '000' .. gannee , -4 ) ) | |||
elseif gannee > -999 then | |||
-- calendrier grégorien proleptique avec année 0. | |||
table.insert( iso, 1, 'U-' .. string.sub( '000' .. ( 0 - gannee ), -4 ) ) | |||
elseif | |||
elseif | |||
else | else | ||
table.insert( iso, 1, 'U' .. gannee ) | |||
end | end | ||
end | end | ||
table.insert( wikiListe, gregAprAn ) | |||
-- l'age | |||
-- | if type( age ) == 'number' and age >= 0 and ( not naissance or age < 120 ) then | ||
if age == 0 then | |||
age = '(' .. agePrefix .. 'moins d’un\194\160an)' | |||
elseif age == 1 then | |||
age = '(' .. agePrefix .. '1\194\160an)' | |||
else | |||
age = '('.. agePrefix .. age .. '\194\160ans)' | |||
end | end | ||
else | |||
age = false | |||
end | end | ||
local | |||
-- compilation du résultat | |||
-- | local wikiTexte = table.concat( wikiListe, ' ' ) | ||
local isoTexte = table.concat( iso, '-' ) | |||
if | -- On ajoute un peu de sémantique. | ||
local wikiHtml = mw.html.create( '' ) | |||
if julienDate then | |||
wikiHtml:tag( 'span') | |||
:addClass( 'nowrap' ) | |||
:attr( 'data-sort-value', isoTexte ) | |||
:wikitext( julienDate ) | |||
:node( julienSup ) | |||
:done() | |||
:wikitext( julienSep ) | |||
end | end | ||
local | |||
local dateHtml = wikiHtml:tag( 'time' ) | |||
:wikitext( wikiTexte ) | |||
if wikiTexte:match( ' ' ) then | |||
dateHtml:addClass( 'nowrap' ) | |||
end | end | ||
if | if isoTexte ~= wikiTexte then | ||
dateHtml:attr( 'datetime', isoTexte ) | |||
:attr( 'data-sort-value', isoTexte ) | |||
end | end | ||
if | if not args.nolinks then | ||
dateHtml:addClass( 'date-lien' ) | |||
end | end | ||
if | if naissance then | ||
dateHtml:addClass( 'bday' ) | |||
elseif mort then | |||
dateHtml:addClass( 'dday' ) | |||
end | end | ||
wikiHtml:wikitext( gregFin ) | |||
if args.republicain then | |||
wikiHtml:wikitext( ' (', dateRepublicaine, ')' ) | |||
if | |||
end | end | ||
if age then | |||
wikiHtml:wikitext( ' ' ) | |||
:tag( 'span' ) | |||
:addClass( 'noprint') | |||
:wikitext( age ) | |||
:done() | |||
end | end | ||
return tostring( wikiHtml ) | |||
return tostring( | |||
end | end | ||
--- | |||
-- fonction destinée aux infobox, notamment pour afficher les dates de naissance et de mort | |||
-- les liens présent dans les dates fournies sont automatiquement supprimés pour gérer les cas où | |||
-- le paramètre contient déjà un modèle date. | |||
if | -- Paramètres : | ||
-- 1 : type de date à afficher (naissance / n, mort / m, ou date / d) | |||
-- 1 : Date ou date de naissance | |||
return | -- 2 : Date de mort si type n ou m | ||
-- qualificatif = suffixe des page de date à lier (exemple : en musique) | |||
-- nolinks : n'affiche pas de lien | |||
-- préfixe : préfixe à afficher s'il y a un jour (par défaut '') | |||
-- préfixe sans jour : préfixe à afficher s'il n'y a pas de jour (par défaut : '') | |||
function fun.dateInfobox( frame ) | |||
local args = frame.args | |||
if type( args ) ~= 'table' or not ( args[1] and args[2] ) then | |||
return | |||
end | end | ||
-- analyseDate sépare la date du contenu qui suit, supprime les liens, et retourne si possible une table avec jour mois année | |||
-- | local function analyseDate( d ) | ||
if trim( d ) then | |||
local analyse = d:match( ' ou ') or d:match( 'entre ' ) or d:match( 'vers ' ) or d:match( 'après ' ) or d:match( 'avant ' ) | |||
if analyse then | |||
return d | |||
if | |||
if | |||
return | |||
end | end | ||
analyse = d:match( 'datetime="([%d-]+)"' ) or d | |||
local | -- sépare la date (avec ses liens) d'une référence ou contenu commençant par un espace) | ||
if not | local debut, fin = analyse:match( '(.-%d%d%d%]*%-?)([\127 ].+)' ) | ||
if not debut then | |||
-- sépare la date du contenu commençant par <br> | |||
debut, fin = analyse:match( '(.-%d%d%d%]*%-?)(<br ?/?>.+)' ) | |||
end | end | ||
analyse = debut or analyse | |||
-- supprime les liens | |||
analyse = analyse:gsub( | |||
'%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', | |||
function ( l, t ) | |||
return trim( t ) or l | |||
end | end | ||
) | |||
local t, r = fun.separationJourMoisAnnee( analyse ) | |||
if t then | |||
return r, fin | |||
else | else | ||
return | return d, fin | ||
end | end | ||
end | end | ||
end | end | ||
if | -- prefix ajoute un préfixe en fonction de la présence ou non du jour si le paramètre "préfixe sans jour" est défini | ||
local function prefix( dateString ) | |||
if dateString then | |||
local datetime = dateString:match( 'datetime="([U%d%-]+)"' ) | |||
if datetime and datetime:match('%-%d%d%-%d%d') and trim( args['préfixe'] ) then | |||
return args['préfixe'] .. ' ' .. dateString | |||
end | |||
if trim( args['préfixe sans jour'] ) then | |||
return args['préfixe sans jour'] .. ' ' .. dateString | |||
end | |||
end | end | ||
return dateString | |||
end | end | ||
local | local naissance = args[1]:match( '^n' ) == 'n' | ||
local mort = args[1]:match( '^m' ) or args[1]:match( 'décès' ) | |||
local evenement = args[1]:match( '^é' ) | |||
local affichageDate, qualificatif = args[2], args[4] | |||
local affichageDateTab, resultatDate, complementDate | |||
local dateNaissance, dateMort | |||
local | if mort or evenement then | ||
if | affichageDate = args[3] | ||
end | end | ||
if not | if not trim( affichageDate ) then | ||
return | |||
end | end | ||
if | if affichageDate:match( '</time>' ) then | ||
-- | -- S'il y a des liens il y a probablement déjà un modèle date, évitons de l'exécuter une 2e fois | ||
if ( naissance or mort or evenement ) and ( affichageDate:match( 'wikidata%-linkback' )) then | |||
dateNaissance = analyseDate( args[2] ) | |||
dateMort = analyseDate( args[3] ) | |||
resultatDate = affichageDate | |||
else | |||
return prefix( affichageDate ) | |||
return | |||
end | end | ||
if | else | ||
if | 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 | end | ||
affichageDateTab.naissance = naissance | |||
affichageDateTab.mort = mort | |||
affichageDateTab.evenement = evenement | |||
affichageDateTab.qualificatif = args.qualificatif or qualificatif | |||
affichageDateTab.nolinks = args.nolinks | |||
affichageDateTab.nocat = args.nocat | |||
affichageDateTab.julien = args.julien | |||
end | end | ||
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 | end | ||
elseif (mort or evenement) 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 | end | ||
if tonumber( calculAge ) then | |||
if | if calculAge > 1 then | ||
age = prefixAge .. calculAge .. '\194\160ans' .. suffixAge | |||
elseif calculAge == 1 then | |||
if | age = prefixAge .. 'un\194\160an' .. suffixAge | ||
elseif calculAge == 0 then | |||
age = prefixAge .. 'moins d’un\194\160an' .. suffixAge | |||
end | end | ||
if | if complementDate and complementDate:match( 'ans?%)' ) then | ||
complementDate = '' | |||
end | end | ||
end | end | ||
if | |||
local | return prefix( resultatDate ) .. ( complementDate or '' ) .. age | ||
if | end | ||
if | |||
--- | |||
-- la fonction dateISO renvoie un date au format aaaa-mm-jj (sans liens) | |||
-- l'année peut être sous la forme 2013 ou [[2013 en litérature|2013]] | |||
-- le mois peut être en lettres ou en chiffres | |||
-- le jour peut être sous la forme '05', '{{1er}}' ou 'vendredi 13' | |||
function fun.dateISO( frame ) | |||
local args = Outils.extractArgs( frame ) | |||
local annee = Outils.notEmpty( args['année'], args.annee, args.year, args.date ) | |||
-- extraction de l'année | |||
if type( annee ) == 'string' then | |||
annee = ( tonumber( annee ) -- match '2013' | |||
or string.match ( annee, '%D(%d%d%d%d)%D' ) -- match '[[2013 en musique|2013]]' | |||
or string.match ( annee, '%D(%d%d%d%d)$' ) -- match '17 septembre 2013' | |||
or string.match ( annee, '^(%d%d%d%d)%D' ) -- match '2013-09-17' | |||
) | |||
end | |||
annee = tonumber( annee ) | |||
-- le format de date iso est défini suivant le calendrier grégorien. | |||
-- Avant l'année 1583 la date est calendrier est probablement du calendrier julien, | |||
-- donc autant s'abstenir. | |||
if annee and annee > 1582 then | |||
local mois = Outils.notEmpty( args.mois, args.month ) | |||
-- num mois trouve le numéro du mois, qu'il soit numérique ou texte, complet ou abrégé. | |||
local nomMois, numMois = fun.determinationMois( mois ) | |||
if numMois then | |||
mois = '-' .. string.sub( '0' .. numMois, -2 ) | |||
local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] ) | |||
if type( jour ) == 'string' then | |||
jour = tonumber( jour ) or tonumber( string.match ( jour, '%d+') ) | |||
end | end | ||
jour = tonumber( jour ) | |||
if | if jour and jour <= listeMois[numMois].nJour then | ||
jour = '-' .. string.sub( '0' .. jour, -2 ) | |||
return annee .. mois .. jour | |||
else | |||
return annee .. mois | |||
end | end | ||
else | |||
return tostring( annee ) | |||
end | end | ||
end | end | ||
local | 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 | 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 | 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 | end | ||
result = p .. result | |||
n = math.floor(n/10) | |||
end | end | ||
end | end | ||
return result | |||
end | |||
-- Conversion et affichage d'une date dans le calendrier républicain | |||
function fun.dateRepublicain(frame) | |||
local pframe = frame:getParent() | |||
local arguments = pframe.args | |||
return fun.formatRepCal(fun.do_toRepCal(arguments)) | |||
end | |||
--- | |||
-- Calcul d'une date dans le calendrier républicain | |||
-- On suppose que les années 4n+3 sont sextiles (3, 7, 11...) | |||
function fun.do_toRepCal(arguments) | |||
local yr = tonumber(arguments.year or arguments[1]) or 2000 | |||
-- rang absolu du jour demandé, le jour 0 étant le 22 septembre 1792 (1er jour de l'an I) | |||
local repDays = fun.do_dayRank(arguments) + fun.do_daysBetween{1792,yr} - fun.do_dayRank{1792,9,22} | |||
local repYear = math.floor((repDays+731)/365.25) - 1 | |||
local repDayRank = repDays - 365*(repYear-1) - math.floor(repYear/4) | |||
local repMonth, repDay = math.floor(repDayRank/30)+1, (repDayRank%30)+1 | |||
return {repYear, repMonth, repDay} | |||
end | |||
--- | |||
-- Formatage d'une date selon le calendrier républicain | |||
-- Usage : fun.formatRepCal{année,mois,jour} | |||
function fun.formatRepCal(arguments) | |||
local months = {"Vendémiaire","Brumaire","Frimaire","Nivôse","Pluviôse","Ventôse","Germinal","Floréal","Prairial","Messidor","Thermidor","Fructidor"} | |||
local extras = {"de la vertu","du génie","du travail","des récompenses","de l'opinion","de la Révolution"} | |||
local result = "" | |||
if(arguments[2] < 13) then | |||
result = result .. tostring(arguments[3]) .. "\194\160" .. months[arguments[2]] | |||
else | |||
result = result .. "jour " .. extras[arguments[3]] | |||
end | end | ||
return | result = result .. " de l'an " .. fun.toRoman(arguments[1]) | ||
return result | |||
end | end | ||
--- | |||
if | -- Voir Modèle:Âge | ||
-- retourne l'âge en fonction de la ou les dates fournies. La valeur retournée est de type 'number' | |||
-- Paramètres : | |||
-- 1, 2, 3 : année, mois jour de naissance (supposé dans le calendrier grégorien) | |||
-- 4, 5, 6 : année, mois, jour du calcul (facultatif, par défaut la date UTC courante). | |||
function fun.age( an, mn, jn, ac, mc, jc ) | |||
if ac == nil then | |||
local today = os.date( '!*t' ) | |||
ac = today.year | |||
mc = today.month | |||
jc = today.day | |||
else | |||
ac = tonumber( ac ) | |||
mc = tonumber( mc ) | |||
jc = tonumber( jc ) | |||
end | 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 | return | ||
end | end | ||
local | |||
if | 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 | ||
local | end | ||
local | |||
function fun.modeleAge( frame ) | |||
local args = Outils.extractArgs( frame ) | |||
local age = fun.age ( | |||
args[1] or args['année'], | |||
args[2] or args['mois'], | |||
args[3] or args['jour'], | |||
args[4], | |||
args[5], | |||
args[6] | |||
) | |||
if age then | |||
return age | |||
else | |||
return '<span class="error">Paramètres incorrects ou insuffisants pour calculer l\'âge précis</span>' | |||
end | end | ||
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 ) | |||
if | + math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 ) | ||
+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400 | |||
- 32167.5 | |||
return julian | |||
end | |||
--- | |||
-- calcul du jour julien à partir d'une date du calendrier julien | |||
function fun.julianDayJulian( year, month, day, hour, min, sec ) | |||
local julian | |||
julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 ) | |||
+ math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 ) | |||
+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400 | |||
- 32205.5 | |||
return julian | |||
end | |||
--- | |||
-- calcul d'une date dans le calendrier grégorien à partir du jour julien | |||
function fun.julianDayToGregorian( julianDay ) | |||
local base = math.floor( julianDay + 32044.5 ) -- 1 March -4800 (proleptic Gregorian date) | |||
local nCentury = math.floor( ( base * 4 + 3 ) / 146097 ) | |||
local sinceCentury = base - math.floor( nCentury * 146097 / 4 ) | |||
local nYear = math.floor( ( sinceCentury * 4 + 3 ) / 1461 ) | |||
local sinceYear = sinceCentury - math.floor( nYear * 1461 / 4 ) | |||
local nMonth = math.floor( ( sinceYear * 5 + 2 ) / 153 ) | |||
local day = sinceYear - math.floor( ( nMonth * 153 + 2 ) / 5 ) + 1 | |||
local month = nMonth - math.floor( nMonth / 10 ) * 12 + 3 | |||
local year = math.floor( sinceYear / 306 ) + nYear + 100 * nCentury - 4800 | |||
return year, month, day | |||
end | |||
--- | |||
-- calcul d'une date dans le calendrier julien à partir du jour julien | |||
-- calcul basé sur l'algorithme de la page fr.wikipedia.org/wiki/Jour_julien (1/10/2013) | |||
function fun.julianDayToJulian( julianDay ) | |||
local year = math.modf( ( julianDay * 4 - 6884469 ) / 1461 ) | |||
local r2 = julianDay - math.modf( ( 1461 * year + 6884472 ) / 4 ) | |||
local month = math.modf( ( 5 * r2 + 461 ) / 153 ) | |||
local day = r2 - math.modf( ( 153 * month - 457 ) / 5 ) + 1 | |||
if month > 12 then | |||
year = year + 1 | |||
month = month - 12 | |||
end | |||
return year, month, day | |||
end | |||
--- | |||
-- calcul d'une date dans le calendrier grégorien à partir d'une date dans le calendrier julien | |||
function fun.julianToGregorian( year, month, day ) | |||
return fun.julianDayToGregorian( fun.julianDayJulian( year, month, day ) ) | |||
end | |||
--- | |||
-- calcul d'une date dans le calendrier julien à partir d'une date dans le calendrier grégorien | |||
function fun.gregorianToJulian( year, month, day ) | |||
year = tonumber(year) | |||
if month then month = tonumber(month) else month = 6 end --prend une valeur centrale pour donner un best "guess" | |||
if day then day = tonumber(day) else day = 15 end | |||
return fun.julianDayToJulian( fun.julianDay( year, month, day ) ) | |||
end | |||
--[[ | |||
Cette fonction retourne "CET" ou "CEST" selon que dans la pseudo-timezone en cours | |||
c'est l'heure d'été ou l'heure d'hiver. | |||
Cette fonction n'a de sens a priori que pour des modèles utilisés en Europe | |||
Paramètre optionnel non nommé : "sans lien" : retourne le texte CET/CEST. sinon | |||
retourne ce même texte avec un wikilien vers les articles correspondants | |||
--]] | |||
function fun.CEST(frame) | |||
-- option : ne pas créer de wikilien | |||
local opt = trim(frame.args[1] or frame:getParent().args[1]) | |||
-- on récupère l'information dans la zone courante | |||
local t = mw.getContentLanguage():formatDate("I", nil, true) | |||
if (t == "1") then -- heure d'été | |||
if (opt == "sans lien") then | |||
return "CEST" | |||
elseif (opt == "décalage") then | |||
return "2" | |||
else | else | ||
return "[[Heure d'été d'Europe centrale|CEST]]" | |||
end | 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 | else | ||
return "[[Heure normale d'Europe centrale|CET]]" | |||
end | end | ||
end | end | ||
end | end | ||
return | return fun | ||
Revision as of 11:46, 5 December 2021
This module provides date functions for use by other modules. Dates in the Gregorian calendar and the Julian calendar are supported, from 9999 BCE to 9999 CE. The calendars are proleptic—they are assumed to apply at all times with no irregularities.
A date, with an optional time, can be specified in a variety of formats, and can be converted for display using a variety of formats, for example, 1 April 2016 or April 1, 2016. The properties of a date include its Julian date and its Gregorian serial date, as well as the day-of-week and day-of-year.
Dates can be compared (for example, date1 <= date2
), and can be used with add or subtract (for example, date + '3 months'
). The difference between two dates can be determined with date1 - date2
. These operations work with both Gregorian and Julian calendar dates, but date1 - date2
is nil if the two dates use different calendars.
The module provides the following items.
Export | Description |
---|---|
_current |
Table with the current year, month, day, hour, minute, second. |
_Date |
Function that returns a table for a specified date. |
_days_in_month |
Function that returns the number of days in a month. |
The following has examples of using the module:
- Module:Date/example • Demonstration showing how Module:Date may be used.
- Module talk:Date/example • Output from the demonstration.
Formatted output
A date can be formatted as text. <syntaxhighlight lang="lua"> local Date = require('Module:Date')._Date local text = Date(2016, 7, 1):text() -- result is '1 July 2016' local text = Date(2016, 7, 1):text('%-d %B') -- result is '1 July' local text = Date('1 July 2016'):text('mdy') -- result is 'July 1, 2016' </syntaxhighlight>
The following simplified formatting codes are available.
Code | Result |
---|---|
hm | hour:minute, with "am" or "pm" or variant, if specified (14:30 or 2:30 pm or variant) |
hms | hour:minute:second (14:30:45) |
ymd | year-month-day (2016-07-01) |
mdy | month day, year (July 1, 2016) |
dmy | day month year (1 July 2016) |
The following formatting codes (similar to strftime) are available.
Code | Result |
---|---|
%a | Day abbreviation: Mon, Tue, ... |
%A | Day name: Monday, Tuesday, ... |
%u | Day of week: 1 to 7 (Monday to Sunday) |
%w | Day of week: 0 to 6 (Sunday to Saturday) |
%d | Day of month zero-padded: 01 to 31 |
%b | Month abbreviation: Jan to Dec |
%B | Month name: January to December |
%m | Month zero-padded: 01 to 12 |
%Y | Year zero-padded: 0012, 0120, 1200 |
%H | Hour 24-hour clock zero-padded: 00 to 23 |
%I | Hour 12-hour clock zero-padded: 01 to 12 |
%p | AM or PM or as in options |
%M | Minute zero-padded: 00 to 59 |
%S | Second zero-padded: 00 to 59 |
%j | Day of year zero-padded: 001 to 366 |
%-d | Day of month: 1 to 31 |
%-m | Month: 1 to 12 |
%-Y | Year: 12, 120, 1200 |
%-H | Hour: 0 to 23 |
%-M | Minute: 0 to 59 |
%-S | Second: 0 to 59 |
%-j | Day of year: 1 to 366 |
%-I | Hour: 1 to 12 |
%% | % |
In addition, %{property}
(where property
is any property of a date) can be used.
For example, Date('1 Feb 2015 14:30:45 A.D.')
has the following properties.
Code | Result |
---|---|
%{calendar} | Gregorian |
%{year} | 2015 |
%{month} | 2 |
%{day} | 1 |
%{hour} | 14 |
%{minute} | 30 |
%{second} | 45 |
%{dayabbr} | Sun |
%{dayname} | Sunday |
%{dayofweek} | 0 |
%{dow} | 0 (same as dayofweek) |
%{dayofweekiso} | 7 |
%{dowiso} | 7 (same as dayofweekiso) |
%{dayofyear} | 32 |
%{era} | A.D. |
%{gsd} | 735630 (numbers of days from 1 January 1 CE; the first is day 1) |
%{juliandate} | 2457055.1046875 (Julian day) |
%{jd} | 2457055.1046875 (same as juliandate) |
%{isleapyear} | false |
%{monthdays} | 28 |
%{monthabbr} | Feb |
%{monthname} | February |
Some shortcuts are available. Given date = Date('1 Feb 2015 14:30')
, the following results would occur.
Code | Description | Example result | Equivalent format |
---|---|---|---|
date:text('%c') | date and time | 2:30 pm 1 February 2015 | %-I:%M %p %-d %B %-Y %{era} |
date:text('%x') | date | 1 February 2015 | %-d %B %-Y %{era} |
date:text('%X') | time | 2:30 pm | %-I:%M %p |
Julian date
The following has an example of converting a Julian date to a date, then obtaining information about the date. <syntaxhighlight lang="lua"> -- Code -- Result Date = require('Module:Date')._Date date = Date('juliandate', 320) number = date.gsd -- -1721105 number = date.jd -- 320 text = date.dayname -- Saturday text = date:text() -- 9 October 4713 BC text = date:text('%Y-%m-%d') -- 4713-10-09 text = date:text('%{era} %Y-%m-%d') -- BC 4713-10-09 text = date:text('%Y-%m-%d %{era}') -- 4713-10-09 BC text = date:text('%Y-%m-%d %{era}', 'era=B.C.E.') -- 4713-10-09 B.C.E. text = date:text('%Y-%m-%d', 'era=BCNEGATIVE') -- -4712-10-09 text = date:text('%Y-%m-%d', 'era=BCMINUS') -- −4712-10-09 (uses Unicode MINUS SIGN U+2212) text = Date('juliandate',320):text('%{gsd} %{jd}') -- -1721105 320 text = Date('Oct 9, 4713 B.C.E.'):text('%{gsd} %{jd}') -- -1721105 320 text = Date(-4712,10,9):text('%{gsd} %{jd}') -- -1721105 320 </syntaxhighlight>
Date differences
The difference between two dates can be determined with date1 - date2
. The result is valid if both dates use the Gregorian calendar or if both dates use the Julian calendar, otherwise the result is nil. An age and duration can be calculated from a date difference.
For example: <syntaxhighlight lang="lua"> -- Code -- Result Date = require('Module:Date')._Date date1 = Date('21 Mar 2015') date2 = Date('4 Dec 1999') diff = date1 - date2 d = diff.age_days -- 5586 y, m, d = diff.years, diff.months, diff.days -- 15, 3, 17 (15 years + 3 months + 17 days) y, m, d = diff:age('ymd') -- 15, 3, 17 y, m, w, d = diff:age('ymwd') -- 15, 3, 2, 3 (15 years + 3 months + 2 weeks + 3 days) y, m, w, d = diff:duration('ymwd') -- 15, 3, 2, 4 d = diff:duration('d') -- 5587 (a duration includes the final day) </syntaxhighlight>
A date difference holds the original dates except they are swapped so diff.date1 >= diff.date2
(diff.date1
is the more recent date). This is shown in the following.
<syntaxhighlight lang="lua">
date1 = Date('21 Mar 2015')
date2 = Date('4 Dec 1999')
diff = date1 - date2
neg = diff.isnegative -- false
text = diff.date1:text() -- 21 March 2015
text = diff.date2:text() -- 4 December 1999
diff = date2 - date1
neg = diff.isnegative -- true (dates have been swapped)
text = diff.date1:text() -- 21 March 2015
text = diff.date2:text() -- 4 December 1999
</syntaxhighlight>
A date difference also holds a time difference: <syntaxhighlight lang="lua"> date1 = Date('8 Mar 2016 0:30:45') date2 = Date('19 Jan 2014 22:55') diff = date1 - date2 y, m, d = diff.years, diff.months, diff.days -- 2, 1, 17 H, M, S = diff.hours, diff.minutes, diff.seconds -- 1, 35, 45 </syntaxhighlight>
A date difference can be added to a date, or subtracted from a date. <syntaxhighlight lang="lua"> date1 = Date('8 Mar 2016 0:30:45') date2 = Date('19 Jan 2014 22:55') diff = date1 - date2 date3 = date2 + diff date4 = date1 - diff text = date3:text('ymd hms') -- 2016-03-08 00:30:45 text = date4:text('ymd hms') -- 2014-01-19 22:55:00 equal = (date1 == date3) -- true equal = (date2 == date4) -- true </syntaxhighlight>
The age and duration methods of a date difference accept a code that identifies the components that should be returned. An extra day is included for the duration method because it includes the final day.
Code | Returned values |
---|---|
'ymwd' |
years, months, weeks, days |
'ymd' |
years, months, days |
'ym' |
years, months |
'y' |
years |
'm' |
months |
'wd' |
weeks, days |
'w' |
weeks |
'd' |
days |
local fun = {} local Outils = require 'Module:Outils' -- chargement de la base de données répertoriant certaines pages existant ou n'existant pas pour éviter les "ifexist". local dataLiens local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' ) if success then dataLiens = resultat else -- protection au cas où le sous-module serait mal modifié dataLiens = { [''] = { mois = { aucun = 1000, tous = { 1773, 2014 } }, } } end -- nettoie un paramètre non nommé (vire les espaces au début et à la fin) -- retourne nil si le texte est vide ou n'est pas du texte. Attention c'est important pour les fonctions qui l'utilisent. local trim = Outils.trim -- Fonction destinée à mettre la première lettre du mois en majuscule : -- utilisation de string car aucun mois ne commence par une lettre non ascii en français ou anglais. local function ucfirst( str ) return str:sub( 1, 1 ):upper() .. str:sub( 2 ) end local modelePremier = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>' -- liste des mois, écriture exacte et alias, en minuscule local listeMois = { { num = 1, nJour = 31, abrev = 'janv.', nom = 'janvier', alias = { 'jan.', 'janv.', 'jan', 'janv', 'january' } }, { num = 2, nJour = 29, abrev = 'fév.', nom = 'février', alias = { 'fevrier', 'fev.', 'fev', 'fév.', 'fév', 'févr', 'févr.', 'february', 'feb', 'feb.' } }, { num = 3, nJour = 31, abrev = 'mars', nom = 'mars', alias = { 'mar.', 'mar', 'march' } }, { num = 4, nJour = 30, abrev = 'avr.', nom = 'avril', alias = { 'avr.', 'avr', 'apr', 'april'} }, { num = 5, nJour = 31, abrev = 'mai', nom = 'mai', alias = { 'may' } }, { num = 6, nJour = 30, abrev = 'juin', nom = 'juin', alias = { 'jun', 'june' } }, { num = 7, nJour = 31, abrev = 'juill.', nom = 'juillet', alias = { 'juil.', 'juil', 'juill.', 'juill', 'jul', 'july' } }, { num = 8, nJour = 31, abrev = 'août', nom = 'août', alias = { 'aoû', 'aug', 'august' } }, { num = 9, nJour = 30, abrev = 'sept.', nom = 'septembre', alias = { 'sept.', 'sept', 'sep.', 'sep', 'september' } }, { num = 10, nJour = 31, abrev = 'oct.', nom = 'octobre', alias = { 'oct.', 'oct', 'october' } }, { num = 11, nJour = 30, abrev = 'nov.', nom = 'novembre', alias = { 'nov.', 'nov', 'november' } }, { num = 12, nJour = 31, abrev = 'déc.', nom = 'décembre', alias = { 'decembre', 'déc.', 'dec.', 'dec', 'déc', 'december' } }, aout = { num = 8, nJour = 31, abrev = 'aout', nom = 'aout', alias = { 'aou' } }, } -- ajoute les noms, abréviations et alias en tant que clés de listeMois for i = 1, 12 do local mois = listeMois[i] listeMois[tostring( i )] = mois if i < 10 then listeMois['0' .. i] = mois end listeMois[mois.nom] = mois listeMois[mois.abrev] = mois for j = 1, #mois.alias do listeMois[mois.alias[j]] = mois end end for i = 1, #listeMois.aout.alias do listeMois[listeMois.aout.alias[i]] = listeMois.aout end local liste_saisons = { { 'printemps', 'spring', }, { 'été', 'summer', }, { 'automne', 'autumn', }, { 'hiver', 'winter', }, } -- à partir d'un nom de saison (en français ou en anglais), -- retourne son nom canonique (exemple : "été") -- si non reconnu, retourne nil function fun.determinationSaison( saison ) local s = trim( saison ) if s then s = mw.ustring.lower( s ) for i = 1, 4 do for j = 1, #liste_saisons[i] do if s == liste_saisons[i][j] then return liste_saisons[i][1] end end end end end --- -- à partir d'un nom de mois (en français ou en anglais), de son numéro ou d'une abréviation, -- retourne son nom canonique (exemple : "juin") et son numéro (exemple : 6) -- si non reconnu, retourne nil, nil function fun.determinationMois( mois ) local result local num = tonumber( mois ) if num then result = listeMois[num] else local str = trim( mois ) if str then result = listeMois[str] if not result then result = listeMois[mw.ustring.lower( str )] end end end if result then return result.nom, result.num else return nil, nil end end -- fonction interne à modeleDate, pour déterminer si on peut se passer de faire un ifexist local function existDate( dataQualificatif, annee, mois ) local data if mois then data = dataQualificatif.mois else data = dataQualificatif.annee end if type( data ) ~= 'table' then -- si data n'existe pas c'est que l'on considère qu'il n'y a pas de lien. return end -- le qualificatif est remplacé par celui de la base de données, ce qui permet des alias. local lien = annee if dataQualificatif.qualificatif ~= '' then lien = lien .. ' ' .. dataQualificatif.qualificatif end local seul = annee if mois then lien = mois .. ' ' .. lien seul = ucfirst( mois ) .. ' ' .. annee end local aucun = tonumber( data.aucun ) if aucun and annee <= aucun then -- si l'année est dans la partie 'aucun' on teste s'il y a malgré tout un lien isolé if type( data.seul ) == 'table' then for i, v in ipairs( data.seul ) do if seul == v or seul == tonumber( v ) then return lien end end end -- partie aucun et pas de lien => nil return nil elseif type( data.tous ) == 'table' then local tous1, tous2 = tonumber( data.tous[1] ), tonumber( data.tous[2] ) if tous1 and tous2 and annee >= tous1 and annee <= tous2 then -- l'année est dans la partie 'tous' donc on retourne le lien return lien end end -- l'année n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe. local cibleLien = mw.title.new( lien ) if cibleLien and cibleLien.exists then return lien end end --- -- Supprime le jour de la semaine, et "le" avant une date function fun.nettoyageJour( jour ) if type( jour ) == 'string' then local nomJour = { '[Ll]undi', '[Mm]ardi', '[Mm]ercredi', '[Jj]eudi', '[Vv]endredi', '[Ss]amedi', '[Dd]imanche', '^ *[Ll]e' } local premier = { '<abbr class="abbr ?" title="[Pp]remier" ?>1<sup>er</sup></abbr>', '1<sup>er</sup>', '1er' } for i = 1, #nomJour do jour = jour:gsub( nomJour[i], '' ) end for i = 1, #premier do jour = jour:gsub( premier[i], '1' ) end jour = trim( jour ) end return jour end --- -- Sépare une chaine date en une table contenant les champs jour, mois et annee. -- la date doit contenir le mois. function fun.separationJourMoisAnnee( date ) date = trim( date ) if date then local function erreur( periode, valeur ) return false, '<span class="error">' .. periode .. ' invalide (' .. valeur .. ')</span>' end local dateAvantCleanup = date local jour, mois, annee, masquerMois, masquerAnnee, separateur -- variable pour construire les regex local j = '([0-3]?%d)' -- jour local m = '([01]?%d)' -- mois numérique local mmm = '([^%s%p%d]+[.]?)' -- mois en toute lettre local mmm2 = '([^%s%p%d]+[.]?[-/][^%s%p%d]+[.]?)' -- mois-mois en toute lettre local aj = '(%-?%d+)' -- année ou jour local s = '[ ./-]+' -- séparateur simple local sep = '([ ./-]+)' -- séparateur avec capture, pour le détecter deux fois local moins = '(%-?)' -- signe moins pour signifier qu'il ne faut pas afficher cette donnée date = fun.nettoyageJour( date ) if date == nil then return erreur( 'Date', dateAvantCleanup ) end -- suppression catégorie, liens, balises date = mw.ustring.gsub( date, '%[%[[Cc]at[ée]gor[yi]e?:.-%]%]', '' ) date = date :gsub( '%b<>', '' ) :gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end ) -- suppression des espaces insécables -- nbsp :gsub( '\194\160', ' ' ) :gsub( ' ', ' ' ) :gsub( ' ', ' ' ) -- narrow nbsp :gsub( '\226\128\175', ' ' ) :gsub( ' ', ' ' ) -- thin space :gsub( '\226\128\137', ' ' ) :gsub( ' ', ' ' ) :gsub( ' ', ' ' ) -- simple space :gsub( ' ', ' ' ) -- plusieurs espaces :gsub( ' +', ' ' ) -- réduction av. J-C pour simplifier un peu les regex : :gsub( '(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?', '-%1' ) -- suppression de l'heure dans les dates ISO :gsub( '^+?([%d-]*%d%d%-%d%d)T%d%d[%d:,.+-]*Z?$' , '%1') -- test année seule if date:match( '^'..aj..'$' ) then annee = date:match( '^'..aj..'$' ) elseif date:match( '^'..aj..s..aj..moins..'$' ) then -- jj/mm, mm/aaaa ou aaaa/mm local a, separateur, b, sb = date:match( '^'..aj..sep..aj..moins..'$' ) a, b = tonumber( a ), tonumber( b ) if separateur:match( '^.+%-$' ) then -- probablement mm/-aaaa, année av.JC b = 0 - b end if a > 12 and ( b < 1 or b > 31 ) or b > 12 and ( a < 1 or a > 31 ) then return erreur( 'Date', dateAvantCleanup ) elseif b < 1 or b > 31 then mois, annee, masquerAnnee = a, b, sb elseif a < 1 or a > 31 then annee, mois = a, b elseif b > 12 then return erreur( 'Mois', b ) else jour, mois, masquerMois = a, b, sb end elseif date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' ) then -- jj/mm/aaaa ou aaaa/mm/jj jour, separateur, mois, masquerMois, annee, masquerAnnee = date:match( '^'..aj..sep..m..moins..'%2'..aj..moins..'$' ) if separateur == '-' and masquerMois == '-' and masquerAnnee == '' and tonumber( annee ) > 0 then -- date au format jj-mm--aaaa type 17-06--44 pour 17 juin 44 av. JC masquerMois = nil annee = 0 - annee end elseif date:match( '^'..j..sep..mmm..moins..'%2'..aj..moins..'$' ) then -- jj mmm aaaa jour, separateur, mois, masquerMois, annee, masquerAnnee = date:match( '^'..j..sep..mmm..moins..'%2'..aj..moins..'$' ) elseif date:match( '^'..mmm..s..aj..moins..'$' ) then -- mmm aaaa mois, separateur, annee, masquerAnnee = date:match( '^'..mmm..sep..aj..moins..'$' ) if separateur:match( '^.+%-$' ) then annee = '-' .. annee end elseif date:match( '^'..mmm2..s..aj..moins..'$' ) then -- mmm-mmm aaaa mois, separateur, annee, masquerAnnee = date:match( '^'..mmm2..sep..aj..moins..'$' ) if separateur:match( '^.+%-$' ) then annee = '-' .. annee end elseif date:match( '^'..j..s..mmm..moins..'$' ) then -- jj mmm jour, mois, masquerMois = date:match( '^'..j..s..mmm..moins..'$' ) elseif date:match( '^'..mmm..s..j..', ?'..aj..'$') then -- mmm jj, aaaa (format anglo-saxon) mois, jour, annee = date:match( '^'..mmm..s..j..', ?'..aj..'$') elseif date:match( '^'..mmm..'$' ) then mois = date else return erreur( 'Date', dateAvantCleanup ) end local jn, an = tonumber( jour ), tonumber( annee ) if jn and an and ( jn > 31 or jn < 0 or #jour >= 3 ) and an <= 31 then -- cas notamment des date ISO 2015-06-17, -0044-06-17 et -0002-06-17 -- inversion du jour et de l'année local temp = annee annee = jour jour = temp end return fun.validationJourMoisAnnee{ jour, mois, annee, masquerAnnee = trim( masquerAnnee ) and true or nil, masquerMois = ( trim( masquerAnnee ) or not annee ) and trim( masquerMois ) and true or nil, -- or nil sert juste à éviter de trainer une valeur false dans tous les tests unitaires. } else return true, {} end end --- -- validationJourMoisAnnee vérifie que les paramètres correspondent à une date valide. -- la date peut être dans les paramètres 1 à 3, ou dans des paramètres jour, mois et annee. -- La fonction retourne true suivi d'une table avec la date en paramètres nommés (sans accent sur année) -- ou false suivi d'un message d'erreur. function fun.validationJourMoisAnnee( frame ) local args = Outils.extractArgs( frame ) local jour, mois, numMois, annee local bjour = args[1] or args['jour'] or '' local bmois = tostring( args[2] or args['mois'] or '' ) local bannee = args[3] or args['annee'] or args['année'] or '' local function erreur( periode, valeur ) return false, '<span class="error">' .. periode .. ' invalide (' .. valeur .. ')</span>' end -- on traite l'année if Outils.notEmpty( bannee ) then annee = tonumber( bannee ) if annee == nil and type( bannee ) == 'string' then -- test si l'année contient av. J.-C. annee = bannee:upper():match( '^(%d+) ?[Aa][Vv]%.? ?[Jj][ .-]*[Cc]%.?' ) annee = tonumber( annee ) if annee then annee = 0 - annee else return erreur( 'Année', bannee ) end elseif annee == 0 then return erreur( 'Année', 0 ) end else annee = nil end -- on traite le mois if Outils.notEmpty( bmois ) then mois, numMois = fun.determinationMois( bmois ) if mois == nil then mois = fun.determinationSaison( bmois ) if mois == nil then local mois1, sep, mois2 = bmois:match( '^([^%s%p%d]+[.]?)([-/])([^%s%p%d]+[.]?)$' ) if mois1 then mois1 = fun.determinationMois( mois1 ) mois2 = fun.determinationMois( mois2 ) if mois1 == nil or mois2 == nil then return erreur( 'Mois', bmois ) end mois = mois1 .. sep .. mois2 else return erreur( 'Mois', bmois ) end end end -- on traite le jour si présent if Outils.notEmpty( bjour ) then if not numMois then erreur( 'Date', 'jour avec saison ou plusieurs mois' ) end jour = tonumber( bjour ) if jour == nil then jour = tonumber( fun.nettoyageJour( bjour ) ) end if jour == nil then return erreur( 'Jour', bjour ) end -- on valide que le jour est correct if jour < 1 or jour > 31 then return erreur( 'Jour', bjour ) elseif jour > listeMois[numMois].nJour then return erreur( 'Jour', bjour .. ' ' .. mois ) elseif jour == 29 and numMois == 2 and annee and ( math.fmod( annee, 4 ) ~= 0 ) then -- l'année bisextile sur les siècles est toujours acceptée pour être compatible avec les dates juliennes. return erreur( 'Jour', '29 février ' .. annee ) end else -- S'il n'y a pas de jour on regarde si la première lettre du mois est en majuscule if bmois:match( '^%u' ) then -- oui, on passe la première lettre en majuscule mois = ucfirst( mois ) end -- s'il n'y a pas d'année non plus on retourne le mois simple end else -- on teste le jour si présent if Outils.notEmpty( bjour ) then if annee then return erreur( 'Mois', 'absent' ) else bjour = fun.nettoyageJour( bjour ) jour = tonumber( bjour ) if jour then if jour > 31 or jour < 1 then annee = jour jour = nil else return erreur( 'Date', 'jour seul : ' .. bjour ) end else return erreur( 'Jour', bjour ) end end end end -- vérification de l'absence d'un décalage if annee and annee < 13 and annee > 0 and not jour and ( tonumber( bmois ) or ( not mois and tonumber( args[4] ) ) ) then return false, '<span class="error">année improbable (' .. annee .. ')</span>' end local resultat = { jour = jour, mois = mois, numMois = numMois, annee = annee, masquerAnnee = args.masquerAnnee, masquerMois = args.masquerMois, } return true, resultat end --- -- émule le modèle {{m|Date}}. -- Paramètres : -- 1 : jour (numéro ou "1er") ou la date complète -- 2 : mois (en toutes lettres) ou spécialité de l'année -- 3 : année (nombre) -- 4 : spécialité de l'année -- julien : date dans le calendrier julien -- compact : affiche le mois sous forme d'abréviation -- avJC : non pour désactiver l'affichage de « av. J.-C. » pour les dates négatives -- âge : ajoute la durée depuis cette date -- agePrefix : préfixe pour l'age, 'à ' par défaut pour les décès -- nolinks : ne met pas de lien sur la date -- afficherErreurs : en cas d'erreur, si défini à "non" ne retourne pas un message d'erreur, mais le 1er argument inchangé -- categoriserErreurs : en cas d'erreur, si défini à "non" ne catégorise pas ; peut aussi être défini avec une catégorie à utiliser à la place de celle par défaut -- naissance : ajoute la class "bday" -- mort : ajoute la class "dday" function fun.modeleDate( frame ) local Yesno = require 'Module:Yesno' local args = Outils.extractArgs( frame ) local resultat local dateNaissanceMort -- analyse des paramètres non nommés (ou paramètres de la date jour, mois, annee) local test, params local arg1, arg2, arg3 = fun.nettoyageJour( args[1] ), trim( args[2] ), trim( args[3] ) if type( arg1 ) == 'string' and arg3 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or arg2 == nil or dataLiens[arg2] or mw.ustring.match( arg2, '%a %a' ) ) then -- la date est dans le premier paramètre test, params = fun.separationJourMoisAnnee( arg1 ) if test then dateNaissanceMort = trim( arg2 ) params.qualificatif = trim( arg2 ) end elseif type( arg1 ) == 'string' and type( arg2 ) == 'string' and arg3 ~= nil and arg4 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or dataLiens[arg3] or mw.ustring.match( arg3, '%a %a' ) ) then -- la date est dans le premier paramètre test, params = fun.separationJourMoisAnnee( arg1 ) if test then dateNaissanceMort = trim( arg2 ) params.qualificatif = trim( arg3 ) end else local function masquerParam( p ) -- sépare le signe moins final éventuel signifiant que le paramètre ne doit pas être affiché. if type( p ) ~= 'string' then return p, nil end local value, mask = p:match( '^%s*(.-)(%-?)%s*$' ) return value, ( mask == '-' or nil ) end local cleanArgs = { arg1 or args.jour } cleanArgs[2], cleanArgs.masquerMois = masquerParam( args[2] or args.mois ) cleanArgs[3], cleanArgs.masquerAnnee = masquerParam( args[3] or args.annee or args['année'] ) test, params = fun.validationJourMoisAnnee( cleanArgs ) if test then params.qualificatif = trim( args[4] ) end end -- analyse des paramètres nommés if test then params.agePrefix = args.agePrefix if args.qualificatif and args.qualificatif ~= '' then params.qualificatif = args.qualificatif end -- julien peut avoir trois valeurs : inactif, format standard (true), format court params.julien = Yesno( args.julien, 'court', false ) params.avJC = Yesno( args.avJC ) if args['républicain'] and args['républicain'] ~= '' then if args['républicain'] == 'liens' then params.republicain = 'liens' else params.republicain = Yesno( args['républicain'], false ) end else params.republicain = false end if args.dateNaissanceMort and args.dateNaissanceMort ~= '' then dateNaissanceMort = args.dateNaissanceMort elseif args['dateNaissanceÉvénement'] and args['dateNaissanceÉvénement'] ~= '' then dateNaissanceMort = args['dateNaissanceÉvénement'] end if dateNaissanceMort then local testNaissanceMort, paramsNaissanceMort = fun.separationJourMoisAnnee( dateNaissanceMort ) if testNaissanceMort then params.anneeNaissanceMort, params.moisNaissanceMort, params.numMoisNaissanceMort, params.jourNaissanceMort = paramsNaissanceMort.annee, paramsNaissanceMort.mois, paramsNaissanceMort.numMois, paramsNaissanceMort.jour end end local listeParam = { age = 'âge', ['âge'] = 'âge', naissance = 'naissance', mort = 'mort', ['événement'] = 'événement', evenement = 'evenement', ['décès'] = 'mort', apJC = 'apJC', nolinks = 'nolinks', compact = 'compact', compacte = 'compact', } for n, v in pairs( listeParam ) do params[v] = params[v] or Yesno( args[n], true, false ) or nil end -- sortie pour les tests unitaire, ou pour débugger if args.debug then return params end resultat = fun._modeleDate( params ) else local yn_afficherErreurs = Yesno( args.afficherErreurs ) if yn_afficherErreurs == nil or yn_afficherErreurs == true then resultat = params else resultat = args[1] end local currentTitle = mw.title.getCurrentTitle() if currentTitle:inNamespaces( 0, 4, 10, 14, 100 ) and not Outils.notEmpty( args.nocat ) and not currentTitle.prefixedText:match( '^Modèle:.+/Test$' ) then local categorie local yn_categoriserErreurs = Yesno( args.categoriserErreurs, 'custom', true ) if yn_categoriserErreurs == nil or yn_categoriserErreurs == true then categorie = '[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée]]' elseif yn_categoriserErreurs == false then categorie = '' else local nomCategorie = args.categoriserErreurs :gsub( '^%[%[', '' ) :gsub( '%]%]$', '' ) :gsub( '^:?[Cc]atégorie:', '' ) :gsub( '^:?[Cc]atégory:', '' ) categorie = '[[Catégorie:' .. nomCategorie .. ']]' end resultat = resultat .. categorie end end return resultat or '' end function fun._modeleDate( args ) local annee, mois, numMois, jour = args.annee, args.mois, args.numMois, args.jour local qualificatif = args.qualificatif if ( annee or mois or jour ) == nil then return end -- on traite l'âge, naissance et mort local agePrefix = args.agePrefix local age = args['âge'] and fun.age( annee, numMois, jour ) local naissance = args.naissance local mort = args.mort local evenement = args['événement'] or args.evenement if mort and args.anneeNaissanceMort then age = fun.age( args.anneeNaissanceMort, args.numMoisNaissanceMort, args.jourNaissanceMort, annee, numMois, jour ) agePrefix = agePrefix or 'à ' -- faut-il mettre \194\160 ? elseif evenement and args.anneeNaissanceMort then if naissance then age = fun.age( annee, numMois, jour, args.anneeNaissanceMort, args.numMoisNaissanceMort, args.jourNaissanceMort ) else age = fun.age(args.anneeNaissanceMort, args.numMoisNaissanceMort, args.jourNaissanceMort, annee, numMois, jour ) end end agePrefix = agePrefix or '' -- on traite le calendrier local gannee, gmois, gjour = annee, numMois, jour -- date suivant le calendrier grégorien pour <time> local jannee, jmois, jjour = annee, mois, jour -- date suivant le calendrier julien si necessaire local julienDate, julienSup, julienSep -- servira éventuellement à afficher la date selon le calendrier julien local gregAprMois, gregAprAn, gregFin -- message de calendrier grégorien lorsque la date est selon le calendrier julien if annee and jour then local amj = annee * 10000 + numMois * 100 + jour if amj < 15821014 then if annee > 0 then gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour ) else -- calendrier grégorien proleptique avec année 0. gannee, gmois, gjour = fun.julianToGregorian( annee + 1, numMois, jour ) end args.julien = false elseif args.julien then gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour ) annee, mois, jour = gannee, listeMois[gmois].nom, gjour if jjour == 1 then jjour = modelePremier end if args.compact then jmois = listeMois[jmois].abrev end if args.julien == 'court' then julienDate = jjour .. ' ' .. jmois .. ' ' julienSup = '<sup>[[calendrier julien|jul.]]</sup>' if jannee == annee then gregAprMois = '<sup>[[calendrier grégorien|grég.]]</sup>' else julienDate = julienDate .. jannee .. ' ' gregAprAn = '<sup>[[calendrier grégorien|grég.]]</sup>' end julienSep = ' / ' else julienDate = jjour .. ' ' .. jmois .. ' ' .. jannee julienSep = ' (' gregFin = ' [[Passage du calendrier julien au calendrier grégorien|dans le calendrier grégorien]])' end elseif args.republicain then local DateRep = require 'Module:Date républicaine' local RepSansLiens if args.republicain == 'liens' then RepSansLiens = false else RepSansLiens = true end dateRepublicaine = DateRep._date_republicaine( RepSansLiens, { fun.formatRepCal( fun.do_toRepCal{gannee, gmois, gjour} ) } ) end else if annee and annee < 0 then gannee = gannee + 1 end args.julien = false args.republicain = false end -- on génère le résultat -- Déclarations des variables local wikiListe = {} -- reçoit le texte affiché pour chaque paramètre local iso = {} -- reçoit le format date ISO de ce paramètre local texteMois = mois -- texte du mois qui sera affiché (éventuellement l'abréviation) if args.compact then if not numMois then -- mois est autre chose qu'un simple mois : saison, mois-mois... auquel cas, pas d'abréviation (provoquait erreur Lua) -- (les abréviations pour le cas "mois[-/]mois" seraient théoriquement possibles, mais ça reste à implémenter) else if args.nolinks then texteMois = '<abbr class=abbr title="' .. mois .. '">' .. listeMois[mois].abrev .. '</abbr>' else texteMois = listeMois[mois].abrev end end end mois = mois and mois:gsub( 'aout', 'août' ) local dataQualificatif, dataCat if not args.nolinks then dataQualificatif = dataLiens[qualificatif or ''] if type( dataQualificatif ) ~= 'table' then -- si le qualificatif n'est pas dans la base de données, on crée une table minimum, -- qui imposera un test sur l'année, mais considère qu'il n'y a pas de lien sur le jour ou le mois dataQualificatif = { qualificatif = qualificatif, annee = { } } end dataCat = dataLiens[dataQualificatif.cat] if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then dataCat = { qualificatif = '' } end end local function wikiLien( lien, texte ) if lien == texte then return '[[' .. texte .. ']]' else return '[[' .. lien .. '|' .. texte .. ']]' end end -- le jour si présent local qualifJour = '' if jour then if args.nolinks then if jour == 1 then jour = modelePremier end table.insert( wikiListe, jour ) else qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif or dataCat.jour and dataCat.qualificatif or '' local texteJour, lien if jour == 1 then texteJour = '1<sup>er</sup>' lien = '1er ' .. mois else texteJour = jour lien = jour .. ' ' .. mois end if qualifJour ~= '' then lien = lien .. ' ' .. qualifJour end -- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour. table.insert( wikiListe, wikiLien( lien, texteJour ) ) table.insert( wikiListe, wikiLien( lien, texteJour .. ' '.. texteMois ) ) end table.insert( iso, 1, string.sub( '0' .. gjour, -2 ) ) end -- le mois if mois then if #wikiListe == 0 and annee == nil then return texteMois end if args.nolinks then if not args.masquerMois then table.insert( wikiListe, texteMois ) end else local lien if annee then if not numMois then -- mois est autre chose qu'un simple mois : saison, mois-mois... auquel cas, pas de lien else lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois ) if lien == nil and qualificatif and qualifJour == '' then -- nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif. lien = existDate( dataLiens[''], annee, mois ) end end end if lien or args.masquerMois then -- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois]]' table.remove( wikiListe ) if not args.masquerMois then table.insert( wikiListe, wikiLien( lien, texteMois ) ) end elseif #wikiListe > 0 then -- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois' table.remove( wikiListe, #wikiListe - 1 ) elseif args.masquerAnnee then -- s'il n'y a pas de jour et que l'année n'est pas affichée, on insère le mois seul. table.insert( wikiListe, texteMois ) end end if gmois then table.insert( iso, 1, string.sub( '0' .. gmois, -2 ) ) end table.insert( wikiListe, gregAprMois ) end -- l'année if annee and not (args.julien == true and args.nolinks and jannee == annee ) then if not args.masquerAnnee then local texteAnnee = annee local lien if annee < 0 then local annneeAvJc = 0 - annee lien = lien or ( annneeAvJc .. ' av. J.-C.' ) if args.avJC == false then texteAnnee = annneeAvJc else texteAnnee = annneeAvJc .. ' <abbr class="abbr" title="' .. annneeAvJc .. ' avant Jésus-Christ">av. J.-C.</abbr>' end elseif args.apJC then texteAnnee = texteAnnee .. ' <abbr class="abbr" title="' .. texteAnnee .. ' après Jésus-Christ">apr. J.-C.</abbr>' end if args.nolinks then -- seulement si on doit l'afficher table.insert( wikiListe, texteAnnee ) else lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee ) or lien or annee if mois and #wikiListe == 0 then -- si le mois n'a pas de lien et n'est pas affiché avec le jour, il est affiché avec l'année. texteAnnee = texteMois .. ' ' .. texteAnnee end table.insert( wikiListe, wikiLien( lien, texteAnnee ) ) end end end if annee then if gannee > 999 then table.insert( iso, 1, gannee ) elseif gannee > -1 then table.insert( iso, 1, string.sub( '000' .. gannee , -4 ) ) elseif gannee > -999 then -- calendrier grégorien proleptique avec année 0. table.insert( iso, 1, 'U-' .. string.sub( '000' .. ( 0 - gannee ), -4 ) ) else table.insert( iso, 1, 'U' .. gannee ) end end table.insert( wikiListe, gregAprAn ) -- l'age if type( age ) == 'number' and age >= 0 and ( not naissance or age < 120 ) then if age == 0 then age = '(' .. agePrefix .. 'moins d’un\194\160an)' elseif age == 1 then age = '(' .. agePrefix .. '1\194\160an)' else age = '('.. agePrefix .. age .. '\194\160ans)' end else age = false end -- compilation du résultat local wikiTexte = table.concat( wikiListe, ' ' ) local isoTexte = table.concat( iso, '-' ) -- On ajoute un peu de sémantique. local wikiHtml = mw.html.create( '' ) if julienDate then wikiHtml:tag( 'span') :addClass( 'nowrap' ) :attr( 'data-sort-value', isoTexte ) :wikitext( julienDate ) :node( julienSup ) :done() :wikitext( julienSep ) end local dateHtml = wikiHtml:tag( 'time' ) :wikitext( wikiTexte ) if wikiTexte:match( ' ' ) then dateHtml:addClass( 'nowrap' ) end if isoTexte ~= wikiTexte then dateHtml:attr( 'datetime', isoTexte ) :attr( 'data-sort-value', isoTexte ) end if not args.nolinks then dateHtml:addClass( 'date-lien' ) end if naissance then dateHtml:addClass( 'bday' ) elseif mort then dateHtml:addClass( 'dday' ) end wikiHtml:wikitext( gregFin ) if args.republicain then wikiHtml:wikitext( ' (', dateRepublicaine, ')' ) end if age then wikiHtml:wikitext( ' ' ) :tag( 'span' ) :addClass( 'noprint') :wikitext( age ) :done() end return tostring( wikiHtml ) end --- -- fonction destinée aux infobox, notamment pour afficher les dates de naissance et de mort -- les liens présent dans les dates fournies sont automatiquement supprimés pour gérer les cas où -- le paramètre contient déjà un modèle date. -- Paramètres : -- 1 : type de date à afficher (naissance / n, mort / m, ou date / d) -- 1 : Date ou date de naissance -- 2 : Date de mort si type n ou m -- qualificatif = suffixe des page de date à lier (exemple : en musique) -- nolinks : n'affiche pas de lien -- préfixe : préfixe à afficher s'il y a un jour (par défaut '') -- préfixe sans jour : préfixe à afficher s'il n'y a pas de jour (par défaut : '') function fun.dateInfobox( frame ) local args = frame.args if type( args ) ~= 'table' or not ( args[1] and args[2] ) then return end -- analyseDate sépare la date du contenu qui suit, supprime les liens, et retourne si possible une table avec jour mois année local function analyseDate( d ) if trim( d ) then local analyse = d:match( ' ou ') or d:match( 'entre ' ) or d:match( 'vers ' ) or d:match( 'après ' ) or d:match( 'avant ' ) if analyse then return d end analyse = d:match( 'datetime="([%d-]+)"' ) or d -- sépare la date (avec ses liens) d'une référence ou contenu commençant par un espace) local debut, fin = analyse:match( '(.-%d%d%d%]*%-?)([\127 ].+)' ) if not debut then -- sépare la date du contenu commençant par <br> debut, fin = analyse:match( '(.-%d%d%d%]*%-?)(<br ?/?>.+)' ) end analyse = debut or analyse -- supprime les liens analyse = analyse:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end ) local t, r = fun.separationJourMoisAnnee( analyse ) if t then return r, fin else return d, fin end end end -- prefix ajoute un préfixe en fonction de la présence ou non du jour si le paramètre "préfixe sans jour" est défini local function prefix( dateString ) if dateString then local datetime = dateString:match( 'datetime="([U%d%-]+)"' ) if datetime and datetime:match('%-%d%d%-%d%d') and trim( args['préfixe'] ) then return args['préfixe'] .. ' ' .. dateString end if trim( args['préfixe sans jour'] ) then return args['préfixe sans jour'] .. ' ' .. dateString end end return dateString end local naissance = args[1]:match( '^n' ) == 'n' local mort = args[1]:match( '^m' ) or args[1]:match( 'décès' ) local evenement = args[1]:match( '^é' ) local affichageDate, qualificatif = args[2], args[4] local affichageDateTab, resultatDate, complementDate local dateNaissance, dateMort if mort or evenement then affichageDate = args[3] end if not trim( affichageDate ) then return end if affichageDate:match( '</time>' ) then -- S'il y a des liens il y a probablement déjà un modèle date, évitons de l'exécuter une 2e fois if ( naissance or mort or evenement ) and ( affichageDate:match( 'wikidata%-linkback' )) then dateNaissance = analyseDate( args[2] ) dateMort = analyseDate( args[3] ) resultatDate = affichageDate else return prefix( affichageDate ) end else affichageDateTab, complementDate = analyseDate( affichageDate ) if type( affichageDateTab ) ~= 'table' then return affichageDateTab else if naissance then dateNaissance = affichageDateTab dateMort = analyseDate( args[3] ) elseif mort then dateNaissance = analyseDate( args[2] ) dateMort = affichageDateTab else qualificatif = args[3] end affichageDateTab.naissance = naissance affichageDateTab.mort = mort affichageDateTab.evenement = evenement 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 or evenement) and dateNaissance and dateMort and type( dateNaissance ) == 'table' and type( dateMort ) == 'table' then calculAge = fun.age( dateNaissance.annee, dateNaissance.numMois, dateNaissance.jour, dateMort.annee, dateMort.numMois, dateMort.jour ) prefixAge = ' (à ' suffixAge = ')' end if tonumber( calculAge ) then if calculAge > 1 then age = prefixAge .. calculAge .. '\194\160ans' .. suffixAge elseif calculAge == 1 then age = prefixAge .. 'un\194\160an' .. suffixAge elseif calculAge == 0 then age = prefixAge .. 'moins d’un\194\160an' .. suffixAge end if complementDate and complementDate:match( 'ans?%)' ) then complementDate = '' end end return prefix( resultatDate ) .. ( complementDate or '' ) .. age end --- -- la fonction dateISO renvoie un date au format aaaa-mm-jj (sans liens) -- l'année peut être sous la forme 2013 ou [[2013 en litérature|2013]] -- le mois peut être en lettres ou en chiffres -- le jour peut être sous la forme '05', '{{1er}}' ou 'vendredi 13' function fun.dateISO( frame ) local args = Outils.extractArgs( frame ) local annee = Outils.notEmpty( args['année'], args.annee, args.year, args.date ) -- extraction de l'année if type( annee ) == 'string' then annee = ( tonumber( annee ) -- match '2013' or string.match ( annee, '%D(%d%d%d%d)%D' ) -- match '[[2013 en musique|2013]]' or string.match ( annee, '%D(%d%d%d%d)$' ) -- match '17 septembre 2013' or string.match ( annee, '^(%d%d%d%d)%D' ) -- match '2013-09-17' ) end annee = tonumber( annee ) -- le format de date iso est défini suivant le calendrier grégorien. -- Avant l'année 1583 la date est calendrier est probablement du calendrier julien, -- donc autant s'abstenir. if annee and annee > 1582 then local mois = Outils.notEmpty( args.mois, args.month ) -- num mois trouve le numéro du mois, qu'il soit numérique ou texte, complet ou abrégé. local nomMois, numMois = fun.determinationMois( mois ) if numMois then mois = '-' .. string.sub( '0' .. numMois, -2 ) local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] ) if type( jour ) == 'string' then jour = tonumber( jour ) or tonumber( string.match ( jour, '%d+') ) end jour = tonumber( jour ) if jour and jour <= listeMois[numMois].nJour then jour = '-' .. string.sub( '0' .. jour, -2 ) return annee .. mois .. jour else return annee .. mois end else return tostring( annee ) end end end --- -- Rang du jour dans l'année -- Usage : do_dayRank{année,mois,jour} function fun.do_dayRank(arguments) local yr = tonumber(arguments.year or arguments[1]) or 1 local mt = tonumber(arguments.month or arguments[2]) or 1 local dy = tonumber(arguments.day or arguments[3]) or 1 -- Rangs des premiers des mois local ranks = {0,31,59,90,120,151,181,212,243,273,304,334} local rank = (ranks[mt] or 0) + dy - 1 if(fun.isLeapYear(yr) and (mt >= 3)) then rank = rank+1 end return rank end -- Nombre de jours entre deux années (du 1er janvier au 1er janvier) -- Suit le calendrier grégorien function fun.do_daysBetween(arguments) local yr1 = tonumber(arguments[1]) or 0 local yr2 = tonumber(arguments[2]) or 0 return fun.daysSinceOrigin(yr2) - fun.daysSinceOrigin(yr1) end -- Nombre de jours depuis l'année 1 (du 1er janvier au 1er janvier) function fun.daysSinceOrigin(year) local yr = year-1 return 365*yr + math.floor(yr/4) - math.floor(yr/100) + math.floor(yr/400) end -- Test d'année bissextile (Suit le calendrier grégorien) function fun.isLeapYear(year) local yr = tonumber(year) or 1 return (yr%4 == 0) and ((yr%100 ~= 0) or (yr%400 == 0)) end -- Conversion d'un nombre en chiffres romains function fun.toRoman(number) local n = math.floor(number) local letters = {"I","V","X","L","C","D","M","",""} local pattern = {"","0","00","000","01","1","10","100","1000","02"} local result = "" if(n<=0 or n>=4000) then result = "---" else for i=1,7,2 do local p = pattern[n%10 + 1] for j=0,2 do p = string.gsub(p,tostring(j),letters[i+j]) end result = p .. result n = math.floor(n/10) end end return result end -- Conversion et affichage d'une date dans le calendrier républicain function fun.dateRepublicain(frame) local pframe = frame:getParent() local arguments = pframe.args return fun.formatRepCal(fun.do_toRepCal(arguments)) end --- -- Calcul d'une date dans le calendrier républicain -- On suppose que les années 4n+3 sont sextiles (3, 7, 11...) function fun.do_toRepCal(arguments) local yr = tonumber(arguments.year or arguments[1]) or 2000 -- rang absolu du jour demandé, le jour 0 étant le 22 septembre 1792 (1er jour de l'an I) local repDays = fun.do_dayRank(arguments) + fun.do_daysBetween{1792,yr} - fun.do_dayRank{1792,9,22} local repYear = math.floor((repDays+731)/365.25) - 1 local repDayRank = repDays - 365*(repYear-1) - math.floor(repYear/4) local repMonth, repDay = math.floor(repDayRank/30)+1, (repDayRank%30)+1 return {repYear, repMonth, repDay} end --- -- Formatage d'une date selon le calendrier républicain -- Usage : fun.formatRepCal{année,mois,jour} function fun.formatRepCal(arguments) local months = {"Vendémiaire","Brumaire","Frimaire","Nivôse","Pluviôse","Ventôse","Germinal","Floréal","Prairial","Messidor","Thermidor","Fructidor"} local extras = {"de la vertu","du génie","du travail","des récompenses","de l'opinion","de la Révolution"} local result = "" if(arguments[2] < 13) then result = result .. tostring(arguments[3]) .. "\194\160" .. months[arguments[2]] else result = result .. "jour " .. extras[arguments[3]] end result = result .. " de l'an " .. fun.toRoman(arguments[1]) return result end --- -- Voir Modèle:Âge -- retourne l'âge en fonction de la ou les dates fournies. La valeur retournée est de type 'number' -- Paramètres : -- 1, 2, 3 : année, mois jour de naissance (supposé dans le calendrier grégorien) -- 4, 5, 6 : année, mois, jour du calcul (facultatif, par défaut la date UTC courante). function fun.age( an, mn, jn, ac, mc, jc ) if ac == nil then local today = os.date( '!*t' ) ac = today.year mc = today.month jc = today.day else ac = tonumber( ac ) mc = tonumber( mc ) jc = tonumber( jc ) end local an = tonumber( an ) local mn = tonumber( mn ) local jn = tonumber( jn ) if an == nil or ac == nil or mn == nil or mc == nil then -- pas de message d'erreur qui risque de faire planter la fonction appelante -- à elle de gérer ce retour. return end local age = ac - an if mc == mn then if jc == nil or jn == nil then return end return age-tonumber( jc < jn and 1 or 0 ) else return age-tonumber( mc < mn and 1 or 0 ) end end function fun.modeleAge( frame ) local args = Outils.extractArgs( frame ) local age = fun.age ( args[1] or args['année'], args[2] or args['mois'], args[3] or args['jour'], args[4], args[5], args[6] ) if age then return age else return '<span class="error">Paramètres incorrects ou insuffisants pour calculer l\'âge précis</span>' end end --- -- calcul du jour julien à partir d'une date du calendrier grégorien function fun.julianDay( year, month, day, hour, min, sec ) local julian julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 ) - math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 100 ) + math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 400 ) + math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 ) + day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400 - 32167.5 return julian end --- -- calcul du jour julien à partir d'une date du calendrier julien function fun.julianDayJulian( year, month, day, hour, min, sec ) local julian julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 ) + math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 ) + day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400 - 32205.5 return julian end --- -- calcul d'une date dans le calendrier grégorien à partir du jour julien function fun.julianDayToGregorian( julianDay ) local base = math.floor( julianDay + 32044.5 ) -- 1 March -4800 (proleptic Gregorian date) local nCentury = math.floor( ( base * 4 + 3 ) / 146097 ) local sinceCentury = base - math.floor( nCentury * 146097 / 4 ) local nYear = math.floor( ( sinceCentury * 4 + 3 ) / 1461 ) local sinceYear = sinceCentury - math.floor( nYear * 1461 / 4 ) local nMonth = math.floor( ( sinceYear * 5 + 2 ) / 153 ) local day = sinceYear - math.floor( ( nMonth * 153 + 2 ) / 5 ) + 1 local month = nMonth - math.floor( nMonth / 10 ) * 12 + 3 local year = math.floor( sinceYear / 306 ) + nYear + 100 * nCentury - 4800 return year, month, day end --- -- calcul d'une date dans le calendrier julien à partir du jour julien -- calcul basé sur l'algorithme de la page fr.wikipedia.org/wiki/Jour_julien (1/10/2013) function fun.julianDayToJulian( julianDay ) local year = math.modf( ( julianDay * 4 - 6884469 ) / 1461 ) local r2 = julianDay - math.modf( ( 1461 * year + 6884472 ) / 4 ) local month = math.modf( ( 5 * r2 + 461 ) / 153 ) local day = r2 - math.modf( ( 153 * month - 457 ) / 5 ) + 1 if month > 12 then year = year + 1 month = month - 12 end return year, month, day end --- -- calcul d'une date dans le calendrier grégorien à partir d'une date dans le calendrier julien function fun.julianToGregorian( year, month, day ) return fun.julianDayToGregorian( fun.julianDayJulian( year, month, day ) ) end --- -- calcul d'une date dans le calendrier julien à partir d'une date dans le calendrier grégorien function fun.gregorianToJulian( year, month, day ) year = tonumber(year) if month then month = tonumber(month) else month = 6 end --prend une valeur centrale pour donner un best "guess" if day then day = tonumber(day) else day = 15 end return fun.julianDayToJulian( fun.julianDay( year, month, day ) ) end --[[ Cette fonction retourne "CET" ou "CEST" selon que dans la pseudo-timezone en cours c'est l'heure d'été ou l'heure d'hiver. Cette fonction n'a de sens a priori que pour des modèles utilisés en Europe Paramètre optionnel non nommé : "sans lien" : retourne le texte CET/CEST. sinon retourne ce même texte avec un wikilien vers les articles correspondants --]] function fun.CEST(frame) -- option : ne pas créer de wikilien local opt = trim(frame.args[1] or frame:getParent().args[1]) -- on récupère l'information dans la zone courante local t = mw.getContentLanguage():formatDate("I", nil, true) if (t == "1") then -- heure d'été if (opt == "sans lien") then return "CEST" elseif (opt == "décalage") then return "2" else return "[[Heure d'été d'Europe centrale|CEST]]" end else -- heure d'hiver (ou autre zone où ça ne s'applique pas) if (opt == "sans lien") then return "CET" elseif (opt == "décalage") then return "1" else return "[[Heure normale d'Europe centrale|CET]]" end end end return fun