« Module:Date complexe » : différence entre les versions
Apparence
Modèle:Infobox>Oliv0 m précision minute = 13 |
m 147 versions importées |
||
| (74 versions intermédiaires par 11 utilisateurs non affichées) | |||
| Ligne 1 : | Ligne 1 : | ||
-- luacheck: globals mw, no max line length | |||
-- TODO: améliorer les synergies avec Module:Date (gestion par module:Date de dates sans lien et de "XIe siècle en astronautique" | -- 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 datemodule = require 'Module:Date' | ||
local linguistic = require 'Module:Linguistique' | local linguistic -- = require 'Module:Linguistique' -- chargé uniquement si nécessaire | ||
local roman = require 'Module:Romain' | local roman -- = require 'Module:Romain' -- chargé uniquement si nécessaire | ||
local p = {} | local p = {} | ||
| Ligne 19 : | Ligne 21 : | ||
second = 14, | second = 14, | ||
} | } | ||
local suffixeAvJC = ' <abbr class="abbr nowrap" title="avant Jésus-Christ">av. J.-C.</abbr>' | |||
local function vowelfirst(str) | local function vowelfirst(str) | ||
linguistic = linguistic or require 'Module:Linguistique' | |||
return linguistic.vowelfirst(str) | 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 | |||
if params and params.withpreposition then | |||
return p.atdate(obj, params) | |||
else | |||
return p.simplestring(obj, params) | |||
end | |||
elseif obj.type == 'rangeobject' then | |||
return p.daterange(obj.begin, obj.ending, params) | |||
end | |||
end | end | ||
local function setprecision(obj, maxprecision) | local function setprecision(obj, maxprecision) | ||
if ( | 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 | 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 | if maxprecision then | ||
maxprecision = tonumber(maxprecision) or numericprecision[maxprecision] | maxprecision = tonumber(maxprecision) or numericprecision[maxprecision] | ||
| Ligne 39 : | Ligne 92 : | ||
local function bigDate(year, precision) -- TODO : gestion de la précision | local function bigDate(year, precision) -- TODO : gestion de la précision | ||
local val, unit = 0, "" | local val, unit = 0, "" | ||
if year > 999999999 then | if year > 999999999 then | ||
| Ligne 48 : | Ligne 100 : | ||
val = year / 1000000 | val = year / 1000000 | ||
end | end | ||
val = | val = mw.getContentLanguage():formatNum(val) | ||
return val .. unit | return val .. unit | ||
end | end | ||
local function milleniumString(millenium, era, hideera) | local function milleniumString(millenium, era, hideera) | ||
roman = roman or require 'Module:Romain' | |||
local str = roman.toRoman(millenium) .. '<sup>e</sup> millénaire' | local str = roman.toRoman(millenium) .. '<sup>e</sup> millénaire' | ||
if era == '-' and (not hideera) then | if era == '-' and (not hideera) then | ||
str = str .. | str = str .. suffixeAvJC | ||
end | end | ||
return str | return str | ||
| Ligne 62 : | Ligne 114 : | ||
local function centuryString(century, era, hideera) | local function centuryString(century, era, hideera) | ||
roman = roman or require 'Module:Romain' | |||
local str = roman.toRoman(century) .. '<sup>e</sup> siècle' | local str = roman.toRoman(century) .. '<sup>e</sup> siècle' | ||
if era == '-' and (not hideera) then | if era == '-' and (not hideera) then | ||
str = str .. | str = str .. suffixeAvJC | ||
end | end | ||
return | return str | ||
end | end | ||
local function decadeString(decade, era, hideera) | local function decadeString(decade, era, hideera, withlink) | ||
local str = 'années ' .. decade .. '0' | local target | ||
local str = 'années ' .. decade | |||
if decade ~= '0' then str = str .. '0' end | |||
if era == '-' and (not hideera) then | if era == '-' and (not hideera) then | ||
if withlink then target = str .. ' av. J.-C.' end | |||
str = str .. suffixeAvJC | |||
end | |||
if withlink then | |||
if target then str = target .. '|' .. str end | |||
str = '[[' .. str .. ']]' | |||
end | end | ||
return str | return str | ||
| Ligne 84 : | Ligne 144 : | ||
return dateobject | return dateobject | ||
end | 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 (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 | ||
return dateobject.day | if displayformat.precision and numericprecision[displayformat.precision] < 11 then | ||
return '' | |||
else | |||
return tostring(dateobject.day) | |||
end | |||
end | end | ||
| Ligne 93 : | Ligne 163 : | ||
displayformat = {} | displayformat = {} | ||
end | end | ||
local linktopic = displayformat.linktopic | local linktopic = displayformat.linktopic or displayformat.link | ||
local nolinks | local nolinks | ||
if linktopic == '-' then | if linktopic == '-' then | ||
| Ligne 101 : | Ligne 171 : | ||
local str | local str | ||
local precision = setprecision(dateobject, displayformat.precision) | local precision = setprecision(dateobject, displayformat.precision) | ||
local year = tonumber(dateobject.year) | |||
-- formats gérés par ce module | -- formats gérés par ce module | ||
if year then | |||
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 | |||
if year == 0 then century = 1 end | |||
str = centuryString(century, era, hideera) | |||
elseif precision == 8 then | |||
local decade = tostring(math.floor(year/10)) | |||
str = decadeString(decade, era, hideera, not nolinks) | |||
end | |||
if str then | |||
return str | |||
end | |||
end | end | ||
-- formats gérés par Module:Date | -- formats gérés par Module:Date | ||
if year and (era == '-') then | if year and (era == '-') then | ||
year = 0 - year | year = 0 - year | ||
end | end | ||
local month, day | local month, day | ||
if precision > 9 then | if precision > 9 then | ||
month = dateobject.month | month = dateobject.month | ||
| Ligne 136 : | Ligne 209 : | ||
end | end | ||
end | end | ||
local | local argAvJC -- équivalent de hideera pour modeleDate | ||
if displayformat.hideera then | if displayformat.hideera then | ||
argAvJC = 'non' | |||
end | end | ||
str = datemodule.modeleDate{jour = day, mois = month, annee = year, qualificatif = linktopic, nolinks = nolinks, avJC = | str = datemodule.modeleDate{jour = day, mois = month, annee = year, qualificatif = linktopic, nolinks = nolinks, avJC = argAvJC, liens = true} | ||
return str or '' | return str or '' | ||
end | end | ||
local function fromToNow( | 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) | 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 | if vowelfirst(datestr) then -- suppose l'absence de lien interne | ||
return "depuis l'" .. datestr | return "depuis l'" .. datestr | ||
| Ligne 152 : | Ligne 225 : | ||
return "depuis le " .. datestr | return "depuis le " .. datestr | ||
end | end | ||
end | |||
if (precision == 8) then -- on dit "depuis les" pour les décennies ("années ...") | |||
return "depuis les " .. datestr | |||
end | end | ||
return "depuis " .. datestr | return "depuis " .. datestr | ||
end | end | ||
local function fromdate(d, displayformat) | local function fromdate(d, displayformat) -- retourne "à partir de date" en langage naturel | ||
displayformat = displayformat or {} | displayformat = displayformat or {} | ||
local precision = setprecision(d, displayformat.precision) | local precision = setprecision(d, displayformat.precision) | ||
| Ligne 163 : | Ligne 239 : | ||
return datestr -- par exemple pour les classements MH, juste afficher la date de début | return datestr -- par exemple pour les classements MH, juste afficher la date de début | ||
end | end | ||
if displayformat and displayformat.textformat == ' | if displayformat and displayformat.textformat == 'short' then | ||
return datestr .. ' | return datestr .. ' – ' -- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret | ||
end | end | ||
if displayformat.stilltrue then | if p.before(os.date("!%Y-%m-%dT%TZ"), d) and (displayformat.stilltrue ~= "?") and (displayformat.stilltrue ~= false) then | ||
fromToNow( | return fromToNow(datestr, precision) | ||
end | end | ||
if (precision >= 11) or (precision == 7) or (precision == 6) | 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 | return 'à partir du ' .. datestr | ||
end | end | ||
if (precision == 10) and (vowelfirst(datemodule. | if (precision == 10) and (vowelfirst(datemodule.determinationMois(d.month))) then | ||
return "à partir d'" .. datestr | return "à partir d'" .. datestr | ||
end | |||
if (precision == 8) then -- on dit "à partir des" pour les décennies | |||
return 'à partir des ' .. datestr | |||
end | end | ||
return 'à partir de ' .. datestr | return 'à partir de ' .. datestr | ||
end | end | ||
local function upto(d, displayformat) | local function upto(d, displayformat) -- retourne "jusqu'à date' en langage naturel | ||
displayformat = displayformat or {} | displayformat = displayformat or {} | ||
local datestring = p.simplestring(d, displayformat) | local datestring = p.simplestring(d, displayformat) | ||
local precision = setprecision(d, displayformat.precision) | local precision = setprecision(d, displayformat.precision) | ||
if | 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) then --dates avec jour | |||
return "jusqu'au " .. datestring | return "jusqu'au " .. datestring | ||
elseif (precision | elseif (precision == 10) then --mois | ||
return "jusqu'à " .. datestring | return "jusqu'à " .. datestring | ||
elseif (precision == 9) then --années | |||
return "jusqu'en " .. datestring | return "jusqu'en " .. datestring | ||
elseif (precision == 8) then --décennies | |||
return "jusqu'aux " .. datestring | |||
elseif (precision >= 6) then --siècles et millénaires | |||
return "jusqu'au " .. datestring | |||
else --million d'années ? | |||
return "jusqu'au " .. datestring | |||
end | |||
end | |||
function p.atdate(d, displayformat) -- retourne "à la date' en langage naturel | |||
displayformat = displayformat or {} | |||
local datestring = p.simplestring(d, displayformat) | |||
local precision = setprecision(d, displayformat.precision) | |||
if (precision >= 11) then --dates avec jour | |||
return "le " .. datestring | |||
elseif (precision >= 9) then --années et mois | |||
return "en " .. datestring | |||
elseif (precision == 8) then --décennies | |||
return "dans les " .. datestring | |||
elseif (precision >= 6) then --siècles et millénaires | |||
return "au " .. datestring | |||
else --million d'années ? | |||
return "dans le " .. datestring | |||
end | end | ||
end | end | ||
local function fromuntillong(startstr, endstr, era, | local function fromuntillong(startstr, endstr, era, startprecision, endprecision) | ||
-- on dit "du 3 au 14 janvier" mais "de | -- on dit "du 3 au 14 janvier" mais "de septembre à octobre" | ||
if | local longstartstr | ||
if startprecision >= 11 then -- >= day | |||
longstartstr = "du " .. startstr | |||
elseif startprecision == 8 then -- == décennie ("années") | |||
longstartstr = "des " .. startstr | |||
else | else | ||
if vowelfirst(startstr) then | if vowelfirst(startstr) then | ||
longstartstr = "d'" .. startstr | |||
else | else | ||
longstartstr = "de " .. startstr | |||
end | end | ||
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 | end | ||
| Ligne 209 : | Ligne 326 : | ||
end | end | ||
local era = endpoint.era | local era = endpoint.era | ||
local sameera | local sameera = false | ||
if startpoint.era == endpoint.era then | if startpoint.era == endpoint.era then | ||
sameera = true | sameera = true | ||
end | end | ||
if (endpoint.year == startpoint.year) then | if sameera and (endpoint.year == startpoint.year) then | ||
startpoint.year = nil | startpoint.year = nil | ||
if (startpoint.month == endpoint.month) then | if (startpoint.month == endpoint.month) then | ||
| Ligne 229 : | Ligne 346 : | ||
local precision = setprecision(endpoint, displayformat.precision) or 9 | local precision = setprecision(endpoint, displayformat.precision) or 9 | ||
startpoint = p.simplestring(startpoint, displayformat) | |||
endpoint = p.simplestring(endpoint, displayformat) | |||
if not (startpoint or endpoint) then | if not (startpoint or endpoint) then | ||
return nil | return nil | ||
| Ligne 237 : | Ligne 354 : | ||
if not endpoint then | if not endpoint then | ||
if precision <= 10 then | if precision <= 10 then | ||
return "après " .. | return "après " .. startpoint | ||
else | else | ||
return "après le " .. | return "après le " .. startpoint | ||
end | end | ||
end | end | ||
if not startpoint then | if not startpoint then | ||
if precision <= 10 then | if precision <= 10 then | ||
return "avant " .. | return "avant " .. endpoint | ||
else | else | ||
return "avant le " .. | return "avant le " .. endpoint | ||
end | end | ||
end | end | ||
-- analyse les paramètres pour éviter les redondances | |||
local startpoint, endpoint, era, displayformat, sameera = removeclutter(startpoint, endpoint, precision, displayformat) | local era, sameera | ||
startpoint, endpoint, era, displayformat, sameera = removeclutter(startpoint, endpoint, precision, displayformat) | |||
local startstr, endstr = | local startstr, endstr = p.simplestring(startpoint, displayformat), p.simplestring(endpoint, displayformat) | ||
displayformat.hideera = true | displayformat.hideera = true | ||
if (startstr == '') or (startstr == endstr) then | if (startstr == '') or (startstr == endstr) then | ||
if (not sameera) then | if (not sameera) then | ||
displayformat.hideera = false --sinon c'est incompréhensible | displayformat.hideera = false -- sinon c'est incompréhensible | ||
return p.simplestring(endpoint, displayformat) | return p.simplestring(endpoint, displayformat) | ||
end | end | ||
return endstr | return endstr | ||
end | end | ||
-- pour éviter les tournures répétitives comme | -- pour éviter les tournures répétitives comme "du 13 septembre 2006 au 18 septembre 2006" | ||
if era == "-" then | if era == "-" then | ||
era = | era = suffixeAvJC | ||
else | else | ||
era = "" | era = "" | ||
end | end | ||
if precision <= | if precision <= 10 then | ||
return "entre " .. startstr .. " et " .. endstr | return "entre " .. startstr .. " et " .. endstr .. era | ||
else | else | ||
return "entre le " .. startstr .. " et le " .. endstr | return "entre le " .. startstr .. " et le " .. endstr .. era | ||
end | end | ||
end | end | ||
| Ligne 280 : | Ligne 398 : | ||
local function fromuntil(startpoint, endpoint, displayformat) | local function fromuntil(startpoint, endpoint, displayformat) | ||
displayformat = displayformat or {} | displayformat = displayformat or {} | ||
local precision = setprecision(endpoint, displayformat.precision) | local startprecision = setprecision(startpoint, displayformat.precision) | ||
local endprecision = setprecision(endpoint, displayformat.precision) | |||
-- analyse les paramètres pour éviter les redondances | |||
local era, sameera | |||
startpoint, endpoint, era, displayformat, sameera = removeclutter(startpoint, endpoint, endprecision, displayformat) | |||
local | local hideera = displayformat.hideera | ||
displayformat.hideera = true -- pour les chaînes intermédiaires | displayformat.hideera = true -- pour les chaînes intermédiaires | ||
local startstr, endstr = | local startstr, endstr = p.simplestring(startpoint, displayformat), p.simplestring(endpoint, displayformat) | ||
if (startstr == '') or (startstr == endstr) then | if (startstr == '') or (startstr == endstr) then | ||
displayformat.hideera = | displayformat.hideera = hideera -- on va faire une chaîne simple, on reprend donc le format initialement demandé | ||
if (not sameera) then | if (not sameera) then | ||
displayformat.hideera = false --sinon c'est incompréhensible | displayformat.hideera = false -- sinon c'est incompréhensible | ||
end | end | ||
return p.simplestring(endpoint, displayformat) | return p.simplestring(endpoint, displayformat) | ||
end | end | ||
-- pour éviter les tournures répétitives comme | -- pour éviter les tournures répétitives comme "du 13 septembre 2006 au 18 septembre 2006" | ||
local hasStartera = false | |||
if era == '-' then | if era == '-' then | ||
era = | era = suffixeAvJC | ||
else | else | ||
era = '' | era = '' | ||
if not (sameera == nil) and not sameera then | |||
startstr = startstr .. suffixeAvJC | |||
hasStartera = true | |||
end | |||
end | end | ||
if displayformat.textformat == 'long' then | if displayformat.textformat == 'long' then | ||
return fromuntillong(startstr, endstr, era, | return fromuntillong(startstr, endstr, era, startprecision, endprecision) | ||
elseif (type( | 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 | return startstr .. ' -<wbr> ' .. endstr .. era | ||
else | else | ||
return startstr .. '-<wbr>' .. endstr .. era | return startstr .. '-<wbr>' .. endstr .. era | ||
end | end | ||
end | end | ||
function p.daterange(startpoint, endpoint, displayformat) | function p.daterange(startpoint, endpoint, displayformat) | ||
local result | |||
if startpoint and endpoint then | if startpoint and endpoint then | ||
result = fromuntil(startpoint, endpoint, displayformat) | |||
elseif startpoint then | elseif startpoint then | ||
result = fromdate(startpoint, displayformat) | |||
elseif endpoint then | elseif endpoint then | ||
result = upto(endpoint, displayformat) | |||
else | 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 | end | ||
return result | |||
end | end | ||
| Ligne 348 : | Ligne 470 : | ||
local function splitISO(str) | local function splitISO(str) | ||
str = mw.text.trim(str) | |||
local era, year, month, day | local era, year, month, day | ||
era = string.sub(str, 1, 1) | era = string.sub(str, 1, 1) | ||
| Ligne 356 : | Ligne 479 : | ||
year, month, day = f(), f(), f() | year, month, day = f(), f(), f() | ||
return era, year, month, day | return era, year, month, day | ||
end | end | ||
function p.splitDate(orig, calendar) | function p.splitDate(orig, calendar) | ||
if not orig then | if not orig then | ||
| Ligne 368 : | Ligne 491 : | ||
return error("bad datatype for date, string expected, got " .. type(orig)) | return error("bad datatype for date, string expected, got " .. type(orig)) | ||
end | end | ||
local era, y, m, d = splitWDdate(orig) | local era, y, m, d = splitWDdate(orig) | ||
if not era then | if not era then | ||
era, y, m, d = splitISO(orig) | era, y, m, d = splitISO(orig) | ||
| Ligne 374 : | Ligne 497 : | ||
y, m, d = tonumber(y or 1), tonumber(m or 1), tonumber(d or 1) | y, m, d = tonumber(y or 1), tonumber(m or 1), tonumber(d or 1) | ||
return {day = d, month = m, year = y, era = era | return {day = d, month = m, year = y, era = era, type = 'dateobject', calendar = calendar} | ||
end | end | ||
function p.before(a, b) -- return true if b is before a or if at least one of a or b is missing | 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) | a = p.splitDate(a) | ||
b = p.splitDate(b) | b = p.splitDate(b) | ||
if (not a) or (not b) then | if (not a) or (not b) then | ||
return true | return true | ||
end | end | ||
local order = { | local order = {'year', 'month', 'day'} | ||
for i, j in | 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 | return true | ||
elseif b[j] | 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 | return false | ||
end | end | ||
end | end | ||
return true | return true | ||
end | end | ||
return p | return p | ||
Dernière version du 22 février 2026 à 00:16
La documentation pour ce module peut être créée à Module:Date complexe/doc
-- luacheck: globals mw, no max line length
-- 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 suffixeAvJC = ' <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
if params and params.withpreposition then
return p.atdate(obj, params)
else
return p.simplestring(obj, params)
end
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 .. suffixeAvJC
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 .. suffixeAvJC
end
return str
end
local function decadeString(decade, era, hideera, withlink)
local target
local str = 'années ' .. decade
if decade ~= '0' then str = str .. '0' end
if era == '-' and (not hideera) then
if withlink then target = str .. ' av. J.-C.' end
str = str .. suffixeAvJC
end
if withlink then
if target then str = target .. '|' .. str end
str = '[[' .. str .. ']]'
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 or displayformat.link
local nolinks
if linktopic == '-' then
nolinks = true
end
local str
local precision = setprecision(dateobject, displayformat.precision)
local year = tonumber(dateobject.year)
-- formats gérés par ce module
if year then
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
if year == 0 then century = 1 end
str = centuryString(century, era, hideera)
elseif precision == 8 then
local decade = tostring(math.floor(year/10))
str = decadeString(decade, era, hideera, not nolinks)
end
if str then
return str
end
end
-- formats gérés par Module:Date
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 argAvJC -- équivalent de hideera pour modeleDate
if displayformat.hideera then
argAvJC = 'non'
end
str = datemodule.modeleDate{jour = day, mois = month, annee = year, qualificatif = linktopic, nolinks = nolinks, avJC = argAvJC, 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 == 'short' then
return' – ' .. datestring -- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret
end
if (precision >= 11) then --dates avec jour
return "jusqu'au " .. datestring
elseif (precision == 10) then --mois
return "jusqu'à " .. datestring
elseif (precision == 9) then --années
return "jusqu'en " .. datestring
elseif (precision == 8) then --décennies
return "jusqu'aux " .. datestring
elseif (precision >= 6) then --siècles et millénaires
return "jusqu'au " .. datestring
else --million d'années ?
return "jusqu'au " .. datestring
end
end
function p.atdate(d, displayformat) -- retourne "à la date' en langage naturel
displayformat = displayformat or {}
local datestring = p.simplestring(d, displayformat)
local precision = setprecision(d, displayformat.precision)
if (precision >= 11) then --dates avec jour
return "le " .. datestring
elseif (precision >= 9) then --années et mois
return "en " .. datestring
elseif (precision == 8) then --décennies
return "dans les " .. datestring
elseif (precision >= 6) then --siècles et millénaires
return "au " .. datestring
else --million d'années ?
return "dans le " .. 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
startpoint = p.simplestring(startpoint, displayformat)
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 era, sameera
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 = suffixeAvJC
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 era, sameera
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 = suffixeAvJC
else
era = ''
if not (sameera == nil) and not sameera then
startstr = startstr .. suffixeAvJC
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