Difference between revisions of "Module:Date"

From ChaldeanWiki
Jump to: navigation, search
(set defaults in the _Date function)
 
m (1 revision imported)
 
(2 intermediate revisions by 2 users not shown)
Line 1: Line 1:
--[[ 
+
local fun = {}
+
This module is intended for processing of date strings.
+
  
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
+
local TableBuilder = require( 'Module:TableBuilder' )
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
+
local Outils = require( 'Module:Outils' )
 +
-- chargement de la base de donnée répertoriant certaines pages existant ou n'existant pas pour éviter les "ifexist".
 +
local dataLiens
 +
local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' )
 +
if success then
 +
dataLiens = resultat
 +
else
 +
-- protection au cas ou le sous module serait mal modifié
 +
dataLiens = { [''] = { mois = { aucun = 1000, tous = { 1773, 2014 } }, } }
 +
end
  
Authors and maintainers:
+
-- nettoie un paramètre non nommé (vire les espaces au début et à la fin)
* User:Parent5446 - original version of the function mimicking template:ISOdate
+
-- retourne  nil si le texte est vide ou n'est pas du texte. Attention c'est important pour les fonction qui l'utilise.
* User:Jarekt - original version of the functions mimicking template:Date and template:ISOyear
+
local trim = Outils.trim
  
]]
+
local function ucfirst( str )
 +
return mw.ustring.upper( mw.ustring.sub( str, 1, 1 ) ) .. mw.ustring.sub( str, 2 )
 +
end
  
 
local p = {}
 
  
-- =======================================
+
-- liste des mois, écriture exacte et simplifiée, en minuscule
-- === Dependencies ======================
+
local liste_mois = {
-- =======================================
+
{ "janvier", "jan.", "janv.", "jan", "janv", "january", nJour = 31 },
local i18n    = require('Module:I18n/date') -- get localized translations of date formats
+
{ "février", "fevrier", "fev.", "fev", "fév.", "fév", "february", nJour = 29 },
local Fallback = require('Module:Fallback') -- get fallback functions
+
{ "mars", "mar.", "mar", "march", nJour = 31 },
local yesno    = require('Module:Yesno')
+
{ "avril", "avr.", "avr", "apr", "april", nJour = 30 },
 +
{ "mai", "may", nJour = 31 },
 +
{ "juin", "jun", "june", nJour = 30 },
 +
{ "juillet", "juil.", "juil", "juill.", "juill", "jul", "july", nJour = 31 },
 +
{ "août", "aout", "aou", "aug", "august", nJour = 31 },
 +
{ "septembre", "sept.", "sept", "sep.", "sep", "september", nJour = 30 },
 +
{ "octobre", "oct.", "oct", "october", nJour = 31 },
 +
{ "novembre", "nov.", "nov", "november", nJour = 30 },
 +
{ "décembre", "decembre", "déc.", "dec.", "dec", "déc", "december", nJour = 31 },
 +
}
  
--[[
+
local liste_saison = {
Date
+
{ 'printemps', 'spring', },
+
{ 'été', 'summer', },
This function is the core part of the ISOdate template.
+
{ 'automne', 'autumn', },
+
{ 'hiver', 'winter', },
Usage:
+
}
{#invoke:Date|Date|year=|month=|day=|hour=|minute=|second=|lang=en}}
+
+
Parameters:
+
    year,month,day,hour,minute,second: broken down date-time component strings
+
  lang: The language to display it in
+
  case: Language format (genitive, etc.) for some languages
+
class: CSS class for the <time> node, use "" for no metadata at all
+
  
Error Handling:
+
-- nom du mois à partir du numéro
 +
function fun.nomDuMois( num )
 +
if type( num ) ~= "number" or num < 1 or num > 12 then
 +
return nil
 +
end
 +
return liste_mois[num][1]
 +
end
  
]]
+
---
function p.Date(frame)
+
-- valide que la chaîne passée est un mois valide.
return p._Date(
+
-- retourne le nom complet ou nil si non reconnu
{
+
-- si reconnu, retourne aussi le numéro du mois [1-12]
frame.args["year"] or '',
+
function fun.valideMois( mois )
frame.args["month"] or '',
+
if type( mois ) ~= "string" then
frame.args["day"] or '',
+
return nil
frame.args["hour"] or '',
+
end
frame.args["minute"] or '',  
+
frame.args["second"]or ''
+
local m = mw.ustring.lower( mw.text.trim( mois ) )
},
+
frame.args["lang"],                  -- language
+
for i = 1, 12 do
frame.args["case"]  or '',          -- allows to specify grammatical case for the month for languages that use them
+
local j = 1
frame.args["class"] or 'dtstart',    -- allows to set the html class of the time node where the date is included. This is useful for microformats.
+
while liste_mois[i][j] ~= nil do
frame.args["trim_year"] or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
+
if liste_mois[i][j] == m then
)
+
return liste_mois[i][1], i
 +
end
 +
j = j + 1
 +
end
 +
end
 +
-- pas trouvé = return nil
 
end
 
end
  
 +
---
 +
-- valide que la chaîne passée est un mois valide.
 +
-- retourne le nom complet ou nil si non reconnu
 +
-- si reconnu, retourne aussi le numéro du mois [1-12]
 +
function fun.valideSaison( saison )
 +
if type( saison ) ~= "string" then
 +
return nil
 +
end
 +
 +
local m = mw.ustring.lower( mw.text.trim( saison ) )
 +
 +
for i = 1, 4 do
 +
local j = 1
 +
while liste_saison[i][j] ~= nil do
 +
if liste_saison[i][j] == m then
 +
return liste_saison[i][1]
 +
end
 +
j = j + 1
 +
end
 +
end
 +
-- pas trouvé = return nil
 +
end
  
function p._Date(datevec, lang, case, class, trim_year)
+
---
-- make sure inputs are in the right format
+
-- determinationMois trouve le numéro du mois et son nom,
if #datevec<6 then  
+
-- à partir de son nom, de son numéro ou d'une expression mathématique.
for i=#datevec,6,1 do datevec[i]='' end  
+
-- Si le deuxième paramètre est vrai, les nombres supérieur à 12 ou non entiers sont acceptés. 
 +
function fun.determinationMois( mois, mod, boucle )
 +
local num, nom
 +
if tonumber( mois ) then
 +
num = math.floor( tonumber( mois ) )
 +
if mod then
 +
-- si le nombre du mois est calculé par une exression, le résultat peut être supérieur à 12, ou inférieur à 1
 +
num = math.fmod( num + 239, 12 ) + 1  -- +239 car fmod(-1) = -1 et non 11
 +
elseif num < 1 or num > 12 then
 +
num = nil
 +
end
 +
elseif trim( mois ) then
 +
nom, num = fun.valideMois( mois )
 +
if nom == nil and boucle == nil then
 +
-- essai de détermination d'un nombre avec le parser #expr de Mediawiki.
 +
-- le paramètre boucle évite de tourner en boucle.
 +
nom, num = fun.determinationMois( mw.getCurrentFrame():callParserFunction( '#expr', mois ), true, true )
 +
end
 
end
 
end
if not case  then case  = '' end
+
if num and not nom then
if  not class then class = '' end
+
nom = liste_mois[num][1]
if  not trim_year then trim_year = '100-999' end
+
end
 +
return nom, num
 +
end
  
-- if language is not provided than look up users language
+
 
-- WARNING: This step should be done by the template as it does not seem to work as well here (cache issues?)
+
-- fonction interne à modeleDate, pour déterminer si on peut se passer de faire un ifexit
if not lang or  not mw.language.isValidCode( lang ) then
+
local function existDate( dataQualificatif, annee, mois )
lang = mw.message.new( "lang" ):plain()
+
local data
 +
if mois then
 +
data = dataQualificatif.mois
 +
else
 +
data = dataQualificatif.annee
 
end
 
end
 +
if type( data ) ~= 'table' then
 +
-- si data n'existe pas c'est que l'on considère qu'il n'y a pas de lien.
 +
return
 +
end
 +
-- le qualificatif est remplacer par celui de la base de donnée, ce qui permet des alias.
 +
local lien = annee .. ' ' .. ( dataQualificatif.qualificatif or '' )
 +
local seul = annee
 +
if mois then
 +
lien = mois .. ' ' .. lien
 +
seul = ucfirst( mois ) .. ' ' .. annee
 +
end
 +
local aucun = tonumber( data.aucun )
 +
if aucun and annee <= aucun then
 +
-- si la l'année est dans la partie 'aucun' on teste s'il y a malgré tout un lien isolé
 +
if type( data.seul ) == 'table' then
 +
for i, v in ipairs( data.seul ) do
 +
if seul == v or seul == tonumber( v ) then
 +
return lien
 +
end
 +
end
 +
end
 +
-- partie aucun et pas de lien => nil
 +
return nil
 +
elseif type( data.tous ) == 'table' then
 +
local tous1, tous2 = tonumber( data.tous[1] ), tonumber( data.tous[2] )
 +
if tous1 and tous2 and annee >= tous1 and annee <= tous2 then
 +
-- l'année est dans la partie 'tous' donc on retourne le lien
 +
return lien
 +
end
 +
end
 +
-- l'annee n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe.
 +
cibleLien = mw.title.new( lien )
 +
if cibleLien and cibleLien.exists then
 +
return lien
 +
end
 +
end
 +
 +
---
 +
-- Supprime le jour de la semaine, et "le" avant une date
 +
function fun.nettoyageJour( jour )
 +
if type( jour ) == 'string' then
 +
local nomJour = { '[Ll]undi', '[Mm]ardi', '[Mm]ercredi', '[Jj]eudi', '[Vv]endredi',
 +
'[Ss]amedi', '[Dd]imanche', '^ *[Ll]e' }
 +
local premier = { '<abbr class="abbr" title="Premier" >1<sup>er</sup></abbr>', '1er' }
 +
for i, v in ipairs( nomJour ) do
 +
jour = jour:gsub( v, '' )
 +
end
 +
for i, v in ipairs( premier ) do
 +
jour = jour:gsub( v, '1' )
 +
end
 +
jour = mw.text.trim( jour )
 +
end
 +
return jour
 +
end
 +
 +
---
 +
-- sépare une chaine date en un tabel contenant les champs jour, mois annee.
 +
function fun.separationJourMoisAnnee( date, decalage, args )
 +
date = trim( date )
 +
if date then
 +
local function erreur( periode, valeur )
 +
return false, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' )
 +
end
 +
decalage = decalage or 0
 +
args = args or {}
 +
date = fun.nettoyageJour( date )
 +
local annee = string.match( date, '^%d%d%d%d?$' )
 +
if annee then
 +
return fun.validationJourMoisAnnee{ '', '', annee, args[2 + decalage], decalage = decalage }
 +
end
 +
-- test date au format jj-mm-aaaa
 +
local j, m, a = string.match( date, '^([0-3]?%d)-([^ /-]+)-*(%d*[ AVJCavjc.-]*)$' )
 +
if not m then
 +
-- test date aux formats jj/mm/aaaa ou dd mmm aaaa
 +
j, m, a = string.match( date, '^([0-3]?%d)[ /]+([^ /-]+)[ /]*(-?%d*[ AVJCavjc.-]*)$' )
 +
end
 +
if not m then
 +
-- test date aux formats aaaa-mm-jj, aaaa/mm/jj et aaaa mmm jj
 +
a, m, j = string.match( date, '^(-?%d%d*)[ /-]+([^ /-]+)[ /-]*([0-3]?%d?)$' )
 +
end
 +
if not m then
 +
-- test date aux formats mm/aaaa ou mmm aaaa
 +
m, a = string.match( date, '^([^ /-]+)-?[ /]*(-?%d*[ AVJCavjc.]*)$' )
 +
end
 +
if m then
 +
if decalage < 0 then
 +
decalage = decalage + 2
 +
end
 +
if tonumber( j ) and tonumber( j ) > 31 and tonumber( a ) and tonumber( a ) < 31 then
 +
-- la date est au format aaaa/mm/jj
 +
return fun.validationJourMoisAnnee{ a, m, j, args[2 + decalage], decalage = decalage }
 +
else
 +
return fun.validationJourMoisAnnee{ j, m, a, args[2 + decalage], decalage = decalage }
 +
end
 +
else
 +
return erreur( 'Date', date )
 +
end
 +
else
 +
return true, {}
 +
end
 +
end
 +
 +
 +
---
 +
-- separationJourMoisAnnee prend jusqu'a cinq paramètre et essaie de les séparer en jour, mois, annee et qualificatif
 +
-- la date peut être dans le premier paramètre ou séparée dans les paramètre 1 à 3 ou 2 à 4.
 +
-- Le qualificatif est cherché dans le paramètre suivant.
 +
-- La fonction retourne true suivit d'une table avec la date en paramètres nommé (sans accent sur année)
 +
-- ou false suivit d'un message d'erreur.
 +
function fun.validationJourMoisAnnee( frame, ... )
 +
local args = Outils.extractArgs( frame, ... )
 +
local jour, mois, numMois, annee, qualificatif, erreur
 +
args[1] = tostring( args[1] or args['jour'] or '' )
 +
args[2] = tostring( args[2] or args['mois'] or '' )
 +
args[3] = tostring( args[3] or args['annee'] or args['année'] or '')
 +
args[4] = tostring( args[4] or '' )
 
 
-- Just in case someone broke the internationalization code than fix the english defaults
+
local function erreur( periode, valeur )
if i18n.DateLang['en'] == nil then
+
return false, Outils.erreur( periode .. ' invalide (' .. valeur .. ')' )
i18n.DateLang['en'] = 'en-form'
+
end
+
if i18n.DateFormat['en-form'] == nil then
+
i18n.DateFormat['en-form'] = {YMDHMS='j F Y, H:i:s', YMDHM='j F Y, H:i', YMD='j F Y', YM='F Y', MD='j F', Y='Y'}
+
 
end
 
end
 +
 +
local decalage = 0
 +
-- si pas de jour mais que args[2] est un mois on décale tout et on
 +
-- n'affiche pas l'année
 +
local arg1, arg2, arg3 = trim( args[1] ), trim( args[2] ), trim( args[3] )
 +
if arg1 == nil and arg2 and (fun.determinationMois( args[3] ) or arg2:match( '[^ /][ /-].*%d%d$' ) ) then
 +
decalage = 1
 +
elseif arg1 == nil and arg2 == nil and arg3 and
 +
( fun.determinationMois( args[4] ) or arg3:match( '[^ /][ /-].*%d%d$' ) ) then
 +
decalage = 2
 +
elseif arg1 and arg1:match( '%d%d%d$' )  then
 +
-- l'année est dans le premier paramètre
 +
decalage = -2
 +
elseif not tonumber( args[3] ) and
 +
tonumber( args[2] ) and tonumber( args[2] ) > 12 and 
 +
fun.determinationMois( args[1] )
 +
then
 +
-- le mois est dans le premier paramètre et l'année dans le deuxième
 +
decalage = -1
 +
end
 +
 +
-- on traite l'année
 +
local bannee = args[3 + decalage]
 +
if Outils.notEmpty( bannee ) then
 +
annee = tonumber( bannee )
 +
if annee == nil and type( bannee ) == 'string'  then
 +
-- test si l'année contient av. J.-C.
 +
annee = string.match( string.upper( bannee ), '^(%d+)%sAV%.?%s?J%.?%-?C' )
 +
annee = tonumber( annee )
 +
if annee then
 +
annee = 0 - annee
 +
elseif decalage == -2 then
 +
return fun.separationJourMoisAnnee( bannee, decalage, args )
 +
else
 +
return erreur( 'Année', bannee )
 +
end
 +
end
 +
else
 +
annee = nil
 +
end
 +
 +
-- on traite le mois
 +
local bmois = args[2 + decalage]
 +
if Outils.notEmpty( bmois ) then
 +
mois, numMois = fun.determinationMois( bmois )
 +
if mois == nil then
 +
mois = fun.valideSaison( bmois )
 +
if mois == nil then
 +
if annee then
 +
return erreur( 'Mois', bmois )
 +
elseif type( dataLiens[ trim( bmois ) ] ) == 'table' then
 +
-- le deuxième paramètre est un qualificatif, donc la date est dans le premier paramètre
 +
local bjour = args[1 + decalage]
 +
return fun.separationJourMoisAnnee( bjour, decalage, args )
 +
else
 +
return erreur( 'Mois ou qualificatif', bmois )
 +
end
 +
end
 +
else
 +
 +
-- on traite le jour si présent
 +
local bjour = args[1 + decalage]
 +
if Outils.notEmpty( bjour ) then
 +
jour = tonumber( bjour )
 +
if jour == nil then
 +
jour = tonumber( fun.nettoyageJour( bjour ) )
 +
end
 +
if jour == nil then
 +
return erreur( 'Jour', bjour )
 +
end
 +
-- on valide que le jour est correct
 +
if jour < 1 or jour > 31 then
 +
return erreur( 'Jour', bjour )
 +
elseif jour > liste_mois[numMois].nJour then
 +
return erreur( 'Jour', bjour .. bmois )
 +
-- l'année bisextile n'est pas testée pour accepter les dates juliennes.
 +
end
 +
else
 +
-- S'il n'y a pas de jour on regarde si la première lettre du mois est en majuscule
 +
if mw.ustring.match( bmois, '^%u' ) then
 +
-- oui, on passe la première lettre en majuscule
 +
mois = ucfirst( mois )
 +
end
 +
-- s'il n'y a pas d'année non plus on retourne le mois simple
 +
end
 +
end
 +
elseif decalage > -1 then
 +
-- on teste le jour si présent
 +
local bjour = args[1 + decalage]
 +
if Outils.notEmpty( bjour ) then
 +
if annee then
 +
return erreur( 'Mois', 'absent' )
 +
else
 +
bjour = fun.nettoyageJour( bjour )
 +
jour = tonumber( bjour )
 +
if jour then
 +
if jour > 31 or jour < 1 then
 +
annee = jour
 +
jour = nil
 +
else
 +
return erreur( 'date', 'jour seul : ' .. bjour )
 +
end
 +
else
 +
return fun.separationJourMoisAnnee( bjour, decalage, args )
 +
end
 +
end
 +
end
 +
end
 +
 +
-- on traite le champs optionnel
 +
qualificatif = trim( args[4 + decalage] ) or args.qualificatif
 +
 +
local result = {
 +
jour = jour,
 +
mois = mois,
 +
numMois = numMois,
 +
annee = annee,
 +
qualificatif = qualificatif,
 +
decalage = decalage + ( args.decalage or 0 )
 +
}
 +
return true, result
 +
end
 +
 +
 +
---
 +
-- émule le modèle {{m|Date}}.
 +
-- Paramètres :
 +
-- 1 : jour (numéro ou "1er"). optionnel, si absent pas de jour
 +
-- 2 : mois (en toutes lettres)
 +
-- 3 : année (nombre)
 +
-- 4 : optionnel, spécialité de l'année
 +
-- Comportement spécial ("truc à deux balles au lieu d'utiliser un
 +
-- paramètre nommé du genre "sans année=oui"...") : si 1 est vide
 +
-- mais que le reste est complet → on n'affiche pas l'année
 +
function fun.modeleDate( frame )
 +
local args = Outils.extractArgs( frame )
 +
 +
-- séparation des paramètres jour, mois et année si nécessaire
 +
local test, resultat = fun.validationJourMoisAnnee( args )
 +
if not test then
 +
local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
 +
if namespaceCategorisation[ mw.title.getCurrentTitle().namespace ] and
 +
not Outils.notEmpty( args.nocat ) then
 +
return resultat .. '[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée]]'
 +
else
 +
return resultat
 +
end
 +
end
 +
local annee, mois, numMois, jour = resultat.annee, resultat.mois, resultat.numMois, resultat.jour
 +
local decalage, qualificatif = resultat.decalage, resultat.qualificatif
 +
 +
if ( annee or mois or jour ) == nil then
 +
return
 +
end
 +
 +
-- on traite l'age, naissance et mort
 +
local age = trim( args['âge'] or args['age'] )
 +
age = age and  fun.age( annee, numMois, jour )
 +
local naissance = trim( args.naissance )
 +
local mort = trim( args.mort )
 +
 +
-- on traite le calendrier
 +
local gannee, gmois, gjour = annee, numMois, jour        -- date suivant le calendrier grégorien pour <time>
 +
local jannee, jmois, jjour = annee, mois, jour  -- servira éventuellement à a affiché la date selon le calendrier julien
 +
local julien2, julien3 = nil, nil                        -- servira éventuellement à a affiché des parenthèses
 +
local julien = trim( string.lower( args.julien or '' ) )
 +
if annee and jour then
 +
local amj = annee * 10000 + numMois * 100 + jour
 +
if amj < 15821014 then
 +
if annee > 0 then
 +
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
 +
else
 +
-- calendrier grégorien proleptique avec année 0.
 +
gannee, gmois, gjour = fun.julianToGregorian( annee + 1, numMois, jour )
 +
end
 +
elseif julien == 'oui' then
 +
gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
 +
annee, mois, jour = gannee, liste_mois[gmois][1], gjour
 +
end
 +
else
 +
if annee and annee < 0 then
 +
gannee = gannee + 1
 +
end
 +
end
 +
 +
 +
-- on génère le résultat
 +
 +
-- Déclarations des variables
 +
local wikiListe = TableBuilder.new()  -- reçois le texte affiché pour chaque paramètre
 +
local iso = TableBuilder.new()        -- reçois le format date ISO de ce paramètre
 +
 +
local dataQualificatif, dataCat
 +
if not args.nolinks then
 +
dataQualificatif = dataLiens[qualificatif or '']
 +
if type( dataQualificatif ) ~= 'table' then
 +
-- si le qualifiquatif n'est pas dans la base de donnée, on crée une table minimum,
 +
-- qui imposera un test sur l'annee, mais considère qu'il n'y a pas de lien sur le jour ou le mois
 +
dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } }
 +
end
 +
dataCat = dataLiens[dataQualificatif.cat]
 +
if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then
 +
dataCat = { qualificatif = '' }
 +
end
 +
end
 +
local function wikiLien( lien, texte )
 +
if lien == texte then
 +
return '[[' .. texte .. ']]'
 +
else
 +
return '[[' .. lien .. '|' .. texte .. ']]'
 +
end
 +
end
 +
 +
-- Date julienne
 +
if jjour ~= jour then
 +
if jjour == 1 then
 +
jjour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
 +
end
 +
if jannee ~= annee then
 +
julien3 = '(<abbr class=abbr title="selon le calendrier julien">' .. jjour .. ' ' .. jmois .. ' ' .. jannee .. '</abbr>)'
 +
else
 +
julien2 = '(<abbr class=abbr title="selon le calendrier julien">' .. jjour .. ' ' .. jmois .. '</abbr>)'
 +
end
 +
end
 +
  
-- create datecode based on which variables are provided and check for out of bound values
+
-- le jour si présent
local maxval = {9999, 12, 31, 23, 59, 60} -- max values for year, month, ...
+
local qualifJour = ''
local c = {'Y', 'M', 'D', 'H', 'M', 'S'}
+
if jour then
local datecode = '' -- a string signifying which combination of variables was provided
+
local texteJour = jour
local datenum = {}  -- date-time encoded as a vector = [year, month, ... , second]
+
if args.nolinks then
for i, v in ipairs( datevec ) do
+
if jour == 1 then
if v~=nil and v~='' then
+
jour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
datecode = datecode .. c[i]
+
datenum[i] = tonumber(v)
+
if datenum[i]==nil and i==2 then
+
-- month is not a number -> check if it is a month name in English
+
v = mw.language.new('en'):formatDate( "n", v)
+
datenum[i] = tonumber(v)
+
 
end
 
end
if datenum[i]==nil or datenum[i]>maxval[i] then
+
wikiListe.insert( jour )
-- Some numbers are out of range -> abort and return the empty string
+
else
return ''
+
qualifJour = dataQualificatif.jour and dataQualificatif.qualificatif
 +
or dataCat.jour and dataCat.qualificatif
 +
or ''
 +
local lien = jour .. ' ' .. mois .. ' ' .. qualifJour
 +
if jour == 1 then
 +
jour = '1<sup>er</sup>'
 +
lien = '1er ' .. mois .. ' ' .. qualifJour
 
end
 
end
 +
-- s'il n'y a pas de lien sur le mois, il sera affiché avec le jour.
 +
wikiListe.insert( wikiLien( lien, jour ) )
 +
wikiListe.insert( wikiLien( lien, jour .. ' '.. mois ) )
 
end
 
end
 +
iso.insert( 1, string.sub( '0' .. gjour, -2 ) )
 
end
 
end
 
 
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
+
-- le mois
local timeStamp
+
if mois then
if datecode == 'YMDHMS' then
+
if #wikiListe == 0 and ( annee == nil or decalage > 1 ) then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
+
return mois
elseif datecode == 'YMDHM' then
+
end
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
+
if args.nolinks then
elseif datecode:sub(1,3)=='YMD' then
+
if decalage < 2 then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
+
wikiListe.insert( mois )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
+
end
elseif datecode == 'YM' then
+
else
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
+
local lien
elseif datecode:sub(1,1)=='Y' then
+
if annee then
timeStamp = string.format('%04i', datenum[1] )
+
lien = existDate( dataQualificatif, annee, mois ) or existDate( dataCat, annee, mois )
datecode = 'Y'  
+
if lien == nil and qualificatif and qualifJour == '' then
elseif datecode == 'M' then
+
-- test nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif.
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
+
lien = existDate( dataLiens[''], annee, mois )
class = '' -- date not complete -> no html formating or micro-tagging of date string
+
end
elseif datecode == 'MD' then
+
end
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
+
if lien or decalage == 2 then
class = '' -- date not complete -> no html formating or micro-tagging of date string
+
-- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois']]
 +
wikiListe.remove()
 +
if decalage < 2 then
 +
wikiListe.insert( wikiLien( lien, mois ) )
 +
end
 +
else
 +
-- sinon on retire le lien affichant 'jour' pour ne garder que le lien 'jour mois'
 +
wikiListe.remove( #wikiListe - 1 )
 +
-- s'il n'y avait pas je jour, la liste est vide mais ça ne pose pas de problème
 +
-- sauf si l'année n'est pas affichée :
 +
if #wikiListe == 0 and ( annee == nil or decalage > 0 ) then
 +
return mois
 +
end
 +
end
 +
end
 +
if gmois then
 +
iso.insert( 1, string.sub( '0' .. gmois, -2 ) )
 +
end
 +
end
 +
if( julien2 ) then
 +
wikiListe.insert( julien2 )
 +
end
 +
 +
-- l'année
 +
if annee then
 +
local texteAnnee = annee
 +
local lien
 +
if annee < 0 then
 +
local annneeAvJc = 0 - annee
 +
lien = lien or ( annneeAvJc .. ' av. J.-C.' )
 +
local avJC = trim( string.lower( args.avJC or '' ) )
 +
if args.avJC == 'non' then
 +
texteAnnee = annneeAvJc
 +
else
 +
texteAnnee = annneeAvJc .. ' <abbr class="abbr" title="'
 +
.. annneeAvJc .. ' avant Jésus-Christ">av. J.-C.</abbr>'
 +
end
 +
end
 +
if args.nolinks then -- seulement si on doit l'affichée
 +
if decalage < 1 then
 +
wikiListe.insert( texteAnnee )
 +
end
 +
else
 +
lien = existDate( dataQualificatif, annee ) or existDate( dataCat, annee ) or lien or annee
 +
if mois and #wikiListe == 0 then
 +
-- si le mois n'a pas de lien et n'est pas affiché avec le jour, il est affiché avec l'année.
 +
texteAnnee = mois .. ' ' .. texteAnnee
 +
end
 +
if decalage < 1 then -- seulement si on doit l'affichée
 +
wikiListe.insert( wikiLien( lien, texteAnnee ) )
 +
end
 +
end
 +
 +
if gannee > 999 then
 +
iso.insert( 1, gannee )
 +
elseif gannee > -1 then
 +
iso.insert( 1, string.sub( '000' .. gannee , -4 ) )
 +
elseif gannee > -999 then
 +
-- calendrier grégorien proleptique avec année 0.
 +
iso.insert( 1, 'U-' .. string.sub( '000' .. ( 0 - gannee ), -4 ) )
 +
else
 +
iso.insert( 1, 'U' .. gannee )
 +
end
 +
end
 +
if( julien3 ) then
 +
wikiListe.insert( julien3 )
 +
end
 +
 
 +
 +
-- l'age
 +
if type( age ) == 'number' and age >= 0 and ( not naissance or age < 120 ) then
 +
if age == 0 then
 +
age = '(moins d\'un&nbsp;an)'
 +
elseif age == 1 then
 +
age = '(1&nbsp;an)'
 +
else
 +
age = '(' .. age .. '&nbsp;ans)'
 +
end
 
else
 
else
return ''  -- format not supported
+
age = false
 
end
 
end
 +
 +
-- compilation du résultat
 +
local wikiTexte = wikiListe.concat( ' ' )
 +
local isoTexte = iso.concat( '-' )
 +
 +
-- On ajoute un peu de sémantique.
 +
local wikiHtml = mw.html.create( '' )
 +
 +
local dateHtml = wikiHtml:tag( 'time' )
 +
:wikitext( wikiTexte )
 +
if wikiTexte:match( ' ' ) then
 +
dateHtml:addClass( 'nowrap' )
 +
end
 +
if isoTexte ~= wikiTexte then
 +
dateHtml:attr( 'datetime', isoTexte )
 +
end
 +
if not args.nolinks then
 +
dateHtml:addClass( 'date-lien' )
 +
end
 +
if naissance then
 +
dateHtml:addClass( 'bday' )
 +
elseif mort then
 +
dateHtml:addClass( 'dday' )
 +
end
 +
if age then
 +
wikiHtml:wikitext( ' ' )
 +
:tag( 'span' )
 +
:addClass( 'noprint')
 +
:wikitext( age )
 +
:done()
 +
end
 +
 +
return tostring( wikiHtml )
 +
end
  
-- ==========================================================
+
---
-- === Create Date String using in chosen language
+
-- fonction destinée aux infobox, notamment pour afficher les dates de naissance et de mort
-- ==========================================================
+
-- les liens présent dans les dates fournies sont automatiquement supprimées pour gérer les cas ou
 +
-- le paramètre contient déjà un modèle date.
 +
-- Paramètres :
 +
-- 1 : type de date à afficher (naissance / n, mort / m, ou date / d)
 +
-- 1 : Date ou date de naissance
 +
-- 2 : Date de mort si type n ou m
 +
-- qualificatif = suffixe des page de date à lier (exemple : en musique)
 +
-- nolinks : n'affiche pas de lien
 +
function fun.dateInfobox( frame )
 +
local args = frame.args
 +
if not args[1] and args[2] then
 +
return
 +
end
 +
 +
local function nettoyageDate( date )
 +
-- supprime les liens en ce qui est entre parenthèse à la fin
 +
date = date:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end )
 +
date = date:gsub( '%([^()]+%)$', '' )
 +
 +
if date:match( '^%s*[-?/Nn][/Aa.]*%s*$' ) or date:match( 'inconn?ue?' ) then
 +
return false
 +
else
 +
return trim( date  )
 +
end
 +
end
 
 
-- which form should the date take?
+
local naissance = args[1]:match( '^n' )
-- Use Fallback module to handle rare languages which are more likely to use different for than default EN form
+
local mort = args[1]:match( '^m' )
local langDateForm = Fallback._langSwitch(i18n.DateLang, lang)
+
 
 
-- special case of French and Gallic dates, which require different date format for the 1st day of the month
+
-- 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 datenum[3]==1 and (langDateForm=='fr-form' or langDateForm=='ga-form') then
+
if mort and args[3] and args[2]:match( '</time>' ) then  
langDateForm = langDateForm .. '1' -- ordinal form for the first day of the month
+
return args[3]
 +
elseif args[2]:match( '</time>' ) then
 +
return args[2]
 
end
 
end
-- Look up country specific format input to {{#time}} function
 
local dFormat = i18n.DateFormat[langDateForm][datecode]
 
 
 
-- overwrite default grammatical case of the month (applies mostly to Slavic languages)
+
local nolinks = trim( args.nolinks )
if (case=='gen') then
+
local nocat = args.nocat
-- CAUTION: at the moment i18n.DateFormat uses "F" only as month name, but this might change and this operation does not check if 'F' is in "" brackets or not, so if some language starts using 'F'  in "" than this will not work for that language
+
local dateN = nettoyageDate( args [2] or '' )
dFormat = dFormat:gsub("F", "xg");
+
local dateM, qualificatif
 +
if naissance or mort then
 +
dateM = nettoyageDate( args[3] or '' )
 +
qualificatif = args.qualificatif or args[4]
 +
else
 +
qualificatif = args.qualificatif or args[3]
 
end
 
end
if (case=='nom') then
+
-- CAUTION: at the moment i18n.DateFormat uses "xg" only as month name, but this might change and this operation does not check if 'xg' is in "" brackets or not, so if some language starts using 'xg' in "" than this will not work for that language
+
if mort then
dFormat = dFormat:gsub("xg", "F");
+
if not dateM then
 +
return
 +
end
 +
local age = ''
 +
if dateN then
 +
local t1, tdateN = fun.separationJourMoisAnnee( dateN )
 +
local t2, tdateM = fun.separationJourMoisAnnee( dateM )
 +
if t1 and t2 then
 +
local calcul = fun.age( tdateN.annee, tdateN.numMois, tdateN.jour, tdateM.annee, tdateM.numMois, tdateM.jour )
 +
if calcul > 1 then
 +
age = ' ' .. calcul .. ' ans)'
 +
elseif calcul == 1 then
 +
age = ' (à un an)'
 +
elseif calcul == 0 then
 +
age = " (à moins d'un an)"
 +
end
 +
end
 +
end
 +
return fun.modeleDate{ dateM, qualificatif, mort = '1', nolinks = nolinks, nocat = nocat } .. age
 +
else
 +
if not trim( dateN ) then
 +
return
 +
end
 +
if naissance and dateM == nil then
 +
return fun.modeleDate{ dateN, qualificatif, age = 'oui', naissance='1', nolinks = nolinks, nocat = nocat }
 +
else
 +
return fun.modeleDate{ dateN, qualificatif, naissance=naissance, nolinks = nolinks, nocat = nocat }
 +
end
 
end
 
end
if ((lang=='ru' or lang=='pl' or lang=='cs' or lang=='sl') and (case=='loc' or case=='ins')) or
+
end
(lang=='fi' and (case=='ptv' or case=='ine'or case=='ela'or case=='ill') ) then
+
 
local monthEn = mw.language.new('en'):formatDate( "F", timeStamp) -- month name in English
+
 
-- month name using proper case and language. It relies on messages stored in MediaWiki namespace for some cases and languages
+
---
-- That is why this IF statement uses "lang" not "langDateForm" variable to decide
+
-- voir émule le modèle:Inscription date
local monthMsg = mw.message.new( string.format('%s-%s', monthEn, case ) ):inLanguage( lang )
+
-- la détection des arguments permet d'utilisé la fonction depuis un modèle, depuis invoke, ou depuis une autre fonction.
if not monthMsg:isDisabled() then -- make sure it exists
+
-- pour facilité l'écriture de lua, annee (sans accent) est accepté lors de l'appel depuis lua.
local month=monthMsg:plain()
+
function fun.inscriptionDate( frame )
dFormat = dFormat:gsub('F', '"'..month..'"'); -- replace default month with month name we already looked up
+
local args = Outils.extractArgs( frame )
dFormat = dFormat:gsub('xg', '"'..month..'"');
+
local annee = Outils.notEmpty( args['année'], args.annee, args.year )
 +
if annee then
 +
if annee:match( '^%-?%d+$' ) then
 +
-- si l'année est correctement renseigné, on essaye de trouver le mois
 +
local mois = Outils.notEmpty( args.mois, args.month, args.saison )
 +
mois = string.lower(fun.determinationMois( mois ) or fun.valideSaison( mois ) or mois or '')
 +
local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
 +
local t, jma = fun.validationJourMoisAnnee( jour, mois, annee )
 +
if t then
 +
return fun.modeleDate{ jour, mois, annee, nolinks=true }
 +
else
 +
local date = { jour }
 +
table.insert( date, mois )
 +
table.insert( date, annee )
 +
return '<time class="nowrap" datevalue="' .. annee .. '">' .. table.concat( date, ' ' ) .. '</time>'
 +
end
 +
else
 +
return fun.inscriptionDate{ date = annee }
 +
end
 +
else
 +
-- si annee n'est pas précisé, on utilise la paramètre date
 +
local date = Outils.validTextArg( args, 'date' )
 +
if date then
 +
date = date:lower()
 +
local t, jma = fun.separationJourMoisAnnee( date )
 +
if t and ( fun.determinationMois( jma.mois ) or fun.valideSaison( jma.mois ) ) then
 +
args['année'] = jma.annee
 +
jma.nolinks = true
 +
jma.nocat = true
 +
return fun.modeleDate( jma )
 +
else
 +
-- date non reconnue, on essaye Month day, year
 +
local mois, jour, annee = mw.ustring.match( date, '^([%a]+)%s*(%d%d?)[,%s]+(%d+)$' )
 +
if fun.determinationMois( mois ) or fun.valideSaison( mois ) then
 +
return fun.modeleDate{ jour, mois, annee, nolinks=true }
 +
end
 +
end
 +
return date
 
end
 
end
 
end
 
end
-- Special case related to Quechua and Kichwa languages
+
return ''
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
+
end
if (lang=='qu' or lang=='qug') and case=='nom' then
+
dFormat = dFormat:gsub('F"pi"', 'F');
+
end
+
  
-- Lua only date formating using {{#time}} parser function (new)
+
---
-- prefered call which gives "Lua error: too many language codes requested." on the [[Module talk:Date/sandbox/testcases]] page
+
-- la fonction dateISO renvoie un date au format aaaa-mm-jj (sans liens)
--local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp)  
+
-- l'année peut être sous la forme 2013 ou [[2013 en litérature|2013]]
local datestr = mw.getCurrentFrame():callParserFunction( "#time", { dFormat, timeStamp, lang } )
+
-- le mois peut être en lettre ou en chiffres
 +
-- le jour peut être sous la forme '05', '{{1er}}' ou 'vendredi 13'
 +
function fun.dateISO( frame )
 +
local args = Outils.extractArgs( frame )
 +
local annee = Outils.notEmpty( args['année'], args.annee, args.year, args.date )
 +
-- extraction de l'année
 +
if type( annee ) == 'string' then
 +
annee = ( tonumber( annee ) -- match '2013'
 +
or string.match ( annee, '%D(%d%d%d%d)%D' ) -- match  '[[2013 en musique|2013]]'
 +
or string.match ( annee, '%D(%d%d%d%d)$' )  -- match '17 septembre 2013'
 +
or string.match ( annee, '^(%d%d%d%d)%D' )  -- match '2013-09-17'
 +
)
 +
end
 +
annee = tonumber( annee )
 
 
-- Another special case related to Thai solar calendar
+
-- le format de date iso est défini suivant le calendrier grégorien.
if lang=='th' and datenum[1]~= nil and datenum[1]<=1940 then
+
-- Avant l'année 1583 la date est calendrier est probablement du calendrier julien,
-- As of 2014 {{#time}} parser function did not resolve those cases properly
+
-- donc autant s'abstenir.
-- See https://en.wikipedia.org/wiki/Thai_solar_calendar#New_year for reference
+
if annee and annee > 1582  then
-- Disable once https://bugzilla.wikimedia.org/show_bug.cgi?id=66648 is fixed
+
local mois = Outils.notEmpty( args.mois, args.month )
if datecode=='Y' then -- date is ambiguous
+
-- num mois trouve le numéro du mois, qu'il soit numérique ou texte, complet ou abrégé.
datestr = string.format('%04i หรือ %04i', datenum[1]+542, datenum[1]+543 )  
+
local nomMois, numMois = fun.determinationMois( mois )
elseif datenum[2]<=3 then -- year is wrong (one too many)
+
if numMois then
datestr = datestr:gsub( string.format('%04i', datenum[1]+543), string.format('%04i', datenum[1]+542 ) )
+
mois = '-' .. string.sub( '0' .. numMois, -2 )
 +
 +
local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
 +
if type( jour ) == 'string' then
 +
jour = tonumber( jour ) or tonumber( string.match ( jour, '%d+') )
 +
end
 +
jour = tonumber( jour )
 +
if jour and jour <= liste_mois[numMois].nJour then
 +
jour = '-' .. string.sub( '0' .. jour, -2 )
 +
return annee .. mois .. jour
 +
else
 +
return annee .. mois
 +
end
 +
else
 +
return tostring( annee )
 
end
 
end
 
end
 
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}
 
 
-- If year<1000 than either keep it padded to the length of 4 digits or trim it
+
local rank = (ranks[mt] or 0) + dy - 1
-- decide if the year will stay padded with zeros (for years in 0-999 range)
+
if(fun.isLeapYear(yr) and (mt >= 3)) then
if datenum[1]~= nil and datenum[1]<1000 then
+
rank = rank+1
local trim = yesno(trim_year,nil)
+
end
if trim == nil then
+
return rank
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
+
end
trim = (YMin~=nil and datenum[1]>=tonumber(YMin) and datenum[1]<=tonumber(YMax))  
+
 
 +
-- Nombre de jours entre deux années (du 1er janvier au 1er janvier)
 +
-- Suit le calendrier grégorien
 +
function fun.do_daysBetween(arguments)
 +
local yr1 = tonumber(arguments[1]) or 0
 +
local yr2 = tonumber(arguments[2]) or 0
 +
 +
return fun.daysSinceOrigin(yr2) - fun.daysSinceOrigin(yr1)
 +
end
 +
 
 +
-- Nombre de jours depuis l'année 1 (du 1er janvier au 1er janvier)
 +
function fun.daysSinceOrigin(year)
 +
local yr = year-1
 +
return 365*yr + math.floor(yr/4) - math.floor(yr/100) + math.floor(yr/400)
 +
end
 +
 
 +
-- Test d'année bissextile (Suit le calendrier grégorien)
 +
function fun.isLeapYear(year)
 +
local yr = tonumber(year) or 1
 +
return (yr%4 == 0) and ((yr%100 ~= 0) or (yr%400 == 0))
 +
end
 +
 
 +
-- Conversion d'un nombre en chiffres romains
 +
function fun.toRoman(number)
 +
local n = math.floor(number)
 +
local letters = {"I","V","X","L","C","D","M","",""}
 +
local pattern = {"","0","00","000","01","1","10","100","1000","02"}
 +
local result = ""
 +
if(n<=0 or n>=4000) then
 +
result = "---"
 +
else
 +
for i=1,7,2 do
 +
p = pattern[n%10 + 1]
 +
for j=0,2 do
 +
p = string.gsub(p,tostring(j),letters[i+j])
 +
end
 +
result = p .. result
 +
n = math.floor(n/10)
 
end
 
end
 +
end
 +
return result
 +
end
 +
 +
-- Conversion et affichage d'une date dans le calendrier républicain
 +
function fun.dateRepublicain(frame)
 +
local pframe = frame:getParent()
 +
local arguments = pframe.args
 +
return fun.formatRepCal(fun.do_toRepCal(arguments))
 +
end
 +
 +
---
 +
-- Calcul d'une date dans le calendrier républicain
 +
-- On suppose que les années 4n+3 sont sextiles (3, 7, 11...)
 +
function fun.do_toRepCal(arguments)
 +
local yr = tonumber(arguments.year or arguments[1]) or 2000
 +
-- rang absolu du jour demandé, le jour 0 étant le 22 septembre 1792 (1er jour de l'an I)
 +
local repDays = fun.do_dayRank(arguments) + fun.do_daysBetween{1792,yr} - fun.do_dayRank{1792,9,22}
 +
local repYear = math.floor((repDays+731)/365.25) - 1
 +
local repDayRank = repDays - 365*(repYear-1) - math.floor(repYear/4)
 +
local repMonth, repDay = math.floor(repDayRank/30)+1, (repDayRank%30)+1
 +
return {repYear, repMonth, repDay}
 +
end
 +
 +
---
 +
-- Formatage d'une date selon le calendrier républicain
 +
-- Usage : fun.formatRepCal{année,mois,jour}
 +
function fun.formatRepCal(arguments)
 +
local months = {"Vendémiaire","Brumaire","Frimaire","Nivôse","Pluviôse","Ventôse","Germinal","Floréal","Prairial","Messidor","Thermidor","Fructidor"}
 +
local extras = {"de la vertu","du génie","du travail","des récompenses","de l'opinion","de la révolution"}
 +
local result = ""
 +
if(arguments[2] < 13) then
 +
result = result .. tostring(arguments[3]) .. "&nbsp;" .. months[arguments[2]]
 +
else
 +
result = result .. "jour " .. extras[arguments[3]]
 +
end
 +
result = result .. " de l'an " .. fun.toRoman(arguments[1])
 +
return result
 +
end
 +
 +
---
 +
-- Voir Modèle:Âge
 +
-- retourne l'age en fonction de la ou les dates fournies. La valeur retounée est de type 'number'
 +
-- Parammètres :
 +
-- 1, 2, 3 : année, mois jour de naissance (supposé dans le calendrier grégorien)
 +
-- 4, 5, 6 : année, mois, joue du calcul (facultatif, par défaut la date UTC courante).
 +
function fun.age( an, mn, jn, ac, mc, jc )
 +
local an = tonumber( an )
 +
if an == nil then
 +
-- pas de message d'erreur qui risque de faire planter la fonction appelante
 +
-- à elle de gérer ce retour.
 +
return
 +
end
 +
-- les jours et mois sont par défaut égal à 1, pour pouvoir calculer un age même si la date est incompète.
 +
local mn = tonumber( mn ) or 1
 +
local jn = tonumber( jn ) or 1
 
 
-- If the date form isn't the Thai solar calendar, don't zero pad years in the range of 100-999.
+
local today = os.date( '!*t' )
-- If at some point support for Islamic/Hebrew/Japanese years is added, they may need to be skipped as well.  
+
local ac = tonumber( ac ) or today.year
if trim then
+
local mc = tonumber( mc ) or today.month
--local yearStr1 = mw.language.new(lang):formatDate( 'Y', timeStamp)
+
local jc = tonumber( jc ) or today.day
local yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', timeStamp, lang } )
+
--local yearStr1 = datestr:match( '%d%d%d%d' ) -- 4 digits in a row (in any language) - that must be a year
+
local age = ac - an
local yearStr2 = yearStr1
+
if mc < mn or ( mc == mn and jc < jn ) then
local zeroStr = mw.ustring.sub(yearStr1,1,1)
+
age = age - 1
for i=1,3 do -- trim leading zeros
+
end
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
+
return age
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
+
end
 +
 
 +
function fun.modeleAge( frame )
 +
args = frame.getParent().args
 +
local annee = args[1] or args['année']
 +
if annee == nil then
 +
return Outils.erreur( "Il faut au minimum l'année pour calculer un âge" )
 +
end
 +
local age = fun.age (
 +
args[1] or args['année'],
 +
args[2] or args['mois'],
 +
args[3] or args['jour'],
 +
args[4],
 +
args[5],
 +
args[6]
 +
)
 +
if age then
 +
return age
 +
else
 +
return Outils.erreur("les paramètres doivent être des chiffres" )
 +
end
 +
end
 +
 
 +
---
 +
-- calcul du jour julien à partir d'une date du calendrier grégorien
 +
function fun.julianDay( year, month, day, hour, min, sec )
 +
local julian
 +
julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 )
 +
- math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 100 )
 +
+ math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 400 )
 +
+ math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
 +
+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400
 +
- 32167.5
 +
return julian
 +
end
 +
 
 +
---
 +
-- calcul du jour julien à partir d'une date du calendrier julien
 +
function fun.julianDayJulian( year, month, day, hour, min, sec )
 +
local julian
 +
julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 )
 +
+ math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
 +
+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400
 +
- 32205.5
 +
return julian
 +
end
 +
 
 +
---
 +
-- calcul d'une date dans le calendrier grégorien à partir du jour julien
 +
function fun.julianDayToGregorian( julianDay )
 +
local base = math.floor( julianDay + 32044.5 ) -- 1 March -4800 (proleptic Gregorian date)
 +
local nCentury = math.floor( ( base * 4 + 3 ) / 146097 )
 +
local sinceCentury = base - math.floor( nCentury * 146097 / 4 )
 +
local nYear = math.floor( ( sinceCentury * 4 + 3 ) / 1461 )
 +
local sinceYear = sinceCentury - math.floor( nYear * 1461 / 4 )
 +
local nMonth = math.floor( ( sinceYear * 5 + 2 ) / 153 )
 +
 +
local day = sinceYear - math.floor( (  nMonth  * 153 + 2 ) / 5 ) + 1
 +
local month = nMonth  - math.floor(  nMonth  / 10 ) * 12 + 3
 +
local year = math.floor( sinceYear / 306 ) + nYear + 100 * nCentury - 4800
 +
 +
return year, month, day
 +
end
 +
 
 +
---
 +
-- calcul d'une date dans le calendrier julien à partir du jour julien
 +
-- calcul basé sur l'algorythme de la page fr.wikipedia.org/wiki/Jour_julien (1/10/2013)
 +
function fun.julianDayToJulian( julianDay )
 +
local year = math.modf( ( julianDay * 4 - 6884469 ) / 1461 )
 +
local r2 = julianDay - math.modf( ( 1461 * year + 6884472 ) / 4 )
 +
local month = math.modf( ( 5 * r2 + 461 ) / 153 )
 +
local day = r2 - math.modf( ( 153 * month - 457 ) / 5 ) + 1
 +
if month > 12 then
 +
year = year + 1
 +
month = month - 12
 +
end
 +
return year, month, day
 +
end
 +
 
 +
---
 +
-- calcul d'une date dans le calendrier grégorien à partir d'une date dans le calendrier julien
 +
function fun.julianToGregorian( year, month, day )
 +
return fun.julianDayToGregorian( fun.julianDayJulian( year, month, day ) )
 +
end
 +
 
 +
---
 +
-- calcul d'une date dans le calendrier julien à partir d'une date dans le calendrier grégorien
 +
function fun.gregorianToJulian( year, month, day )
 +
year = tonumber(year)
 +
if month then month = tonumber(month) else month = 6 end --prend une valeur centrale pour donner un best "guess"
 +
if day then day = tonumber(day) else day = 15 end
 +
return fun.julianDayToJulian( fun.julianDay( year, month, day ) )
 +
end
 +
 
 +
 
 +
 
 +
---
 +
-- erreurModuleData affiche  d'un message d'erreur si le Module:Langue/Data n'a pas été chargé correctement,
 +
-- pour la page de discussion de la base de donnée et ceux qui veulent surveiller cette page.
 +
function fun.erreurModuleData()
 +
local success, resultat = pcall ( mw.loadData, 'Module:Date/Data' )
 +
if success == false then
 +
local message = [[<strong class="error">Le chargement du module Date/Data génère une erreur : </strong><br />%s<br />
 +
 
 +
<span class="error">Cette erreur doit être corrigée au plus vite car des milliers de page ne s'affichent pas correctement</span>
 +
]]
 +
return string.format( message, resultat )
 +
end
 +
end
 +
 
 +
---
 +
-- checkDataCat génère des liens vers les pages annuelles, mensuelles et d'éphémérides liè aux
 +
-- catégories du Module:Date/Data. La date la plus ancienne dépend de 'aucun' et 'seul[1]'
 +
-- Paramètres :
 +
-- 1 : la catégorie. Il y aura une section par qualificatif de cette catégorie.
 +
-- mois : oui pour avoir les liens vers les pages mensuelles et éphémérides (4 jours dans l'année)
 +
-- alias : pour avoir des lien pour les alias en plus des qualificatif
 +
function fun.checkDataCat( frame )
 +
local category = trim(frame.args[1])
 +
local monthLinks = frame .args.mois == 'oui'
 +
local alias = frame.args.alias == 'oui'
 +
local dataLink = mw.loadData( 'Module:Date/Data' )
 +
local wikiList =  TableBuilder.new()
 +
local currentYear = tonumber( os.date( '%Y' ) )
 +
local columns = '<div style="-moz-column-width:5em;-webkit-column-width:5em;column-width:5em;-moz-column-gap:1em;-webkit-column-gap:1em;column-gap:1em;text-align:left;">'
 +
local newSection
 +
if monthLinks then
 +
newSection = '\n\n== %s ==\n\n=== Années ===\n' .. columns
 +
else
 +
newSection ='\n\n== %s ==\n' .. columns
 +
end
 +
for field, dataField in pairs( dataLink ) do
 +
-- boucle sur tous les qualificatif ayant pour catégorie le premier paramère
 +
if dataField.cat == category or ( category == 'cat' and dataField.cat == field ) then
 +
local monthInitialYear, initialYear
 +
-- définition de l'année à partir de laquelle on va tester toutes les année / mois
 +
if dataField.qualificatif == field or ( category == 'cat' and dataField.cat == field ) then
 +
if dataField.annee and dataField.annee.aucun and dataField.annee.aucun < currentYear then
 +
local aucun = ( dataField.annee.seul and dataField.annee.seul[1] ) or dataField.annee.aucun
 +
initialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), currentYear - 50 )
 
else
 
else
break
+
initialYear = currentYear - 50
 +
end
 +
if dataField.mois and tonumber( dataField.mois.aucun ) and ( tonumber( dataField.mois.aucun ) < currentYear ) then
 +
local aucun = dataField.mois.aucun
 +
monthInitialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), currentYear - 8 )
 +
else
 +
monthInitialYear = currentYear - 8
 +
end
 +
elseif alias then
 +
-- si le paramètre alias est défini on teste aussi tous les alias, sinon ils sont ignorés
 +
initialYear = currentYear - 50
 +
monthInitialYear = currentYear - 8
 +
end
 +
 +
-- création de l'ensembles des liens
 +
if initialYear then
 +
-- ajout de lien vers les pages annuelles de l'année en court + 5 jusqu'à initialYear
 +
wikiList.insert( string.format( newSection, field ) )
 +
local fieldLink = ' ' .. field
 +
if category == 'cat' then
 +
fieldLink = ' ' .. dataField.qualificatif
 +
end
 +
for year = ( currentYear + 5 ), initialYear, -1  do
 +
wikiList.insert( '\n* [[' .. year .. fieldLink ..'|' .. year .. ']]' )
 +
end
 +
wikiList.insert( '\n</div>' )
 +
 +
if monthLinks then
 +
-- insertstion de liens vers les mois de l'année en court + 1 jusqu'à monthInitialYear
 +
wikiList.insert( '\n\n=== Mois ==='  )
 +
local month, sep
 +
for year = ( currentYear + 1 ), monthInitialYear, -1  do
 +
wikiList.insert( '\n* ' .. year .. ' : ' )
 +
sep = ' • '
 +
for j = 1, 12 do
 +
month = ucfirst( liste_mois[j][1] ) .. ' '
 +
if j == 12 then sep = ''
 +
end
 +
wikiList.insert( '[[' .. month .. year .. ' ' .. fieldLink .. '|' .. month .. ']]' .. sep )
 +
end
 +
end
 +
 +
-- insertion de quelques date pour tester les éphémérides
 +
wikiList.insert( '\n\n=== Jours ==='  )
 +
wikiList.insert( '\n* [[1er janvier ' .. fieldLink .. ']]' )
 +
wikiList.insert( '\n* [[14 mars ' .. fieldLink .. ']]' )
 +
wikiList.insert( '\n* [[22 juin ' .. fieldLink .. ']]' )
 +
wikiList.insert( '\n* [[3 septembre ' .. fieldLink .. ']]' )
 
end
 
end
 
end
 
end
datestr = datestr:gsub( yearStr1, yearStr2 )
 
--datestr = string.format('%s (%s, %s)', datestr, yearStr1, yearStr2 )
 
 
end
 
end
 
end
 
end
 
 
-- html formating and tagging of date string
+
return table.concat( wikiList )
if class ~= '' then
+
end
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
+
 
datestr = DateHtmlTags:format(class, timeStamp, datestr)
+
--[[
 +
  Cette fonction retourne "CET" ou "CEST" selon que dans la pseudo-timezone en cours
 +
    c'est l'heure d'été ou l'heure d'hiver.
 +
  Cette fonction n'a de sens a priori que pour des modèles utilisés en Europe
 +
 
 +
  Paramètre optionnel non nommé : "sans lien" : retourne le texte CET/CEST. sinon
 +
    retourne ce même texte avec un wikilien vers les articles correspondant
 +
--]]
 +
function fun.CEST(frame)
 +
-- option : ne pas créer de wikilien
 +
local opt = mw.text.trim(frame.args[1] or frame:getParent().args[1] or "")
 +
-- on récupère l'information dans la zone courante
 +
local t = mw.getContentLanguage():formatDate("I", nil, true)
 +
 +
if (t == "1") then  -- heure d'été
 +
if (opt == "sans lien") then
 +
return "CEST"
 +
elseif (opt == "décalage") then
 +
return "2"
 +
else
 +
return "[[Heure d'été d'Europe centrale|CEST]]"
 +
end
 +
else  -- heure d'hiver (ou autre zone où ça ne s'applique pas)
 +
if (opt == "sans lien") then
 +
return "CET"
 +
elseif (opt == "décalage") then
 +
return "1"
 +
else
 +
return "[[Heure normale d'Europe centrale|CET]]"
 +
end
 
end
 
end
return datestr
 
 
end
 
end
  
return p
+
return fun

Latest revision as of 05:18, 9 May 2015

Documentation for this module may be created at Module:Date/doc

local fun = {}

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

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

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


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

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

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

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

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

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


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

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

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


---
-- émule le modèle {{m|Date}}.
-- Paramètres :
--		1 : jour (numéro ou "1er"). optionnel, si absent pas de jour
--		2 : mois (en toutes lettres)
--		3 : année (nombre)
--		4 : optionnel, spécialité de l'année
--		Comportement spécial ("truc à deux balles au lieu d'utiliser un
--		paramètre nommé du genre "sans année=oui"...") : si 1 est vide
--		mais que le reste est complet → on n'affiche pas l'année
function fun.modeleDate( frame )
	local args = Outils.extractArgs( frame )
	
	-- séparation des paramètres jour, mois et année si nécessaire
	local test, resultat = fun.validationJourMoisAnnee( args )
	if not test then
		local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
		if namespaceCategorisation[ mw.title.getCurrentTitle().namespace ] and 
			not Outils.notEmpty( args.nocat ) then
			return resultat .. '[[Catégorie:Page utilisant le modèle date avec une syntaxe erronée]]'
		else
			return resultat
		end
	end
	local annee, mois, numMois, jour = resultat.annee, resultat.mois, resultat.numMois, resultat.jour
	local decalage, qualificatif = resultat.decalage, resultat.qualificatif
	
	if ( annee or mois or jour ) == nil then
		return
	end
	
	-- on traite l'age, naissance et mort
	local age = trim( args['âge'] or args['age'] )
	age = age and  fun.age( annee, numMois, jour )
	local naissance = trim( args.naissance )
	local mort = trim( args.mort )
	
	-- on traite le calendrier
	local gannee, gmois, gjour = annee, numMois, jour        -- date suivant le calendrier grégorien pour <time>
	local jannee, jmois, jjour = annee, mois, jour   -- servira éventuellement à a affiché la date selon le calendrier julien
	local julien2, julien3 = nil, nil                        -- servira éventuellement à a affiché des parenthèses
	local julien = trim( string.lower( args.julien or '' ) )
	if annee and jour then
		local amj = annee * 10000 + numMois * 100 + jour
		if amj < 15821014 then
			if annee > 0 then
				gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
			else
				-- calendrier grégorien proleptique avec année 0.
				gannee, gmois, gjour = fun.julianToGregorian( annee + 1, numMois, jour )
			end
		elseif julien == 'oui' then
			gannee, gmois, gjour = fun.julianToGregorian( annee, numMois, jour )
			annee, mois, jour = gannee, liste_mois[gmois][1], gjour
		end
	else
		if annee and annee < 0 then
			gannee = gannee + 1
		end
	end
	
	
	-- on génère le résultat
	
	-- Déclarations des variables
	local wikiListe = TableBuilder.new()   -- reçois le texte affiché pour chaque paramètre
	local iso = TableBuilder.new()         -- reçois le format date ISO de ce paramètre
	
	local dataQualificatif, dataCat
	if not args.nolinks then
		dataQualificatif = dataLiens[qualificatif or '']
		if type( dataQualificatif ) ~= 'table' then
			-- si le qualifiquatif n'est pas dans la base de donnée, on crée une table minimum,
			-- qui imposera un test sur l'annee, mais considère qu'il n'y a pas de lien sur le jour ou le mois
			dataQualificatif = { qualificatif = ' ' .. qualificatif, annee = { } }
		end
		dataCat = dataLiens[dataQualificatif.cat]
		if type( dataCat ) ~= 'table' or dataCat == dataQualificatif then
			dataCat = { qualificatif = '' }
		end
	end
	local function wikiLien( lien, texte )
		if lien == texte then 
			return '[[' .. texte .. ']]'
		else
			return '[[' .. lien .. '|' .. texte .. ']]'
		end	
	end
	
	-- Date julienne
	if jjour ~= jour then
		if jjour == 1 then
			jjour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
		end
		if jannee ~= annee then
			julien3 = '(<abbr class=abbr title="selon le calendrier julien">' .. jjour .. ' ' .. jmois .. ' ' .. jannee .. '</abbr>)'
		else
			julien2 = '(<abbr class=abbr title="selon le calendrier julien">' .. jjour .. ' ' .. jmois .. '</abbr>)'
		end
	end
	

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

		
	-- l'age
	if type( age ) == 'number' and age >= 0 and ( not naissance or age < 120 ) then
		if age == 0 then
			age = '(moins d\'un&nbsp;an)'
		elseif age == 1 then
			age = '(1&nbsp;an)'
		else
			age = '(' .. age .. '&nbsp;ans)'
		end
	else
		age = false
	end
	
	-- compilation du résultat
	local wikiTexte = wikiListe.concat( ' ' )
	local isoTexte = iso.concat( '-' )
	
	-- On ajoute un peu de sémantique.
	local wikiHtml = mw.html.create( '' )
	
	local dateHtml = wikiHtml:tag( 'time' )
			:wikitext( wikiTexte )
	if wikiTexte:match( ' ' ) then
		dateHtml:addClass( 'nowrap' )
	end
	if isoTexte ~= wikiTexte then
		dateHtml:attr( 'datetime', isoTexte )
	end
	if not args.nolinks then
		dateHtml:addClass( 'date-lien' )
	end
	if naissance then 
		dateHtml:addClass( 'bday' )
	elseif mort then
		dateHtml:addClass( 'dday' )
	end
	if age then
		wikiHtml:wikitext( ' ' )
				:tag( 'span' )
					:addClass( 'noprint')
					:wikitext( age )
					:done()
	end
	
	return tostring( wikiHtml )
end

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


---
-- voir émule le modèle:Inscription date
-- la détection des arguments permet d'utilisé la fonction depuis un modèle, depuis invoke, ou depuis une autre fonction.
-- pour facilité l'écriture de lua, annee (sans accent) est accepté lors de l'appel depuis lua.
function fun.inscriptionDate( frame )
	local args = Outils.extractArgs( frame )
	local annee = Outils.notEmpty( args['année'], args.annee, args.year )
	if annee then
		if annee:match( '^%-?%d+$' ) then
			-- si l'année est correctement renseigné, on essaye de trouver le mois
			local mois = Outils.notEmpty( args.mois, args.month, args.saison )
			mois = string.lower(fun.determinationMois( mois ) or fun.valideSaison( mois ) or mois or '')
			local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
			local t, jma = fun.validationJourMoisAnnee( jour, mois, annee )
			if t then
				return fun.modeleDate{ jour, mois, annee, nolinks=true }
			else
				local date = { jour }
				table.insert( date, mois )
				table.insert( date, annee )
				return '<time class="nowrap" datevalue="' .. annee .. '">' .. table.concat( date, ' ' ) .. '</time>'
			end
		else
			return fun.inscriptionDate{ date = annee }
		end
	else
		-- si annee n'est pas précisé, on utilise la paramètre date
		local date = Outils.validTextArg( args, 'date' )
		if date then
			date = date:lower()
			local t, jma = fun.separationJourMoisAnnee( date )
			if t and ( fun.determinationMois( jma.mois ) or fun.valideSaison( jma.mois ) ) then 
				args['année'] = jma.annee
				jma.nolinks = true
				jma.nocat = true
				return fun.modeleDate( jma )
			else	
				-- date non reconnue, on essaye Month day, year
				local mois, jour, annee = mw.ustring.match( date, '^([%a]+)%s*(%d%d?)[,%s]+(%d+)$' )
				if fun.determinationMois( mois ) or fun.valideSaison( mois ) then
					return fun.modeleDate{ jour, mois, annee, nolinks=true }
				end
			end
			return date
		end
	end
	return ''
end

---
-- la fonction dateISO renvoie un date au format aaaa-mm-jj (sans liens)
-- l'année peut être sous la forme 2013 ou [[2013 en litérature|2013]]
-- le mois peut être en lettre ou en chiffres
-- le jour peut être sous la forme '05', '{{1er}}' ou 'vendredi 13'
function fun.dateISO( frame )
	local args = Outils.extractArgs( frame )
	local annee = Outils.notEmpty( args['année'], args.annee, args.year, args.date )
	-- extraction de l'année
	if type( annee ) == 'string' then
		annee = ( tonumber( annee )	-- match '2013'
				or string.match ( annee, '%D(%d%d%d%d)%D' ) -- match  '[[2013 en musique|2013]]'
				or string.match ( annee, '%D(%d%d%d%d)$' )  -- match '17 septembre 2013'
				or string.match ( annee, '^(%d%d%d%d)%D' )  -- match '2013-09-17'
		)
	end
	annee = tonumber( annee )
	
	-- le format de date iso est défini suivant le calendrier grégorien.
	-- Avant l'année 1583 la date est calendrier est probablement du calendrier julien,
	-- donc autant s'abstenir.
	if annee and annee > 1582  then
		local mois = Outils.notEmpty( args.mois, args.month )
		-- num mois trouve le numéro du mois, qu'il soit numérique ou texte, complet ou abrégé.
		local nomMois, numMois = fun.determinationMois( mois )
		if numMois then
			mois = '-' .. string.sub( '0' .. numMois, -2 )
			
			local jour = Outils.notEmpty( args.jour, args.day, args['quantième'] )
			if type( jour ) == 'string' then
				jour = tonumber( jour ) or tonumber( string.match ( jour, '%d+') )
			end
			jour = tonumber( jour )
			if jour and jour <= liste_mois[numMois].nJour then
				jour = '-' .. string.sub( '0' .. jour, -2 )
				return annee .. mois .. jour
			else
				return annee .. mois
			end
		else
			return tostring( annee )
		end
	end
end

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

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

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

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

-- Conversion d'un nombre en chiffres romains
function fun.toRoman(number)
	local n = math.floor(number)
	local letters = {"I","V","X","L","C","D","M","",""}
	local pattern = {"","0","00","000","01","1","10","100","1000","02"}
	local result = ""
	if(n<=0 or n>=4000) then
		result = "---"
	else
		for i=1,7,2 do
			p = pattern[n%10 + 1]
			for j=0,2 do
				p = string.gsub(p,tostring(j),letters[i+j])
			end
			result = p .. result
			n = math.floor(n/10)
		end
	end
	return result
end

-- Conversion et affichage d'une date dans le calendrier républicain
function fun.dateRepublicain(frame)
	local pframe = frame:getParent()
	local arguments = pframe.args
	return fun.formatRepCal(fun.do_toRepCal(arguments))
end

---
-- Calcul d'une date dans le calendrier républicain
-- On suppose que les années 4n+3 sont sextiles (3, 7, 11...)
function fun.do_toRepCal(arguments)
	local yr = tonumber(arguments.year or arguments[1]) or 2000
	-- rang absolu du jour demandé, le jour 0 étant le 22 septembre 1792 (1er jour de l'an I)
	local repDays = fun.do_dayRank(arguments) + fun.do_daysBetween{1792,yr} - fun.do_dayRank{1792,9,22}
	local repYear = math.floor((repDays+731)/365.25) - 1
	local repDayRank = repDays - 365*(repYear-1) - math.floor(repYear/4)
	local repMonth, repDay = math.floor(repDayRank/30)+1, (repDayRank%30)+1
	return {repYear, repMonth, repDay}
end

---
-- Formatage d'une date selon le calendrier républicain
-- Usage : fun.formatRepCal{année,mois,jour}
function fun.formatRepCal(arguments)
	local months = {"Vendémiaire","Brumaire","Frimaire","Nivôse","Pluviôse","Ventôse","Germinal","Floréal","Prairial","Messidor","Thermidor","Fructidor"}
	local extras = {"de la vertu","du génie","du travail","des récompenses","de l'opinion","de la révolution"}
	local result = ""
	if(arguments[2] < 13) then
		result = result .. tostring(arguments[3]) .. "&nbsp;" .. months[arguments[2]]
	else
		result = result .. "jour " .. extras[arguments[3]]
	end
	result = result .. " de l'an " .. fun.toRoman(arguments[1])
	return result
end

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

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

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

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

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

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

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

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



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

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

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

--[[
  Cette fonction retourne "CET" ou "CEST" selon que dans la pseudo-timezone en cours
    c'est l'heure d'été ou l'heure d'hiver.
  Cette fonction n'a de sens a priori que pour des modèles utilisés en Europe
  
  Paramètre optionnel non nommé : "sans lien" : retourne le texte CET/CEST. sinon
    retourne ce même texte avec un wikilien vers les articles correspondant
--]]
function fun.CEST(frame)
	-- option : ne pas créer de wikilien
	local opt = mw.text.trim(frame.args[1] or frame:getParent().args[1] or "")
	-- on récupère l'information dans la zone courante
	local t = mw.getContentLanguage():formatDate("I", nil, true)
	
	if (t == "1") then  -- heure d'été
		if (opt == "sans lien") then
			return "CEST"
		elseif (opt == "décalage") then
			return "2"
		else
			return "[[Heure d'été d'Europe centrale|CEST]]"
		end
	else  -- heure d'hiver (ou autre zone où ça ne s'applique pas)
		if (opt == "sans lien") then
			return "CET"
		elseif (opt == "décalage") then
			return "1"
		else
			return "[[Heure normale d'Europe centrale|CET]]"
		end
	end
end

return fun