Aller au contenu

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

De Wreck
Modèle:Infobox>Od1n
retrait d'un "require" n'étant pas utilisé (aurait été nécessaire si on avait fait usage de linguistic.ucfirst ici, mais c'est bon puisqu'il est déjà utilisé dans la fonction parente p.daterange, qui couvre p.fromuntil, p.fromdate et p.upto)
m 147 versions importées
 
(23 versions intermédiaires par 3 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 20 : Ligne 22 :
}
}


local avJC = '<abbr class="abbr nowrap" title="avant Jésus-Christ"> av. J.-C.</abbr>'
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
end
Ligne 30 : Ligne 32 :
--[[ transforme un snak en un nouvel objet utilisable par des fonctions comme p.setprecision
--[[ 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}
{type = 'dateobject', timestamp = str, era = '+' ou '-', year = number, month = number, day = number, calendar = calendar}
]]--  
]]--
if not params then
if not params then
params = {}
params = {}
end
end
 
local newobj = p.splitDate(orig.time, orig.calendarmodel)
local newobj = p.splitDate(orig.time, orig.calendarmodel)
 
newobj.precision = params.precision or orig.precision
newobj.precision = params.precision or orig.precision
newobj.type = 'dateobject'
newobj.type = 'dateobject'
Ligne 45 : Ligne 47 :
--[[
--[[
objet comportant un timestamp pour le classement chronologique et deux dateobject (begin et ending)
objet comportant un timestamp pour le classement chronologique et deux dateobject (begin et ending)
]]--  
]]--
local timestamp
local timestamp
if begin then
if begin then
Ligne 57 : Ligne 59 :
function p.objectToText(obj, params)
function p.objectToText(obj, params)
if obj.type == 'dateobject' then
if obj.type == 'dateobject' then
return p.simplestring(obj, params)
if params and params.withpreposition then
return p.atdate(obj, params)
else
return p.simplestring(obj, params)
end
elseif obj.type == 'rangeobject' then
elseif obj.type == 'rangeobject' then
return p.daterange(obj.begin, obj.ending, params)
return p.daterange(obj.begin, obj.ending, params)
Ligne 97 : Ligne 103 :
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 .. avJC
str = str .. suffixeAvJC
end
end
return str
return str
Ligne 109 : 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 .. avJC
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.|' .. str .. avJC
if withlink then target = str .. ' av. J.-C.' end
str = str .. suffixeAvJC
end
end
return '[[' .. str .. ']]'
if withlink then
if target then str = target .. '|' .. str end
str = '[[' .. str .. ']]'
end
return str
end
end


Ligne 132 : Ligne 144 :
return dateobject
return dateobject
end
end
 
-- si le date object comporte déjà le texte souhaité on le retourne
-- si le date object comporte déjà le texte souhaité on le retourne
if(dateobject.string) then return dateobject.string end
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 149 : 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 157 : 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 194 : 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, liens = true}
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(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 "depuis le" 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 211 : Ligne 226 :
end
end
end
end
if (precision == 8) then -- ont dit "depuis les" pour les décennies ("années ...")
if (precision == 8) then -- on dit "depuis les" pour les décennies ("années ...")
return "depuis les " .. datestr
return "depuis les " .. datestr
end
end
Ligne 217 : Ligne 232 :
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 227 : 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) and (displayformat.stilltrue ~= "?") and (displayformat.stilltrue ~= false) then
if p.before(os.date("!%Y-%m-%dT%TZ"), d) and (displayformat.stilltrue ~= "?") and (displayformat.stilltrue ~= false) then
return fromToNow(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
Ligne 236 : Ligne 251 :
return "à partir d'" .. datestr
return "à partir d'" .. datestr
end
end
if (precision == 8) then -- ont dit "à partir des" pour les décennies
if (precision == 8) then -- on dit "à partir des" pour les décennies
return 'à partir des ' .. datestr
return 'à partir des ' .. datestr
end
end
Ligne 242 : Ligne 257 :
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
elseif (precision == 8) then
elseif (precision == 9) then --années
return "jusqu'en " .. datestring
elseif (precision == 8) then --décennies
return "jusqu'aux " .. datestring
return "jusqu'aux " .. datestring
else
elseif (precision >= 6) then --siècles et millénaires
return "jusqu'en " .. datestring
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, startprecision, endprecision)
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"
local longstartstr
local longstartstr
if startprecision >= 11 then -- >= day
if startprecision >= 11 then -- >= day
Ligne 279 : Ligne 312 :
local longendstr
local longendstr
if endprecision >= 11 then -- >= day
if endprecision >= 11 then -- >= day
longendstr = " au " .. endstr .. era
longendstr = " au " .. endstr .. era
elseif endprecision == 8 then -- == décennie ("années")
elseif endprecision == 8 then -- == décennie ("années")
longendstr = " aux " .. endstr .. era
longendstr = " aux " .. endstr .. era
else
else
longendstr = " à " .. endstr .. era
longendstr = " à " .. endstr .. era
end
end
return longstartstr .. longendstr
return longstartstr .. longendstr
Ligne 313 : 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 321 : 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 era, sameera
startpoint, endpoint, era, displayformat, sameera = removeclutter(startpoint, endpoint, precision, displayformat)


local startstr, endstr = p.simplestring(startpoint, displayformat), p.simplestring(endpoint, 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 septembre 2006"
-- pour éviter les tournures répétitives comme "du 13 septembre 2006 au 18 septembre 2006"
if era == "-" then
if era == "-" then
era = avJC
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
Ligne 367 : Ligne 401 :
local endprecision = setprecision(endpoint, 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, endprecision, 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 septembre 2006"
-- pour éviter les tournures répétitives comme "du 13 septembre 2006 au 18 septembre 2006"
local hasStartera = false
local hasStartera = false
if era == '-' then
if era == '-' then
era = avJC
era = suffixeAvJC
else
else
era = ''
era = ''
if not (sameera == nil) and not sameera then
if not (sameera == nil) and not sameera then
startstr = startstr .. avJC
startstr = startstr .. suffixeAvJC
hasStartera = true
hasStartera = true
end
end
end
end
if displayformat.textformat == 'long' then
if displayformat.textformat == 'long' then
return fromuntillong(startstr, endstr, era, startprecision, endprecision)
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
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
Ligne 415 : Ligne 450 :
end
end
if result and displayformat and displayformat.ucfirst and displayformat.ucfirst ~= '-' then
if result and displayformat and displayformat.ucfirst and displayformat.ucfirst ~= '-' then
linguistic = require 'Module:Linguistique'
linguistic = linguistic or require 'Module:Linguistique'
result = linguistic.ucfirst(result)
result = linguistic.ucfirst(result)
end
end
Ligne 456 : 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 466 : 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
Ligne 501 : Ligne 536 :


function p.equal(a, b, precision)
function p.equal(a, b, precision)
a = p.splitDate(a)  
a = p.splitDate(a)
b = p.splitDate(b)
b = p.splitDate(b)


if type(precision) == "string" then
if type(precision) == "string" then
precision = tonumber(precision) or numericprecision[mw.text.trim( precision)]
precision = tonumber(precision) or numericprecision[mw.text.trim(precision)]
end
end


Ligne 516 : Ligne 551 :
end
end


if a.era and b.era and (b.era ~= a.era) then
if a.era and b.era and (b.era ~= a.era) then
return false
return false
end
end


 
if (precision >= 11) then
if (precision >= 11) then
if a.day and b.day and (b.day ~= a.day) then
if a.day and b.day and (b.day ~= a.day) then
Ligne 538 : Ligne 572 :
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