Aller au contenu

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

De Wreck
Modèle:Infobox>Zolo
Aucun résumé des modifications
m 147 versions importées
 
(143 versions intermédiaires par 13 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
local datemodule = require('Module:Date')
-- luacheck: globals mw, no max line length
local linguistic = require('Module:Linguistique')
 
-- 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 p = {}


Ligne 13 : Ligne 18 :
day = 11,
day = 11,
hour = 12,
hour = 12,
minute = 12,
minute = 13,
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
end


local function guessprecision(obj) -- précision des dates qui ne viennent pas de Module:Wikidata/Dates
function p.dateObject(orig, params)
local prec = 0
--[[ transforme un snak en un nouvel objet utilisable par des fonctions comme p.setprecision
for i, j in pairs(obj) do
{type = 'dateobject', timestamp = str, era = '+' ou '-', year = number, month = number, day = number, calendar = calendar}
if (numericprecision[i] or 0) > prec then
]]--
prec = numericprecision[i]
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
end
end  
elseif obj.type == 'rangeobject' then
return prec
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
end


local function centuryString(centurynumber)
local function bigDate(year, precision) -- TODO : gestion de la précision
return mw.getCurrentFrame():expandTemplate{ title = 'Nombre en romain', args = { centurynumber + 1 }} .. '<sup>e</sup> siècle'
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
end


local function centuryString(centurynumber)
local function centuryString(century, era, hideera)
return mw.getCurrentFrame():expandTemplate{ title = 'Nombre en romain', args = { centurynumber + 1 }} .. '<sup>e</sup> millénaire'
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
end


local function decadeString(decadenumber)
local function decadeString(decade, era, hideera, withlink)
return '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 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
end


function p.simplestring(dateobject)  
function p.simplestring(dateobject, displayformat)
 
-- transforme un object date ponctuel en texte
-- 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
-- les dates de type ISO devraient passer par Module:Date, mais il faut pouvoir désactiver les liens
local yearstr, monthstr, daystr= tostring(dateobject.year), tostring(dateobject.month), tostring(dateobject.day)
if type(dateobject) == 'string' or type(dateobject) == 'nil' then
return dateobject
end


-- adaptation à mw.formatDate en attendant de passer par Module:Date
-- si le date object comporte déjà le texte souhaité on le retourne
if yearstr then
if dateobject.string then
while #yearstr < 4 do
return dateobject.string
yearstr = 0 .. yearstr
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
end
end


local era = dateobject.era
local era = dateobject.era
local precision = dateobject.precision or guessprecision(dateobject)
 
if not displayformat then
if precision == 6 then
displayformat = {}
local century = tostring(math.floor(dateobject.year/100))
end
str = centuryString(century)
local linktopic = displayformat.linktopic or displayformat.link
elseif precision == 7 then
local nolinks
local century = tostring(math.floor(dateobject.year/100))
if linktopic == '-' then
str = centuryString(century)
nolinks = true
elseif precision == 8 then
end
local decade = tostring(math.floor(dateobject.year/10))  
 
str = decadestring(decade)
local str
elseif precision == 9 then
local precision = setprecision(dateobject, displayformat.precision)
str = yearstr
local year = tonumber(dateobject.year)
elseif precision == 10 then
 
str =mw.language.new('fr'):formatDate('F Y', yearstr .. '-' .. monthstr )
-- formats gérés par ce module
if dateobject.year < 1000 then -- enlève les zéros en trop
if year then
str = string.gsub(str, '0', '')
if year > 999999 then -- grosses dates pour l'astronomie, la paléontologie
return bigDate(year, precision)
end
end
elseif precision == 11  then
 
str = mw.language.new('fr'):formatDate('j F Y', yearstr .. '-' .. monthstr .. '-' .. daystr)
local hideera = displayformat.hideera
if day == 1 then -- ajustement "1 janvier" -> 1er janvier
 
str = string.gsub(t, '1', "1<sup>er</sup>", 1) -- remplacer "1 janvier" par "1er janvier"
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
end
if dateobject.year < 1000 then  
if str then
str = string.gsub(str, '0', '')
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
end
end
if era == '-' then
 
str = str .. ' av. J.-C.'
local argAvJC -- équivalent de hideera pour modeleDate
if displayformat.hideera then
argAvJC = 'non'
end
end
return str or 'date invalide'
str = datemodule.modeleDate{jour = day, mois = month, annee = year, qualificatif = linktopic, nolinks = nolinks, avJC = argAvJC, liens = true}
return str or ''
end
end


local function fromdate(d) -- retourne "à partir de date" en langage naturel
local function fromToNow(datestr, precision) -- retourne "depuis" plutôt que "à partir de" quand ce n'est pas terminé
local precision = d.precision or guessprecision(d)
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
local datestr = p.simplestring(d)
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


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
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
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
else
if vowelfirst(str) then
if vowelfirst(startstr) then
return "à partir d'" .. datestr
longstartstr = "d'" .. startstr
else
else
return 'à partir de ' .. datestr
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
end
end
return startpoint, endpoint, era, displayformat, sameera
end
end


local function upto(d) -- retourne "jusqu'à date' en langage naturel
function p.between(startpoint, endpoint, displayformat)
local datestring = p.simplestring(d)
displayformat = displayformat or {}
local precision = d.precision or guessprecision(d)
local precision = setprecision(endpoint, displayformat.precision) or 9
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
startpoint = p.simplestring(startpoint, displayformat)
elseif (precision >= 9) then
endpoint = p.simplestring(endpoint, displayformat)
return "jusqu'à " .. datestring
 
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
else
return "jusqu\'en " .. datestring
return "entre le " .. startstr .. " et le " .. endstr .. era
end
end
end
end


local function fromuntil(startpoint, endpoint)
local function fromuntil(startpoint, endpoint, displayformat)
local precision = endpoint.precision or guessprecision(endpoint) -- may need 2 precisions for start and end dates
displayformat = displayformat or {}
local startstr = p.simplestring(startpoint)
local startprecision = setprecision(startpoint, displayformat.precision)
local endstr = p.simplestring(endpoint)
local endprecision = setprecision(endpoint, displayformat.precision)
-- à améliorer pour éviter les tournures répétitives comme "du 13 septembre 2006 au 18 september 2006"
 
-- analyse les paramètres pour éviter les redondances
-- on dit "du 3 au 14 janvier" mais "de septembe à octobre
 
if precision >= 11 then -- >= day
local era, sameera
return "du " .. startstr .. " au " ..  endstr
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
else
if vowelfirst(startstr) then
era = ''
return "d'" .. startstr .. " à ".. endstr
if not (sameera == nil) and not sameera then
else
startstr = startstr .. suffixeAvJC
return "de " .. startstr .. " à " .. endstr
hasStartera = true
end
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
end
end


function p.fuzzydate(dateobjet)
function p.daterange(startpoint, endpoint, displayformat)
local str = p.simplestring(dateobject)
local result
return "vers " .. str
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
end


function p.daterange(startpointobject, endpointobject)  
function p.duration(start, ending)
if startpointobject and endpointobject then
if (not start) or (not ending) then
return fromuntil(startpointobject, endpointobject)
return nil -- ?
elseif startpointobject then
end
return fromdate(startpointobject)
return datemodule.age(start.year, start.month, start.day, ending.year, ending.month, ending.day)
elseif endpointobject then
end
return upto(endpointobject)
 
else
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
return nil
end
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
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