« Module:Date » : différence entre les versions
w>Od1n (suppression warnings luacheck « variable 'an' [idem 'mn', 'jn'] was previously defined as an argument on line 1202 » (refs 96274029, 119175413 et 119220646)) |
m (1 version importée) |
(Aucune différence)
|
Dernière version du 5 juillet 2023 à 10:44
La documentation pour ce module peut être créée à Module:Date/doc
Erreur de script : Erreur Lua : erreur interne : l’interpréteur s’est arrêté avec le signal « -129 ».
-- luacheck: globals mw, no max line length 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 ( 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 local dateRepublicaine 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 if annee < 0 then local anneeAvJc = 0 - annee if args.avJC == false then texteAnnee = anneeAvJc else texteAnnee = anneeAvJc .. ' <abbr class="abbr" title="' .. anneeAvJc .. ' 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 local lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee ) if not lien then if annee < 0 then local anneeAvJc = 0 - annee lien = anneeAvJc .. ' av. J.-C.' else lien = annee end end 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 dateRepublicaine 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 an = tonumber( an ) mn = tonumber( mn ) 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 -- si l'intervalle traverse l'année zéro, il faut soustraire une année -- parce que cette année n'existe pas dans les calendriers chrétiens if an < 0 and ac > 0 then age = age - 1 end if mc == mn then if jc == nil or jn == nil then return end return age - ( jc < jn and 1 or 0 ) else return age - ( 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