Aller au contenu

« Module:Date complexe » : différence entre les versions

De Wreck
Modèle:Infobox>Edelsmann
La fonction fromToNow() ne se déclenchait plus (ce qui rendait impossible l'affichage de "depuis" plutôt que "à partir de" pour les événements en cours). C'est corrigé.
m 147 versions importées
 
(56 versions intermédiaires par 7 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"


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 = require 'Module:Linguistique'
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


Ligne 48 : 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 format = require "Module:Format"
local val, unit = 0, ""
local val, unit = 0, ""
if year > 999999999 then
if year > 999999999 then
Ligne 57 : Ligne 100 :
val = year / 1000000
val = year / 1000000
end
end
val = format.do_formatnum({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'
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 .. ' av. J.-C.'
str = str .. suffixeAvJC
end
end
return str
return str
Ligne 72 : Ligne 114 :


local function centuryString(century, era, hideera)
local function centuryString(century, era, hideera)
roman = roman or require 'Module:Romain'
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 .. ' av. J.-C.'
str = str .. suffixeAvJC
end
end
return str
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
str = str .. ' av. J.-C.'
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
end
end


Ligne 95 : 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
if displayformat.precision and numericprecision[displayformat.precision] < 11 then
if displayformat.precision and numericprecision[displayformat.precision] < 11 then
Ligne 108 : 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 116 : 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
local year = tonumber( dateobject.year) or 0
if year then
if year > 999999 then -- grosses dates pour l'astronomie, la paléontologie
if year > 999999 then -- grosses dates pour l'astronomie, la paléontologie
return bigDate(year, precision)
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
 
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
-- formats gérés par Module:Date
local year = dateobject.year
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 153 : Ligne 209 :
end
end
end
end
 
local avJC -- équivalent de hideera pour modeleDate
local argAvJC -- équivalent de hideera pour modeleDate
if displayformat.hideera then
if displayformat.hideera then
avJC = 'non'
argAvJC = 'non'
end
end
str = datemodule.modeleDate{jour = day, mois = month, annee = year, qualificatif = linktopic, nolinks = nolinks, avJC = 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(d, datestr, precision) -- retourne "depuis" plutôt que "à partir de" quand c'est pas terminé
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 -- ont dit "à partir du pour les dates avec jour, les siècles, les millénaires
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 169 : 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) -- retourne "à partir de date" en langage naturel
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 183 : Ligne 242 :
return datestr .. '&nbsp;&ndash;&nbsp;' -- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret
return datestr .. '&nbsp;&ndash;&nbsp;' -- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret
end
end
if p.before ( os.date("!%Y-%m-%dT%TZ"), d) then return
if p.before(os.date("!%Y-%m-%dT%TZ"), d) and (displayformat.stilltrue ~= "?") and (displayformat.stilltrue ~= false) then
fromToNow(d, datestr, precision)
return fromToNow(datestr, precision)
end
end
if (precision >= 11) or (precision == 7) or (precision == 6) then -- ont dit "à partir du pour les dates avec jour, les siècles, les millénaires
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.determinationMois(d.month))) then
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) -- retourne "jusqu'à date' en langage naturel
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 displayformat and displayformat.textformat == 'infobox' then
return '&nbsp;&ndash;&nbsp;'.. datestring-- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret
end
if displayformat and displayformat.textformat == 'short' then
if displayformat and displayformat.textformat == 'short' then
return'&nbsp;&ndash;&nbsp;' .. datestring -- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret
return'&nbsp;&ndash;&nbsp;' .. datestring -- pour certaines infobox (footballeur par exemple), afficher date de début et un tiret
end
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
if (precision >= 11) then --dates avec jour
return "jusqu'au " .. datestring
return "jusqu'au " .. datestring
elseif (precision > 9) then
elseif (precision == 10) then --mois
return "jusqu'à " .. datestring
return "jusqu'à " .. datestring
else
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, precision)
local function fromuntillong(startstr, endstr, era, startprecision, endprecision)
-- on dit "du 3 au 14 janvier" mais "de septembe à octobre
-- on dit "du 3 au 14 janvier" mais "de septembre à octobre"
if precision >= 11 then -- >= day
local longstartstr
return "du " .. startstr .. " au " .. endstr .. era
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
return "d'" .. startstr .. " à ".. endstr .. era
longstartstr = "d'" .. startstr
else
else
return "de " .. startstr .. " à " .. endstr .. era
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 232 : 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
Ligne 252 : Ligne 346 :
local precision = setprecision(endpoint, displayformat.precision) or 9
local precision = setprecision(endpoint, displayformat.precision) or 9


local startpoint = p.simplestring(startpoint, displayformat)
startpoint = p.simplestring(startpoint, displayformat)
local endpoint = p.simplestring(endpoint, displayformat)
endpoint = p.simplestring(endpoint, displayformat)
 
if not (startpoint or endpoint) then
if not (startpoint or endpoint) then
return nil
return nil
Ligne 260 : Ligne 354 :
if not endpoint then
if not endpoint then
if precision <= 10 then
if precision <= 10 then
return "après " .. startpoint
return "après " .. startpoint
else
else
return "après le " .. startpoint
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 " .. endpoint
return "avant " .. endpoint
else
else
return "avant le " .. endpoint
return "avant le " .. endpoint
end
end
end
end


-- analyse les paramètres pour éviter les redondances
-- 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)
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
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 "du 13 septembre 2006 au 18 september 2006"
-- pour éviter les tournures répétitives comme "du 13 septembre 2006 au 18 septembre 2006"
if era == "-" then
if era == "-" then
era = " av J-C"
era = suffixeAvJC
else
else
era = ""
era = ""
end
end
 
if precision <= 10 then
if precision <= 10 then
return "entre " .. startstr .. " et " .. endstr .. " " .. era
return "entre " .. startstr .. " et " .. endstr .. era
else
else
return "entre le " .. startstr .. " et le " .. endstr .. " " .. era
return "entre le " .. startstr .. " et le " .. endstr .. era
end
end
end
end
Ligne 303 : 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
-- analyse les paramètres pour éviter les redondances
local startpoint, endpoint, era, displayformat, sameera = removeclutter(startpoint, endpoint, precision, displayformat)


local hideera= displayformat.hideera
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
displayformat.hideera = true -- pour les chaînes intermédiaires
 
local startstr, endstr = p.simplestring(startpoint, displayformat), p.simplestring(endpoint, displayformat)
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 = hideera -- on va faire une chaîne simple, on reprend donc le format initialement demandé
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 "du 13 septembre 2006 au 18 september 2006"
-- 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 = ' av J-C'
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, precision)
return fromuntillong(startstr, endstr, era, startprecision, endprecision)
elseif (type(precision) == "number") and (precision > 9) then -- si les date contiennent des mois ou jours, il vaut mieux un espace
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
function p.fuzzydate(dateobject, displayformat)
local str = p.simplestring(dateobject, displayformat)
if not str then
return nil
end
return "vers " .. str
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
return fromuntil(startpoint, endpoint, displayformat)
result = fromuntil(startpoint, endpoint, displayformat)
elseif startpoint then
elseif startpoint then
return fromdate(startpoint, displayformat)
result = fromdate(startpoint, displayformat)
elseif endpoint then
elseif endpoint then
return upto(endpoint, displayformat)
result = upto(endpoint, displayformat)
else
else
return nil
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 371 : 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 379 : 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 391 : 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 401 : Ligne 501 :


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 = {'era', 'year', 'month', 'day'}
local order = {'year', 'month', 'day'}
for i, j in pairs(order) do
if a['era'] == '+' then
if b[j] < a[j] 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] > a[j] then
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 .. '&nbsp;&ndash;&nbsp;' -- 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'&nbsp;&ndash;&nbsp;' .. 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