Module:Date

De Lagny-sur-Marne Wiki
Révision datée du 30 août 2013 à 23:33 par Zebulon84 (discussion) (retouche de la modification précédente)
Aller à : navigation, rechercher

La documentation pour ce module peut être créée à Module:Date/doc

local fun = {}

local TableBuilder = require( 'Module:TableBuilder' )

-- génère une erreur
function fun.erreurDate(texte)
    return '<span class="error">' .. (texte or "''aucune erreur indiquée''") .. "</span>"
end

-- nettoie un paramètre non nommé (vire les espaces au début et à la fin)
function fun.nettoie(texte)
    if (texte == nil or texte == "" or type(texte) ~= "string") then
        return ""
    end
    return mw.text.trim(texte)
end

-- fonction "#ifexist", en attendant que mw.title soit intégré et fournisse cette fonctionnalité
function fun.ifexist(page)
    if (type(page) == "string" and page ~= "") then
        return mw.title.new( page ).exists
    end
end


-- Ajoute autant de caractère c que nécessaire à la chaîne str pour arriver à la ongeur length
function fun.prepend(str, c, length)
    local lenstr = mw.ustring.len( str )
    if lenstr < length then
        str = mw.ustring.rep( c, length - lenstr)
    end
    return str
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 function trim( texte )
    if type( texte ) == "string" and texte ~= "" then
        return mw.text.trim( texte )
    end
end

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 },
}

-- 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( 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

-- determinationMois trouve le numéro du mois et son nom, 
-- à partir de son nom, de son numéro ou d'une expression mathématique.
-- l'objet frame est facultatif, mais permet de tester si mois est une expression type '2+3'
function fun.determinationMois( mois, frame )
    local num, nom
    if tonumber( mois ) then
        num = math.floor( math.fmod( tonumber( mois ) - 1, 12 )  ) + 1
    elseif type( mois ) == "string" then
        nom, num = fun.valideMois( mois )
        if nom == nil and frame and frame.callParserFunction then
            -- essai de détermination d'un nombre avec le parser #expr de Mediawiki.
            -- la fonction s'appelle sans l'objet frame pour ne pas tourner en boucle.
            nom, num = fun.determinationMois( frame:callParserFunction( '#expr', mois ) )
        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 data == nil 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
    
    if type( data.aucun ) == 'number' and annee <= (data.aucun or 0) 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 then
                    return lien
                end
            end
        end
        -- partie aucun et pas de lien => nil
        return nil
    elseif type( data.tous ) == 'table'
        and type( data.tous[1] ) == 'number' 
        and type( data.tous[2] ) == 'number'
        and annee >= data.tous[1]
        and annee <= data.tous[2]
    then
        -- l'année est dans la partie 'tous' donc on retourne le lien
        return lien
    end
    -- l'annee n'est ni dans la partie aucun, ni dans la partie tous donc il faut tester si la page existe.
    if mw.title.new( lien ).exists then
        return lien
    end
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 = frame.args or frame
    if args[1] == nil and args[3] == nil and frame.getParent then
        args = frame:getParent().args
    end
    
    -- 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 = { 1938, 2013 } }, }, parametresPage = { } }
    end
    
    local annee, mois, numMois, jour
    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
    if trim( args[1] ) == nil and fun.valideMois( trim( args[3] ) ) ~= nil then
        decalage = 1
    end
    
    -- on traite l'année
    local bannee = trim( args[3 + decalage] )
    if bannee then
        annee = tonumber( bannee )
        if annee == nil then
            -- test si l'année contient av. J.-C.
            annee = mw.ustring.match( mw.ustring.upper( bannee ), '^(%d+)%sAV%.?%s?J%.?%-?C' )
            annee = tonumber( annee )
            if annee then
                annee = 0 - annee
            else
                return erreurDate( 'Année invalide (' .. bannee .. ')' )
            end
        end
    else
        annee = nil
    end

    -- on traite le mois
    local bmois = trim (args[2 + decalage] )
    if bmois then
        mois, numMois = fun.valideMois( bmois )
        if mois == nil then 
            -- on teste si le mois est fourni sous forme numérique
            numMois = tonumber( bmois )
            mois = fun.nomDuMois( numMois )
        end
        if mois == nil then 
            return erreurDate( 'Mois invalide (' .. bmois .. ')' )
        else
        
            -- on traite le jour si présent
            local bjour = trim( args[1 + decalage] )
            if bjour then
                jour = tonumber( bjour )
                if jour == nil then
                    if bjour == '1er' 
                        or bjour == '<abbr class="abbr" title="Premier" >1<sup>er</sup></abbr>' -- correspond à {{1er}}
                    then
                        jour = 1
                    else
                        return erreurDate( 'Jour invalide (' .. bjour .. ')' )
                    end
                end
                -- on valide que le jour est correct
                if jour < 1 or jour > 31 then
                    return erreurDate( 'Jour invalide (' .. bjour .. ')' )
                elseif jour > liste_mois[numMois].nJour then
                    return erreurDate( 'Jour invalide (' .. bjour .. ' ' .. mois .. ')' )
                    -- 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
    -- else
    --    return erreurDate("Le mois est obligatoire")
    end

    -- on traite le champs optionnel
    local qualificatif = trim( args[4 + decalage] ) or dataLiens.parametresPage.qualificatif
    
    -- on traite l'age
    local age = trim( args['âge'] or args['age'] )
    age = age and  fun.age( annee, numMois, jour )
    
    -- on traite le calendrier
    local gannee, gmois, gjour = annee, numMois, jour       -- date suivant le calendrier grégorien pour <time>
    local jannee, jmois, jjour, julien = annee, mois, jour  -- servira éventuellement à a affiché la date selon le calendrier julien
    local julien1, julien2, julien3 = '', '', ''            -- servira éventuellement à a affiché des parenthèses
    local julien = trim( string.lower( args.julien or dataLiens.parametresPage.julien or '' ) )
    if annee and jour then
        local amj = annee * 10000 + numMois * 100 + jour 
        if amj < 15821014 then
            gannee, gmois, gjour = fun.jullianToGregorian( annee, numMois, jour )
        elseif julien == 'oui' then  
            gannee, gmois, gjour = fun.jullianToGregorian( annee, numMois, jour )
            annee, numMois, jour = gannee, liste_mois[gmois][1], gjour
        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 = 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
    
    -- Date julienne
    if jjour ~= jour then
        wikiListe.insert( '<abbr title="selon le calendrier julien" >' .. jjour )
        julien1 = '('
        if jannee ~= annee then
            wikiListe.insert( jmois )
            wikiListe.insert( jannee .. '</abbr>' )
            julien3 = ')'
        else
            wikiListe.insert( jmois .. '</abbr>' )
            julien2 = ')'
        end
    end 
    
    -- le jour si présent
    if jour then
        local lien = jour .. ' ' .. mois
        if jour == 1 then
            jour = '<abbr class="abbr" title="premier">1<sup>er</sup></abbr>'
        end
        if jour == 1 then
            lien = '1er ' .. mois
        end
        lien = lien .. ( dataQualificatif.jour or '' )
        wikiListe.insert( julien1 .. '[[' .. lien .. '|' .. jour .. ']]' )
        wikiListe.insert( julien1 .. '[[' .. lien .. '|' .. jour .. ' '.. mois .. ']]' .. julien2 )
        iso.insert( 1, string.sub( '0' .. gjour, -2 ) )
    end

    -- le mois
    if mois then
        local lien
        if annee then
            lien = existDate( dataQualificatif, annee, mois )
            if lien == nil and qualificatif and (jour == nil or dataQualificatif.jour == nil) then
                -- test nouveau test sans le qualificatif uniquement s'il n'y a pas d'éphémérides pour ce qualificatif.
                lien = existDate( dataLiens[''].mois, annee, mois )
            end
        end 
        if lien then
            -- s'il y a un lien on retire le lien affichant 'jour mois' pour ajouter '[[mois annee|mois']]
            wikiListe.remove()
            wikiListe.insert(  '[[' .. lien .. '|' .. mois .. ']]' .. julien2 )
        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 
        iso.insert( 1, string.sub( '0' .. gmois, -2 ) )
    end
    
    -- l'année
    if annee and decalage == 0 then -- seulement si on doit l'affichée
        local lien = existDate( dataQualificatif, annee )
        local texte = annee
        if annee < 0 then
            local annneeAvJc = 0 - annee
            lien = lien or ( annneeAvJc .. ' av. J.-C.' )
            local avJC = trim( string.lower( args.avJC or dataLiens.parametresPage.avJC or '' ) )
            if args.avJC == 'non' then
                texte = annneeAvJc
            else
                texte = annneeAvJc .. '&nbsp;<abbr class="abbr" title="' 
                    .. annneeAvJc .. ' avant Jésus-Christ">av.&nbsp;J.-C.</abbr>'
            end
        end
        lien = lien or annee
        if mois and #wikiListe == 0 then
            texte = mois .. ' ' .. texte 
        end
        wikiListe.insert(  '[[' .. lien .. '|' .. texte .. ']]' .. julien3 )
        iso.insert( 1, string.sub( '000' .. annee , -4 ) )
    end
    
    -- l'age 
        local classAge
    if type( age ) == 'number' and age >= 0 then  
        if age == 0 then
            age = '<span class="noprint"> (moins d\'1 an)</span>'
        elseif age == 1 then 
            age = '<span class="noprint"> (1 an)</span>'
        else
            age = '<span class="noprint"> (' .. age .. 'ans)</span>'
        end
        classAge = 'class="bday" '
    else
        age = ''
        classAge = ''
    end
    
    -- compilation du résultat
    local wikiText = wikiListe.concat( '&nbsp;' )
    -- On ajoute un peu de sémantique. La date doit être dans le calendrier grégorien proleptique 
    -- ce formatage ne s'applique pas avant J.C.
    if wikiText ~= '' and ( gannee == nil or gannee > 0) then    
        wikiText = '<time '.. classAge .. 'datetime="' .. iso.concat( '-' ) .. '">' .. wikiText .. '</time>'
    end
    
    return wikiText .. age
end

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

-- la fonction dateISO renvoie un date au format aaaa-mm-jj (sans liens)
-- 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 annee, mois, jour
    if frame.getParent then
        annee = frame:getParent().args ['année'] or frame.args ['année'] or frame.args.annee or ''
        mois = frame:getParent().args.mois or frame.args.mois or ''
        jour = frame:getParent().args.jour or frame:getParent().args ['quantième'] or frame.args.jour or ''
    else
        annee = frame ['année'] or frame.annee or ''
        mois = frame.mois or ''
        jour = frame.jour or ''
    end
    annee = tonumber ( annee ) or string.match ( annee, '%D(-? ?%d?%d?%d%d)%D')
    if annee then
        annee = fun.prepend ( tostring ( annee ), '0', 4 )
        local nomMois, numMois = fun.determinationMois( mois, frame )
        if numMois then
            if numMois < 10 then
                mois = '-0' .. numMois
            else
                mois = '-' .. numMois
            end
            local bjour = tonumber(jour) or tonumber( string.match ( jour, '%D?(%d?%d)%D?') )
            if bjour then
                if bjour < 10 then
                    jour = '-0' .. bjour
                else
                    jour = '-' .. bjour
                end
            else
                jour = ''
            end
        else
            mois = ''
            jour = ''
        end
        return annee .. mois .. jour
    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
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 erreurDate( "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 erreurDate("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, minute, second )
    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 + ( minute or 0 ) / 1440 + ( second or 0 ) / 86400
           - 32167.5
    return julian
end

-- calcul du jour julien à partir d'une date du calendrier julier
function fun.julianDayJulian( year, month, day, hour, minute, second )
    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 + ( minute or 0 ) / 1440 + ( second or 0 ) / 86400
           - 32205.5
    return julian
end

-- calcul d'une date dans le calendrier Grégorien à partir du jour julien
function fun.jullianDayToGregorian( jd )
    local base = math.floor( jd + 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

function fun.jullianToGregorian( year, month, day )
    return fun.jullianDayToGregorian( fun.julianDayJulian( 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

function fun.checkDataCat( frame )
    local category = frame.args[1]
    local monthLinks = frame .args[2] == 'oui'
    local all = frame.args[3] == 'oui'
    local dataLink = mw.loadData( 'Module:Date/Data' )
    local wikiList =  TableBuilder.new()
    local currentYear = tonumber( os.date( '%Y' ) )
    local newSection 
    if monthLink then 
        newSection = '\n\n== %s ==\n\n=== Années ===\n{{Colonnes|taille=5|1='
    else
        newSection ='\n\n== %s ==\n{{Colonnes|taille=5|1='
    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 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 then
                if dataField.annee and dataField.annee.aucun and dataField.annee.aucun < currentYear then
                    local aucun = dataField.annee.aucun
                    initialYear = math.min( aucun - math.ceil( (currentYear - aucun) / 4 ), 1980 )
                else 
                    initialYear = currentYear - 50
                end
                if dataField.mois and dataField.mois.aucun and 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 all then
                -- si le paramètre 2 est défini on teste aussi tous les alias depuis 1980. 
                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 ) )
                field = ' ' .. field
                for year = ( currentYear + 5 ), initialYear, -1  do
                    wikiList.insert( '\n* [[' .. year .. field ..'|' .. year .. ']]' )
                end
                wikiList.insert( '\n}}' )
                
                if monthLinks then
                    -- insertstion de liens vers les mois de l'année en court + 1 jusqu'à monthInitialYear
                    wikiList.insert( '\n\n=== Mois ==='  )
                    local month, sep
                    for year = ( currentYear + 1 ), monthInitialYear, -1  do
                        wikiList.insert( '\n* ' .. year .. ' : ' )
                        sep = ' • '
                        for j = 1, 12 do 
                            month = ucfirst( liste_mois[j][1] ) .. ' '
                            if j == 12 then sep = ''
                            end
                            wikiList.insert( '[[' .. month .. year .. ' ' .. field .. '|' .. ']]' .. sep )
                        end
                    end
                    
                    -- insertion de quelques date pour tester les éphémérides
                    wikiList.insert( '\n\n=== Jours ==='  )
                    wikiList.insert( '\n* [[1er janvier ' .. field .. ']]' )
                    wikiList.insert( '\n* [[14 mars ' .. field .. ']]' )
                    wikiList.insert( '\n* [[22 juin ' .. field .. ']]' )
                    wikiList.insert( '\n* [[3 septembre ' .. field .. ']]' )
                end
            end
        end
    end
    
    return table.concat( wikiList )
end

return fun