Module:Date complexe
Aller à la navigation
Aller à la recherche
La documentation pour ce module peut être créée à Module:Date complexe/doc
-- TODO: améliorer les synergies avec Module:Date (gestion par module:Date de dates sans lien et de "XIe siècle en astronautique" local datemodule = require 'Module:Date' local linguistic -- = require 'Module:Linguistique' -- chargé uniquement si nécessaire local roman -- = require 'Module:Romain' -- chargé uniquement si nécessaire local p = {} local numericprecision = { -- convertir les précisions en valeurs numériques = à celles utilisées par Wikidata gigayear = 0, megayear = 3, millenium = 6, century = 7, decade = 8, year = 9, month = 10, day = 11, hour = 12, minute = 13, second = 14, } local avJC = '<abbr class="abbr nowrap" title="avant Jésus-Christ"> av. J.-C.</abbr>' local function vowelfirst(str) linguistic = linguistic or require 'Module:Linguistique' return linguistic.vowelfirst(str) end function p.dateObject(orig, params) --[[ transforme un snak en un nouvel objet utilisable par des fonctions comme p.setprecision {type = 'dateobject', timestamp = str, era = '+' ou '-', year = number, month = number, day = number, calendar = calendar} ]]-- if not params then params = {} end local newobj = p.splitDate(orig.time, orig.calendarmodel) newobj.precision = params.precision or orig.precision newobj.type = 'dateobject' return newobj end function p.rangeObject(begin, ending, params) --[[ objet comportant un timestamp pour le classement chronologique et deux dateobject (begin et ending) ]]-- local timestamp if begin then timestamp = begin.timestamp else timestamp = ending.timestamp end return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'} end function p.objectToText(obj, params) if obj.type == 'dateobject' then return p.simplestring(obj, params) elseif obj.type == 'rangeobject' then return p.daterange(obj.begin, obj.ending, params) end end local function setprecision(obj, maxprecision) local precision if type(obj) == "string" then precision = tonumber(obj) elseif type(obj) == "number" then precision = obj elseif type(obj) == "table" then precision = tonumber(obj.precision) or numericprecision[obj.precision] end if not precision then precision = 0 end -- maxprecision, surtout pour données Wikidata quand on veut afficher avec moins de précision que l'input (par exemple afficher seulement l'année) if maxprecision then maxprecision = tonumber(maxprecision) or numericprecision[maxprecision] end if maxprecision then return math.min(precision, maxprecision) end return precision end local function bigDate(year, precision) -- TODO : gestion de la précision local val, unit = 0, "" if year > 999999999 then unit = " [[giga|G]][[Année julienne|a]]" val = year / 1000000000 elseif year > 999999 then unit = " [[méga|M]][[Année julienne|a]]" val = year / 1000000 end val = mw.getContentLanguage():formatNum(val) return val .. unit end local function milleniumString(millenium, era, hideera) roman = roman or require 'Module:Romain' local str = roman.toRoman(millenium) .. '<sup>e</sup> millénaire' if era == '-' and (not hideera) then str = str .. avJC end return str end local function centuryString(century, era, hideera) roman = roman or require 'Module:Romain' local str = roman.toRoman(century) .. '<sup>e</sup> siècle' if era == '-' and (not hideera) then str = str .. avJC end return str end local function decadeString(decade, era, hideera) local str = 'années ' .. decade .. '0' if era == '-' and (not hideera) then str = str .. ' av. J.-C.|' .. str .. avJC end return '[[' .. str .. ']]' end function p.simplestring(dateobject, displayformat) -- transforme un object date ponctuel en texte -- les dates de type ISO devraient passer par Module:Date, mais il faut pouvoir désactiver les liens if type(dateobject) == 'string' or type(dateobject) == 'nil' then return dateobject end -- si le date object comporte déjà le texte souhaité on le retourne if dateobject.string then return dateobject.string end if (not dateobject.year) and (not dateobject.month) and dateobject.day then -- si seul le jour est passé, par exemple à cause de removeclutter, le format n'est pas pris en charge par module:Date if displayformat.precision and numericprecision[displayformat.precision] < 11 then return '' else return tostring(dateobject.day) end end local era = dateobject.era if not displayformat then displayformat = {} end local linktopic = displayformat.linktopic local nolinks if linktopic == '-' then nolinks = true end local str local precision = setprecision(dateobject, displayformat.precision) -- formats gérés par ce module local year = tonumber(dateobject.year) or 0 if year > 999999 then -- grosses dates pour l'astronomie, la paléontologie return bigDate(year, precision) end local hideera = displayformat.hideera if precision == 6 then local millenium = math.floor((year - 1)/1000) + 1 str = milleniumString(millenium, era, hideera) elseif precision == 7 then local century = math.floor((year - 1)/100) + 1 str = centuryString(century, era, hideera) elseif precision == 8 then local decade = tostring(math.floor(year/10)) str = decadeString(decade, era, hideera) end if str then return str end -- formats gérés par Module:Date local year = dateobject.year if year and (era == '-') then year = 0 - year end local month, day if precision > 9 then month = dateobject.month if precision > 10 then day = dateobject.day end end local avJC -- équivalent de hideera pour modeleDate if displayformat.hideera then avJC = 'non' end str = datemodule.modeleDate{jour = day, mois = month, annee = year, qualificatif = linktopic, nolinks = nolinks, avJC = avJC, liens = true} return str or '' end local function fromToNow(datestr, precision) -- retourne "depuis" plutôt que "à partir de" quand ce n'est pas terminé if (precision >= 11) or (precision == 7) or (precision == 6) then -- on dit "depuis le" pour les dates avec jour, les siècles, les millénaires if vowelfirst(datestr) then -- suppose l'absence de lien interne return "depuis l'" .. datestr else return "depuis le " .. datestr end end if (precision == 8) then -- on dit "depuis les" pour les décennies ("années ...") return "depuis les " .. datestr end return "depuis " .. datestr end local function fromdate(d, displayformat) -- retourne "à partir de date" en langage naturel displayformat = displayformat or {} local precision = setprecision(d, displayformat.precision) local datestr = p.simplestring(d, displayformat) if displayformat and displayformat.textformat == 'minimum' then return datestr -- par exemple pour les classements MH, juste afficher la date de début end if displayformat and displayformat.textformat == 'short' then return datestr .. ' – ' -- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret end if p.before(os.date("!%Y-%m-%dT%TZ"), d) and (displayformat.stilltrue ~= "?") and (displayformat.stilltrue ~= false) then return fromToNow(datestr, precision) end if (precision >= 11) or (precision == 7) or (precision == 6) then -- on dit "à partir du" pour les dates avec jour, les siècles, les millénaires return 'à partir du ' .. datestr end if (precision == 10) and (vowelfirst(datemodule.determinationMois(d.month))) then return "à partir d'" .. datestr end if (precision == 8) then -- on dit "à partir des" pour les décennies return 'à partir des ' .. datestr end return 'à partir de ' .. datestr end local function upto(d, displayformat) -- retourne "jusqu'à date' en langage naturel displayformat = displayformat or {} local datestring = p.simplestring(d, displayformat) local precision = setprecision(d, displayformat.precision) if displayformat and displayformat.textformat == 'infobox' then return ' – '.. datestring -- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret end if displayformat and displayformat.textformat == 'short' then return' – ' .. datestring -- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret end if (precision >= 11) or (precision == 7) or (precision == 6) then -- on dit "jusqu'au" pour les dates avec jour, et pour les siècles return "jusqu'au " .. datestring elseif (precision > 9) then return "jusqu'à " .. datestring elseif (precision == 8) then return "jusqu'aux " .. datestring else return "jusqu'en " .. datestring end end local function fromuntillong(startstr, endstr, era, startprecision, endprecision) -- on dit "du 3 au 14 janvier" mais "de septembre à octobre" local longstartstr if startprecision >= 11 then -- >= day longstartstr = "du " .. startstr elseif startprecision == 8 then -- == décennie ("années") longstartstr = "des " .. startstr else if vowelfirst(startstr) then longstartstr = "d'" .. startstr else longstartstr = "de " .. startstr end end local longendstr if endprecision >= 11 then -- >= day longendstr = " au " .. endstr .. era elseif endprecision == 8 then -- == décennie ("années") longendstr = " aux " .. endstr .. era else longendstr = " à " .. endstr .. era end return longstartstr .. longendstr end local function removeclutter(startpoint, endpoint, precision, displayformat) -- prépare à rendre la date plus jolie : "juin 445 av-JC-juillet 445 av-JC -> juin-juillet 445-av-JC" if (type(startpoint) ~= 'table') or (type(endpoint) ~= 'table') then return startpoint, endpoint, precision, displayformat end local era = endpoint.era local sameera = false if startpoint.era == endpoint.era then sameera = true end if sameera and (endpoint.year == startpoint.year) then startpoint.year = nil if (startpoint.month == endpoint.month) then startpoint.month = nil if (startpoint.day == endpoint.day) then startpoint.day = nil end end end return startpoint, endpoint, era, displayformat, sameera end function p.between(startpoint, endpoint, displayformat) displayformat = displayformat or {} local precision = setprecision(endpoint, displayformat.precision) or 9 local startpoint = p.simplestring(startpoint, displayformat) local endpoint = p.simplestring(endpoint, displayformat) if not (startpoint or endpoint) then return nil end if not endpoint then if precision <= 10 then return "après " .. startpoint else return "après le " .. startpoint end end if not startpoint then if precision <= 10 then return "avant " .. endpoint else return "avant le " .. endpoint end end -- analyse les paramètres pour éviter les redondances local startpoint, endpoint, era, displayformat, sameera = removeclutter(startpoint, endpoint, precision, displayformat) local startstr, endstr = p.simplestring(startpoint, displayformat), p.simplestring(endpoint, displayformat) displayformat.hideera = true if (startstr == '') or (startstr == endstr) then if (not sameera) then displayformat.hideera = false -- sinon c'est incompréhensible return p.simplestring(endpoint, displayformat) end return endstr end -- pour éviter les tournures répétitives comme "du 13 septembre 2006 au 18 septembre 2006" if era == "-" then era = avJC else era = "" end if precision <= 10 then return "entre " .. startstr .. " et " .. endstr .. era else return "entre le " .. startstr .. " et le " .. endstr .. era end end local function fromuntil(startpoint, endpoint, displayformat) displayformat = displayformat or {} local startprecision = setprecision(startpoint, displayformat.precision) local endprecision = setprecision(endpoint, displayformat.precision) -- analyse les paramètres pour éviter les redondances local startpoint, endpoint, era, displayformat, sameera = removeclutter(startpoint, endpoint, endprecision, displayformat) local hideera= displayformat.hideera displayformat.hideera = true -- pour les chaînes intermédiaires local startstr, endstr = p.simplestring(startpoint, displayformat), p.simplestring(endpoint, displayformat) if (startstr == '') or (startstr == endstr) then displayformat.hideera = hideera -- on va faire une chaîne simple, on reprend donc le format initialement demandé if (not sameera) then displayformat.hideera = false -- sinon c'est incompréhensible end return p.simplestring(endpoint, displayformat) end -- pour éviter les tournures répétitives comme "du 13 septembre 2006 au 18 septembre 2006" local hasStartera = false if era == '-' then era = avJC else era = '' if not (sameera == nil) and not sameera then startstr = startstr .. avJC hasStartera = true end end if displayformat.textformat == 'long' then return fromuntillong(startstr, endstr, era, startprecision, endprecision) elseif (type(startprecision) == "number") and (startprecision > 9) or (type(endprecision) == "number") and (endprecision > 9) or hasStartera then -- si les date contiennent des mois ou jours, ou si il y a un era avant, il vaut mieux un espace return startstr .. ' -<wbr> ' .. endstr .. era else return startstr .. '-<wbr>' .. endstr .. era end end function p.daterange(startpoint, endpoint, displayformat) local result if startpoint and endpoint then result = fromuntil(startpoint, endpoint, displayformat) elseif startpoint then result = fromdate(startpoint, displayformat) elseif endpoint then result = upto(endpoint, displayformat) else result = nil end if result and displayformat and displayformat.ucfirst and displayformat.ucfirst ~= '-' then linguistic = linguistic or require 'Module:Linguistique' result = linguistic.ucfirst(result) end return result end function p.duration(start, ending) if (not start) or (not ending) then return nil -- ? end return datemodule.age(start.year, start.month, start.day, ending.year, ending.month, ending.day) end local function splitWDdate(str) -- depuis datavalue.value.time de Wikidata, fonctionnerait aussi en utilisant simplement splitISO local pattern = "(%W)(%d+)%-(%d+)%-(%d+)" local era, year, month, day = str:match(pattern) return era, year, month, day end local function splitISO(str) str = mw.text.trim(str) local era, year, month, day era = string.sub(str, 1, 1) if tonumber(era) then era = '+' end local f = string.gmatch(str, '%d+') year, month, day = f(), f(), f() return era, year, month, day end function p.splitDate(orig, calendar) if not orig then return nil end if type(orig) == 'table' then return orig end if type(orig) ~= 'string' then return error("bad datatype for date, string expected, got " .. type(orig)) end local era, y, m, d = splitWDdate(orig) if not era then era, y, m, d = splitISO(orig) end y, m, d = tonumber(y or 1), tonumber(m or 1), tonumber(d or 1) return {day = d, month = m, year = y, era = era, type = 'dateobject', calendar = calendar} end function p.before(a, b) -- return true if b is before a or if at least one of a or b is missing a = p.splitDate(a) b = p.splitDate(b) if (not a) or (not b) then return true end local order = {'year', 'month', 'day'} if a['era'] == '+' then if b['era'] == '+' then for i, j in ipairs(order) do if b[j] < a[j] then return true elseif b[j] > a[j] then return false end end else -- b - return true end else -- a - if b['era'] == '+' then return false else -- b - for i, j in ipairs(order) do if b[j] > a[j] then return true elseif b[j] < a[j] then return false end end end end return true end function p.equal(a, b, precision) a = p.splitDate(a) b = p.splitDate(b) if type(precision) == "string" then precision = tonumber(precision) or numericprecision[mw.text.trim(precision)] end if not precision then precision = 11 -- day by default ? end if (not a) or (not b) then return true end if a.era and b.era and (b.era ~= a.era) then return false end if (precision >= 11) then if a.day and b.day and (b.day ~= a.day) then return false end end if (precision >= 10) then if a.month and b.month and (b.month ~= a.month) then return false end end if (precision >= 9) then if a.year and b.year and (b.year ~= a.year) then return false end end return true end return p