Módulo:Data complexa
A documentação para este módulo pode ser criada na página Módulo:Data complexa/doc
-- TODO: melhorar sinergias com o Módulo:Data (gerenciamento de módulos: Data de datas não relacionadas e "astronáutica do século XI"
local datemodule = require 'Módulo:Data'
local linguistic -- = require 'Módulo:Linguística' -- carregado apenas se necessário
local romano -- = require 'Módulo:Romano' -- carregado apenas se necessário
local p = {}
local numericprecision = { -- converter precisões em valores numéricos = para aqueles usados pelo Wikidata
gigayear = 0,
megayear = 3,
millenium = 6,
century = 7,
decade = 8,
year = 9,
month = 10,
day = 11,
hour = 12,
minute = 13,
second = 14,
}
local function vowelfirst(str)
linguistic = require 'Módulo:Linguística'
return linguistic.vowelfirst(str)
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, especialmente para dados Wikidata quando queremos mostrar com menos precisão do que a entrada (por exemplo, exibir apenas o ano)
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 : gestão da precisão
local format = require "Módulo:Formato"
local val, unidade = 0, ""
if year > 999999999 then
unidade = " [[giga|G]][[Ano juliano|a]]"
val = year / 1000000000
elseif year > 999999 then
unidade = " [[mega|M]][[Ano juliano|a]]"
val = year / 1000000
end
val = format.do_formatnum({val})
return val .. unidade
end
local function milleniumString(millenium, era, hideera)
romano = romano or require 'Módulo:Romano'
local str = romano.toRoman(millenium) .. ' milénio'
if era == '-' and (not hideera) then
str = str .. ' a.C.'
end
return str
end
local function centuryString(century, era, hideera)
romano = romano or require 'Módulo:Romano'
local str = romano.toRoman(century) .. ' século'
if era == '-' and (not hideera) then
str = str .. ' a.C.'
end
return str
end
local function decadeString(decade, era, hideera)
local str = 'década de ' .. decade .. '0'
if era == '-' and (not hideera) then
str = str .. ' a.C.'
end
return '[[' .. str .. ']]'
end
function p.simplestring(dateobject, displayformat)
-- transforma um objeto de data de ponto em texto
-- as datas do tipo ISO devem passar pelo Módulo:Data, mas você deve poder desativar os links
if type(dateobject) == 'string' or type(dateobject) == 'nil' then
return dateobject
end
if (not dateobject.year) and (not dateobject.month) and dateobject.day then -- se apenas o dia passou, por exemplo, devido a removeclutter, o formato não é suportado pelo módulo: Data
if displayformat.precision and numericprecision[displayformat.precision] < 11 then
return ''
else
return tostring(dateobject.day)
end
end
local era = dateobject.era
if not displayformat then
displayformat = {}
end
local linktopic = displayformat.linktopic
local nolinks
if linktopic == '-' then
nolinks = true
end
local str
local precision = setprecision(dateobject, displayformat.precision)
-- formatos gerados por este módulo
local year = tonumber( dateobject.year) or 0
if year > 999999 then -- grandes datas para astronomia, paleontologia
return bigDate(year, precision)
end
if precision == 6 then
local millenium = math.floor(year/1000) + 1
str = milleniumString(millenium, era, hideera)
elseif precision == 7 then
local century = math.floor(year/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
-- formatos gerados por Módulo:Data
local year = dateobject.year
if year and (era == '-') then
year = 0 - year
end
local month, day
if precision > 9 then
month = dateobject.month
if precision > 10 then
day = dateobject.day
end
end
local ac -- equivalente de hideera para modeloData
if displayformat.hideera then
ac = 'não'
end
str = datemodule.modeloData{dia = day, mes = month, ano = year, qualificativo = linktopic, nolinks = nolinks, ac = ac}
return str or ''
end
local function fromToNow(d, datestr, precision) -- devolver "de" em vez de "de" quando não tiver terminado
if (precision >= 11) or (precision == 7) or (precision == 6) then -- ter dito "desde as datas com dia, séculos, milénios
if vowelfirst(datestr) then -- pressupõe a ausência de link interno
return "depois de" .. datestr
else
return "depois do " .. datestr
end
end
return "depois " .. datestr
end
local function fromdate(d, displayformat) -- devolve "da data" em linguagem natural
displayformat = displayformat or {}
local precision = setprecision(d, displayformat.precision)
local datestr = p.simplestring(d, displayformat)
if displayformat and displayformat.textformat == 'minimum' then
return datestr -- por exemplo, para as classificações do MH, mostre apenas a data de início
end
if displayformat and displayformat.textformat == 'short' then
return datestr .. ' – ' -- para alguma infobox (jogador de futebol, por exemplo), mostra a data de início e um traço
end
if displayformat.stilltrue and p.before ( os.date("!%Y-%m-%dT%TZ"), d) then return
fromToNow(d, datestr, precision)
end
if (precision >= 11) or (precision == 7) or (precision == 6) then -- ter dito "desde as datas com dia, séculos, milénios
return 'a partir de ' .. datestr
end
if (precision == 10) and (vowelfirst(datemodule.determinationMes(d.month))) then
return "a partir de" .. datestr
end
return 'a partir de ' .. datestr
end
local function upto(d, displayformat) -- devolve "até à data" em linguagem natural
displayformat = displayformat or {}
local datestring = p.simplestring(d, displayformat)
local precision = setprecision(d, displayformat.precision)
if displayformat and displayformat.textformat == 'infobox' then
return ' – '.. datestring -- para alguma infobox (jogador de futebol, por exemplo), mostra a data de início e um traço
end
if displayformat and displayformat.textformat == 'short' then
return' – ' .. datestring -- para alguma infobox (jogador de futebol, por exemplo), mostra a data de início e um traço
end
if (precision >= 11) or (precision == 7) or (precision == 6) then --dizemos "até" para datas com dia e por séculos
return "até a " .. datestring
elseif (precision > 9) then
return "até a " .. datestring
else
return "até " .. datestring
end
end
local function fromuntillong(startstr, endstr, era, precision)
-- diz "de 3 a 14 de janeiro" mas "de setembro a outubro
if precision >= 11 then -- >= day
return "de " .. startstr .. " a " .. endstr .. era
else
if vowelfirst(startstr) then
return "de" .. startstr .. " a ".. endstr .. era
else
return "de " .. startstr .. " a " .. endstr .. era
end
end
end
local function removeclutter(startpoint, endpoint, precision, displayformat) -- prepare-se para tornar a data mais bonita: "De junho de 445 a.C. - julho de 445 a.C. -> De junho a julho de 445 a.C."
if (type(startpoint) ~= 'table') or (type(endpoint) ~= 'table') then
return startpoint, endpoint, precision, displayformat
end
local era = endpoint.era
local sameera
if startpoint.era == endpoint.era then
sameera = true
end
if sameera and (endpoint.year == startpoint.year) then
startpoint.year = nil
if (startpoint.month == endpoint.month) then
startpoint.month = nil
if (startpoint.day == endpoint.day) then
startpoint.day = nil
end
end
end
return startpoint, endpoint, era, displayformat, sameera
end
function p.between(startpoint, endpoint, displayformat)
displayformat = displayformat or {}
local precision = setprecision(endpoint, displayformat.precision) or 9
local startpoint = p.simplestring(startpoint, displayformat)
local endpoint = p.simplestring(endpoint, displayformat)
if not (startpoint or endpoint) then
return nil
end
if not endpoint then
if precision <= 10 then
return "depois " .. startpoint
else
return "depois de " .. startpoint
end
end
if not startpoint then
if precision <= 10 then
return "antes " .. endpoint
else
return "antes de " .. endpoint
end
end
-- analisar configurações para evitar redundâncias
local startpoint, endpoint, era, displayformat, sameera = removeclutter(startpoint, endpoint, precision, displayformat)
local startstr, endstr = p.simplestring(startpoint, displayformat), p.simplestring(endpoint, displayformat)
displayformat.hideera = true
if (startstr == '') or (startstr == endstr) then
if (not sameera) then
displayformat.hideera = false --caso contrário, é incompreensível
return p.simplestring(endpoint, displayformat)
end
return endstr
end
-- evitar períodos repetitivos como "de 13 de setembro de 2006 a 18 de setembro de 2006
if era == "-" then
era = " a.C."
else
era = ""
end
if precision <= 10 then
return "entre " .. startstr .. " e " .. endstr .. " " .. era
else
return "entre a " .. startstr .. " e a " .. endstr .. " " .. era
end
end
local function fromuntil(startpoint, endpoint, displayformat)
displayformat = displayformat or {}
local precision = setprecision(endpoint, displayformat.precision)
-- analisar configurações para evitar redundância
local startpoint, endpoint, era, displayformat, sameera = removeclutter(startpoint, endpoint, precision, displayformat)
local hideera= displayformat.hideera
displayformat.hideera = true -- para as cadeias intermediárias
local startstr, endstr = p.simplestring(startpoint, displayformat), p.simplestring(endpoint, displayformat)
if (startstr == '') or (startstr == endstr) then
displayformat.hideera = hideera -- vamos fazer uma cadeia simples, então usamos o formato original
if (not sameera) then
displayformat.hideera = false --caso contrário, é incompreensível
end
return p.simplestring(endpoint, displayformat)
end
-- evitar períodos repetitivos como "de 13 de setembro de 2006 a 18 de setembro de 2006"
if era == '-' then
era = ' a.C.'
else
era = ''
end
if displayformat.textformat == 'long' then
return fromuntillong(startstr, endstr, era, precision)
elseif (type(precision) == "number") and (precision > 9) then -- Se as datas contiverem meses ou dias, é melhor ter um espaço
return startstr .. ' -<wbr> ' .. endstr .. era
else
return startstr .. '-<wbr>' .. endstr .. era
end
end
function p.fuzzydate(dateobject, displayformat)
local str = p.simplestring(dateobject, displayformat)
if not str then
return nil
end
return "para " .. str
end
function p.daterange(startpoint, endpoint, displayformat)
if startpoint and endpoint then
return fromuntil(startpoint, endpoint, displayformat)
elseif startpoint then
return fromdate(startpoint, displayformat)
elseif endpoint then
return upto(endpoint, displayformat)
else
return nil
end
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) -- desde datavalue.value.time da Wikidata, também funcionaria usando apenas 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)
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 = {'era', 'year', 'month', 'day'}
for i, j in pairs(order) do
if b[j] < a[j] then
return true
elseif b[j] > a[j] then
return false
end
end
return true
end
return p