Módulo:Data
A documentação para este módulo pode ser criada na página Módulo:Data/doc
local fun = {}
local Ferramentas = require 'Módulo:Ferramentas'
-- carregando o banco de dados listando algumas páginas existentes para evitar "ifexist".
local dataLinks
local success, resultado = pcall ( mw.loadData, 'Módulo:Data/Dados' )
if success then
dataLinks = resultado
else
-- proteção no caso ou sub módulos serem mal modificados
dataLinks = { [''] = { mes = { vazio = 1000, todos = { 1773, 2014 } }, } }
end
-- limpa um parâmetro sem nome (vira os espaços no começo e no fim)
-- voltar nil se o texto estiver vazio ou não estiver enviando mensagens de texto. A atenção é importante para a função que a utiliza.
local trim = Ferramentas.trim
-- Função destinada a meter a primeira letra do mês em minúscula do mês :
-- uso de string porque nenhum mês começa com uma letra não ascii em português ou inglês.
local function lcfirst( str )
return str:sub( 1, 1 ):lower() .. str:sub( 2 )
end
-- lista de meses, escritas exatamente com alias, em minúscula
local listaMes = {
{ num = 1, nDia = 31, abrev = 'jan.', nome = 'janeiro', alias = { 'jan.', 'jan.', 'jan', 'janeiro', 'january'} },
{ num = 2, nDia = 29, abrev = 'fev.', nome = 'fevereiro', alias = { 'fevereiro', 'fev.', 'fev', 'february'} },
{ num = 3, nDia = 31, abrev = 'mar.', nome = 'março', alias = { 'mar.', 'mar', 'março', 'march' } },
{ num = 4, nDia = 30, abrev = 'abr.', nome = 'abril', alias = { 'abr.', 'abr','abril', 'april' } },
{ num = 5, nDia = 31, abrev = 'maio', nome = 'maio', alias = { 'maio', 'may' } },
{ num = 6, nDia = 30, abrev = 'jun.', nome = 'junho', alias = { 'jun', 'june' } },
{ num = 7, nDia = 31, abrev = 'jul.', nome = 'julho', alias = { 'jul.', 'july' } },
{ num = 8, nDia = 31, abrev = 'ago.', nome = 'agosto', alias = { 'ago', 'august' } },
{ num = 9, nDia = 30, abrev = 'set.', nome = 'setembro', alias = { 'set', 'set.', 'setembro', 'september' } },
{ num = 10, nDia = 31, abrev = 'out.', nome = 'outubro', alias = { 'out', 'out.', 'october' } },
{ num = 11, nDia = 30, abrev = 'nov.', nome = 'novembro', alias = { 'nov', 'nov.', 'november' } },
{ num = 12, nDia = 31, abrev = 'dez.', nome = 'dezembro', alias = { 'dezembro', 'dez.', 'dez', 'december' } },
}
-- adicionar nomes, abreviaturas e aliases como uma lista de keyMonth listaMes
for i = 1, 12 do
local mes = listaMes[ i ]
listaMes[ tostring( i ) ] = mes
listaMes[ '0' .. i ] = mes
listaMes[ mes.nome ] = mes
listaMes[ mes.abrev ] = mes
for _, n in ipairs( mes.alias ) do
listaMes[ n ] = mes
end
end
--for _, n in ipairs( listaMes.aout.alias ) do
-- listaMes[ n ] = listaMes.aout
--end
fun.listaMes = listaMes
local lista_estacao = {
{ 'Primavera', 'spring', },
{ 'Verão', 'summer', },
{ 'Outono', 'autumn', },
{ 'Inverno', 'winter', },
}
---
-- validadar que a string passada é uma valida.
-- devolver o nome completo ou nil se não for reconhecido
-- se reconhecido, também retorna o número de mes [1-12]
function fun.validaMes( mes )
local m = trim( mes )
if m then
m = mw.ustring.lower( m )
if listaMes[ m ] then
return listaMes[ m ].nome, listaMes[ m ].num
end
end
end
---
-- validar que a string passada é uma estação valida.
-- devolver o nome completo ou nil se não for reconhecido
function fun.validaEstacao( estacao )
local s = trim( estacao )
if s then
s = mw.ustring.lower( s )
for i = 1, 4 do
for _, n in ipairs( lista_estacao[i] ) do
if s == n then
return lista_estacao[i][1]
end
end
end
end
end
---
-- determinationMes encontrar o número do mes e seu nome,
-- do seu nome, seu número ou uma expressão matemática.
-- Se o segundo parâmetro for true, números maiores que 12 ou não inteiros serão aceites.
function fun.determinationMes( mes, mod, laco )
local num, nome
if tonumber( mes ) then
num = math.floor( tonumber( mes ) )
if mod then
-- se o número de mes é calculado por uma expressão, o resultado pode ser maior que 12, ou menor que 1
num = math.fmod( num + 239, 12 ) + 1 -- +239 car fmod(-1) = -1 et non 11
elseif num < 1 or num > 12 then
num = nil
end
elseif trim( mes ) then
nome, num = fun.validaMes( mes )
if nome == nil and laco == nil then
-- tente determinar um número com o analisador #expr do Mediawiki.
-- o parâmetro laco evita o loop.
nome, num = fun.determinationMes( mw.getCurrentFrame():callParserFunction( '#expr', mes ), true, true )
end
end
if num and not nome then
nome = listaMes[ num ].nome
end
return nome, num
end
-- função interna para modelData, para determinar se podemos fazer sem ifexitif
local function existData( dataQualificativo, ano, mes )
local data
if mes then
data = dataQualificativo.mes
else
data = dataQualificativo.ano
end
if type( data ) ~= 'table' then
-- Se os dados não existem, considera-se que não há link.
return
end
-- o qualificador é substituído pelo do banco de dados, que permite aliases.
local link = ano
if dataQualificativo.qualificativo then
link = link .. ' ' .. dataQualificativo.qualificativo
end
local singular = ano
if mes then
link = mes .. ' de ' .. link
singular = mes .. ' de ' .. ano
end
local vazio = tonumber( data.vazio )
if vazio and ano <= vazio then
-- se o ano está na parte "não", testamos se ainda há um link isolado
if type( data.singular ) == 'table' then
for i, v in ipairs( data.singular ) do
if singular == v or singular == tonumber( v ) then
return link
end
end
end
-- parte não e nenhum link => nada
return nil
elseif type( data.todos ) == 'table' then
local todos1, todos2 = tonumber( data.todos[1] ), tonumber( data.todos[2] )
if todos1 and todos2 and ano >= todos1 and ano <= todos2 then
-- o ano está no partido 'todos' então retornamos o link
return link
end
end
-- o ano não está nem na parte nem na parte de todos, então você tem que testar se a página existe.
local alvoLink = mw.title.new( link )
if alvoLink and alvoLink.exists then
return link
end
end
---
-- Exclui o dia da semana e "o" antes de um dado
function fun.limpezaDia( dia )
if type( dia ) == 'string' then
local nomeDia = { '[Ss]egunda-feira', '[Tt]erça-feira', '[Qq]uarta-feira', '[Qq]uinta-feira', '[Ss]exta-feira',
'[Ss]ábado', '[Dd]omingo', '^ *[Oo]' }
-- local premier = { '<abbr class="abbr" title="[Pp]remier" ?>1<sup>er</sup></abbr>', '1<sup>er</sup>', '1er' }
for i, v in ipairs( nomeDia ) do
dia = dia:gsub( v, '' )
end
-- for i, v in ipairs( premier ) do
-- dia = dia:gsub( v, '1' )
-- end
dia = trim( dia )
end
return dia
end
---
-- Separa uma cadeia de data numa tabela com campos dia, mes e ano.
-- os dados devem conter o mês.
function fun.separationDiaMesAno( data )
data = trim( data )
if data then
local function erro( periode, valor )
return false, '<span class="error">' .. periode .. ' invalida (' .. valor .. ')</span>'
end
local dia, mes, ano, esconderMes, esconderAno, separador
-- variável para construir as regex
local j = '([0-3]?%d)' -- dia
local m = '([01]?%d)' -- mês numérico
local mmm = '([^%s%p%d]+[.]?)' -- mês em todas as letras
local aj = '(%-?%d+)' -- ano ou dia
local s = '[ ./-de]+' -- separador simples
-- local sep = '([ ./-de]+)' -- separador com capture, para detectá-lo duas vezes que não é necesário
local menos = '(%-?)' -- o sinal menos para indicar que esses dados não devem ser exibidos
local regexb = {
jmmm = '^'..j..s..mmm..menos..'$',
mmmjva = '^'..mmm..s..j..', ?'..aj..'$',
}
data = fun.limpezaDia( data )
-- excluir categoria, links, tags
data = mw.ustring.gsub( data, '%[%[[Cc]ategor[yi]a?:.-%]%]', '' )
data = data :gsub( '%b<>', '' )
:gsub( '%[%[([^%[%]|]*)|?([^%[%]]*)%]%]', function ( l, t ) return trim( t ) or l end )
-- remoção de espaços não quebráveis
-- nbsp
:gsub( '\194\160', ' ' )
:gsub( ' ', ' ' )
:gsub( ' ', ' ' )
-- narrow nbsp
:gsub( '\226\128\175', ' ' )
:gsub( ' ', ' ' )
-- thin space
:gsub( '\226\128\137', ' ' )
:gsub( ' ', ' ' )
:gsub( ' ', ' ' )
-- simple space
:gsub( ' ', ' ' )
-- vários espaços
:gsub( ' +', ' ' )
-- redução a.C. para simplificar um pouco de regex:
:gsub( '(%d+) ?[Aa]%.? ?C%.?', '-%1' )
-- exclusão de horas em dados ISO
:gsub( '^+?([%d-]*%d%d%-%d%d)T%d%d[%d:,.+-]*Z?$' , '%1')
-- teste de um ano
if data:match( '^'..aj..'$' ) then
ano = data:match( '^'..aj..'$' )
elseif data:match( '^'..aj..s..aj..menos..'$' ) then
-- dd/mm, mm/aaaa ou aaaa/mm
local a, separador, b, sb = data:match( '^'..aj..s..aj..menos..'$' )
a, b = tonumber( a ), tonumber( b )
if separador:match( '^.+%-$' ) then
-- provavelmente mm/-aaaa, ano a.C.
b = 0 - b
end
if a > 12 and ( b < 1 or b > 31 ) or
b > 12 and ( a < 1 or a > 31 ) then
return erro( 'Data', data )
elseif b < 1 or b > 31 then
mes, ano, esconderAno = a, b, sb
elseif a < 1 or a > 31 then
ano, mes = a, b
elseif b > 12 then
return erro( 'Mes', b )
else
dia, mes, esconderMes = a, b, sb
end
elseif data:match( '^'..aj..s..m..menos..'%2'..s..aj..menos..'$' ) then
-- dd/mm/aaaa ou aaaa/mm/dd
dia, separador, mes, esconderMes, ano, esconderAno = data:match( '^'..aj..s..m..menos..'%2'..s..aj..menos..'$' )
if separador == '-' and esconderMes == '-' and esconderAno == '' and tonumber( ano ) > 0 then
-- data no formato dd-mm--aaaa type 17-06--44 para 17 junho 44 a.C.
esconderMes = nil
ano = 0 - ano
end
elseif data:match( '^'..j..s..mmm..menos..'%2'..s..aj..menos..'$' ) then
-- dd mmm aaaa
dia, separador, mes, esconderMes, separador, ano, esconderAno = data:match( '^'..j..s..mmm..menos..'%2'..s..aj..menos..'$' )
elseif data:match( '^'..mmm..s..aj..menos..'$' ) then
-- mmm aaaa
mes, separador, ano, esconderAno = data:match( '^'..mmm..s..aj..menos..'$' )
if separador:match( '^.+%-$' ) then
ano = '-' .. ano
end
elseif data:match( '^'..j..s..mmm..menos..'$' ) then
-- dd mmmm
dia, mes, esconderMes = data:match( '^'..j..s..mmm..menos..'$' )
elseif data:match( '^'..mmm..s..j..', ?'..aj..'$') then
-- mmm dd, aaaa (formato anglo-saxão)
mes, dia, ano = data:match( '^'..mmm..s..j..', ?'..aj..'$')
elseif data:match( '^'..mmm..'$' ) then
mes = data
else
return erro( 'Data', data )
end
local jn, an = tonumber( dia ), tonumber( ano )
if jn and an and ( jn > 31 or jn < 0 or #dia >= 3 ) and an <= 31 then
-- caso particular dos dados ISO 2015-06-17, -0044-06-17 e -0002-06-17
-- inversão do dia e do ano
local temp = ano
ano = dia
dia = temp
end
return fun.validationDiaMesAno{
dia, mes, ano,
esconderAno = trim( esconderAno ) and true or nil,
esconderMes = ( trim( esconderAno ) or not ano ) and trim( esconderMes ) and true or nil,
-- or nil serve apenas para evitar arrastar um valor falso em todos os testes unitários.
}
else
return true, {}
end
end
---
-- validationDiaMesAno verifica os parâmetros correspondentes a uma cadeia valida de dados.
-- os dados podem estar nos parâmetros 1 a 3, ou nos parâmetros dia, mes e ano.
-- A função retorna true seguida por uma tabela com os dados em parâmetros nomeados (sem foco no ano)
-- ou falso seguido por uma mensagem de erro.
function fun.validationDiaMesAno( frame, ... )
local args = Ferramentas.extractArgs( frame, ... )
local dia, mes, numMes, ano
local bdia = args[1] or args['dia'] or ''
local bmes = tostring( args[2] or args['mês'] or args['mes'] or '' )
local bano = args[3] or args['ano'] or args['year'] or ''
local function erro( periode, valor )
return false, '<span class="error">' .. periode .. ' inválido (' .. valor .. ')</span>'
end
-- agora tratamos o ano
if Ferramentas.notEmpty( bano ) then
ano = tonumber( bano )
if ano == nil and type( bano ) == 'string' then
-- teste se o ano contiver a.C.
ano = string.match( string.upper( bano ), '^(%d+) ?[Aa]%.? ?[Cc]%.?' )
ano = tonumber( ano )
if ano then
ano = 0 - ano
else
return erro( 'Ano', bano )
end
elseif ano == 0 then
return erro( 'Ano', 0 )
end
else
ano = nil
end
-- agora tratamos o mês
if Ferramentas.notEmpty( bmes ) then
mes, numMes = fun.determinationMes( bmes )
if mes == nil then
mes = fun.validaEstacao( bmes )
if mes == nil then
return erro( 'Mês', bmes )
end
else
-- agora tratamos o dias se está informado
if Ferramentas.notEmpty( bdia ) then
dia = tonumber( bdia )
if dia == nil then
dia = tonumber( fun.limpezaDia( bdia ) )
end
if dia == nil then
return erro( 'Dia', bdia )
end
-- agora verifica se dia está bem
if dia < 1 or dia > 31 then
return erro( 'Dia', bdia )
elseif dia > listaMes[numMes].nDia then
return erro( 'Dia', bdia .. ' ' .. mes )
elseif dia == 29 and numMes == 2 and ano and ( math.fmod( ano, 4 ) ~= 0 ) then
--o ano bisexto nos séculos é de todos os dias aceite para ser compatível com dados julianos.
return erro( 'Dia', '29 de fevereiro de ' .. ano )
end
else
-- Se não houver dia, a pessoa olha se a primeira letra do mes é minuúscula
if bmes:match( '^%u' ) then
-- sim, passamos a primeira letra em letras minúsculas
-- mes = lcfirst( mes )
end
-- se não houver ano, retornamos o mês simples
end
end
else
-- verificamos o dia se está informado
if Ferramentas.notEmpty( bdia ) then
if ano then
return erro( 'Mês', 'não informado' )
else
bdia = fun.limpezaDia( bdia )
dia = tonumber( bdia )
if dia then
if dia > 31 or dia < 1 then
ano = dia
dia = nil
else
return erro( 'Data', 'dia único : ' .. bdia )
end
else
return erro( 'Dia', bdia )
end
end
end
end
-- verificação da ausência de desvio
if ano and ano < 13 and ano > 0 and not dia and ( tonumber( bmes ) or (not mes and tonumber( args[4] ) ) ) then
return false, '<span class="error">ano improvável (' .. ano .. ')</span>'
end
local resultado = {
dia = dia,
mes = mes,
numMes = numMes,
ano = ano,
esconderAno = args.esconderano,
esconderMes = args.escondermes,
}
return true, resultado
end
---
-- emula a predefinição {{tl|Data}}.
-- Configurações:
-- 1: dia (número) ou os dados completos
-- 2: mês (na íntegra) ou estação do ano
-- 3: ano (número)
-- 4: estação do ano
-- juliano: dados no calendário juliano
-- compacto: exibe o mês como uma abreviatura
-- ad: não para desativar a exibição de "a.C." para dados negativos
-- idade: adicione a duração desde que esses dados
-- nolink: não coloque um link nos dados
-- nascimento: adicione a classe "bday"
-- dead: adicione a classe "dday"
function fun.modeloData( frame )
local args = Ferramentas.extractArgs( frame )
local cat, resultado = ''
-- analisar parâmetros sem nome (ou parâmetros de dados dia, mês, ano)
local test, params
local arg1, arg2, arg3 = fun.limpezaDia( args[1] ), trim( args[2] ), trim( args[3] )
if type( arg1 ) == 'string' and arg3 == nil and ( arg1:match( '[^ ./-][ ./-]+[^ ./-]' ) or arg2 == nil or dataLinks[arg2] or mw.ustring.match( arg2, '%a %a' ) ) then
-- os dados estão no primeiro parâmetro
test, params = fun.separationDiaMesAno( arg1 )
if test then
params.qualificativo = arg2
end
else
local function esconderParam( p )
-- separa o possível sinal de menos significando que o parâmetro não deve ser exibido.
if type( p ) ~= 'string' then
return p, nil
end
local value, mask = p:match( '^%s*(.-)(%-?)%s*$' )
return value, ( mask == '-' or nil )
end
local cleanArgs = { arg1 or args.dia }
cleanArgs[2], cleanArgs.escondermes = esconderParam( args[2] or args.mes )
cleanArgs[3], cleanArgs.esconderano = esconderParam( args[3] or args.ano )
test, params = fun.validationDiaMesAno( cleanArgs )
if test then
params.qualificativo = trim( args[4] )
end
end
-- analisar os parâmetros nomeados
if test then
local Yesno = require 'Módulo:Yesno'
params.qualificativo = params.qualificativo or args.qualificativo
-- Juliano pode ter três valores: inativo, formato padrão (true), formato curto
params.juliano = Yesno( args.juliano, 'curto', false )
params.ac = Yesno( args.ac )
local listaParam = {
idade = 'idade',
nascimento = 'nascimento',
morte = 'morte',
falecido = 'morte',
ac = 'a.C.',
nolinks = 'nolinks',
compacto = 'compacto',
compacta = 'compacto',
}
for n, v in pairs( listaParam ) do
params[v] = params[v] or Yesno( args[n], true, false ) or nil
end
-- saída para testes unitários ou para depurar
if args.debug then
return params
end
resultado = fun._modeloData( params )
else
local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [14] = true, [100] = true }
if namespaceCategorisation[ mw.title.getCurrentTitle().namespace ] and not Ferramentas.notEmpty( args.nocat ) then
cat = '[[Categoria:!Páginas que usam a predefinição data com erros de sintaxe]]'
end
resultado = params .. cat
end
return resultado or ''
end
function fun._modeloData( args )
local ano, mes, numMes, dia = args.ano, args.mes, args.numMes, args.dia
local qualificativo = args.qualificativo
if ( ano or mes or dia ) == nil then
return
end
-- agora tratamos a idade, o nascimento e a morte
local idade = args['idade'] and fun.idade( ano, numMes, dia )
local nascimento = args.nascimento
local morte = args.morte
-- tratar o calendário
local gano, gmes, gdia = ano, numMes, dia -- dados de acordo com o calendário gregoriano para <hora>
local jano, jmes, jdia = ano, mes, dia -- dados de acordo com o calendário juliano, se necessário
local julianoData, julianoSup, julianoSep -- pode ser usado para exibir os dados de acordo com o calendário juliano
local gregAprMes, gregAprAno, gregFim -- Mensagem do calendário gregoriano quando os dados estão de acordo com o calendário juliano
if ano and dia then
local amj = ano * 10000 + numMes * 100 + dia
if amj < 15821014 then
if ano > 0 then
gano, gmes, gdia = fun.julianToGregorian( ano, numMes, dia )
else
-- Calendário gregoriano proléptico com ano 0.
gano, gmes, gdia = fun.julianToGregorian( ano + 1, numMes, dia )
end
args.juliano = false
elseif args.juliano then
gano, gmes, gdia = fun.julianToGregorian( ano, numMes, dia )
ano, mes, dia = gano, listaMes[gmes].nome, gdia
if args.compacto then
jmes = listaMes[ jmes ].abrev
end
if args.juliano == 'curto' then
julianoData = jdia .. ' ' .. jmes .. ' '
julianoSup = '<sup>[[calendário juliano|jul.]]</sup>'
if jano == ano then
gregAprMes = '<sup>[[calendário gregoriano|greg.]]</sup>'
else
julianoData = julianoData .. jano .. ' '
gregAprAno = '<sup>[[calendário gregoriano|greg.]]</sup>'
end
julianoSep = ' / '
else
julianoData = jdia .. ' ' .. jmes .. ' ' .. jano
julianoSep = ' ('
gregFim = ' [[Mudança para o calendário gregoriano|dentro do calendário gregoriano]])'
end
end
else
if ano and ano < 0 then
gano = gano + 1
end
args.juliano = false
end
-- agora geramos o resultado
-- Declarações de variáveis
local wikiLista = {} -- recebe a mensagem de texto exibida para cada parâmetro
local iso = {} -- recebe o formato de data ISO de cada parâmetro
local textoMes = mes -- mensagem de texto que será exibida (possivelmente a abreviação)
if args.compacto then
if args.nolinks then
textoMes = '<abbr class=abbr title="' .. mes .. '">' .. listaMes[ mes ].abrev .. '</abbr>'
else
textoMes = listaMes[ mes ].abrev
end
end
local dataQualificativo, dataCat
if not args.nolinks then
dataQualificativo = dataLinks[qualificativo or '']
if type( dataQualificativo ) ~= 'table' then
-- se o qualificador não estiver no banco de dados, criamos uma tabela mínima,
-- que vai impor um teste no ano, mas considera que não há link no dia ou no mes
dataQualificativo = { qualificativo = ' ' .. qualificativo, ano = { } }
end
dataCat = dataLinks[dataQualificativo.cat]
if type( dataCat ) ~= 'table' or dataCat == dataQualificativo then
dataCat = { qualificativo = '' }
end
end
local function wikiLink( link, texto )
if link == texto then
return '[[' .. texto .. ']]'
else
return '[[' .. link .. '|' .. texto .. ']]'
end
end
-- o dia se informado
local qualifDia = ''
if dia then
local textoDia = dia
if args.nolinks then
table.insert( wikiLista, dia )
else
qualifDia = dataQualificativo.dia and dataQualificativo.qualificativo
or dataCat.dia and dataCat.qualificativo
or ''
local link = dia .. ' de ' .. mes .. ' ' .. qualifDia
-- se não houver um link no mes, ele será exibido com o dia.
table.insert( wikiLista, wikiLink( link, dia ) )
table.insert( wikiLista, wikiLink( link, dia .. ' de '.. textoMes ) )
end
table.insert( iso, 1, string.sub( '0' .. gdia, -2 ) )
end
-- o mês
if mes then
if #wikiLista == 0 and ano == nil then
return textoMes
end
if args.nolinks then
if not args.escondermes then
table.insert( wikiLista, textoMes )
end
else
local link
if ano then
link = existData( dataQualificativo, ano, mes ) or existData( dataCat, ano, mes )
if link == nil and qualificativo and qualifDia == '' then
-- teste novo teste sem o qualificador somente se não houver efemérides para esse qualificador.
link = existData( dataLinks[''], ano, mes )
end
end
if link or args.escondermes then
-- se houver um link, remova o link que exibe 'dia mes' para adicionar 'Mês dia'
table.remove( wikiLista )
if not args.escondermes then
table.insert( wikiLista, wikiLink( link, textoMes ) )
end
elseif #wikiLista > 0 then
-- caso contrário, removemos o link exibindo 'dia' para manter apenas o link 'dia mês'
table.remove( wikiLista, #wikiLista - 1 )
elseif args.esconderano then
-- se não houver dia e o ano não for exibido, insira o único.
table.insert( wikiLista, textoMes )
end
end
if gmes then
table.insert( iso, 1, string.sub( '0' .. gmes, -2 ) )
table.insert( wikiLista, ' de ' )
end
table.insert( wikiLista, gregAprMes )
end
-- o ano
if ano and not (args.juliano == true and args.nolinks and jano == ano ) then
if not args.esconderano then
local textoAno = ano
local link
if ano < 0 then
local anoaC = 0 - ano
link = link or ( anoaC .. ' a.C.' )
if args.ac == false then
textoAno = anoaC
else
textoAno = anoaC .. ' <abbr class="abbr" title="'
.. anoaC .. ' antes da Era Comum">a.C.</abbr>'
end
elseif args.ac then
textoAno = textoAno .. ' <abbr class="abbr" title="'
.. textoAno .. ' depois da Era Comum">depois de a.C.</abbr>'
end
if args.nolinks then -- somente se tivermos que exibi-lo
table.insert( wikiLista, textoAno )
else
link = existData( dataQualificativo, ano ) or existData( dataCat, ano ) or link or ano
if mes and #wikiLista == 0 then
-- se o mes não tiver link e não for exibido com o dia, ele será exibido com o ano.
textoAno = textoMes .. ' de ' .. textoAno
end
table.insert( wikiLista, wikiLink( link, textoAno ) )
end
end
end
if ano then
if gano > 999 then
table.insert( iso, 1, gano )
elseif gano > -1 then
table.insert( iso, 1, string.sub( '000' .. gano , -4 ) )
elseif gano > -999 then
-- Calendário gregoriano proléptico com ano 0.
table.insert( iso, 1, 'U-' .. string.sub( '000' .. ( 0 - gano ), -4 ) )
else
table.insert( iso, 1, 'U' .. gano )
end
end
table.insert( wikiLista, gregAprAno )
-- a idade
if type( idade ) == 'number' and idade >= 0 and ( not nascimento or idade < 120 ) then
if idade == 0 then
idade = '(menos de um ano)'
elseif idade == 1 then
idade = '(1 ano)'
else
idade = '(' .. idade .. ' anos)'
end
else
idade = false
end
-- compilação dos resultados
local wikiTexto = table.concat( wikiLista, ' ' )
local isoTexto = table.concat( iso, '-' )
-- Nós adicionamos um pouco de semântica.
local wikiHtml = mw.html.create( '' )
if julianoData then
wikiHtml:tag( 'span')
:addClass( 'nowrap' )
:attr( 'date-sort-value', isoTexto )
:wikitext( julianoData )
:node( julianoSup )
:done()
:wikitext( julianoSep )
end
local dataHtml = wikiHtml:tag( 'time' )
:wikitext( wikiTexto )
if wikiTexto:match( ' ' ) then
dataHtml:addClass( 'nowrap' )
end
if isoTexto ~= wikiTexto then
dataHtml:attr( 'datetime', isoTexto )
:attr( 'date-sort-value', isoTexto )
end
if not args.nolinks then
dataHtml:addClass( 'date-link' )
end
if nascimento then
dataHtml:addClass( 'bday' )
elseif morte then
dataHtml:addClass( 'dday' )
end
wikiHtml:wikitext( gregFim )
if idade then
wikiHtml:wikitext( ' ' )
:tag( 'span' )
:addClass( 'noprint')
:wikitext( idade )
:done()
end
return tostring( wikiHtml )
end
---
-- função para infoboxes, especialmente para exibir nascimento e dados mortos
-- os links presentes nos dados fornecidos são automaticamente excluídos para gerenciar os casos ou
-- o parâmetro já contém um modelo de dados.
-- Configurações:
-- 1 : type de dados a afixar (nascimento / n, morte / m, ou data / d)
-- 1 : Data ou data de nascimento
-- 2 : Data de morte se tipo n ou m
-- qualificativo = sufixo de páginas de dados para vincular (exemplo: na música)
-- nolinks : não mostrar link
-- préfixe : prefixo para exibir se houver um dia (por padrão '')
-- prefixo sem dia: prefixo a ser exibido se não houver dia (padrão: '')
function fun.dataInfobox( frame )
local args = frame.args
if type( args ) ~= 'table' or not ( args[1] and args[2] ) then
return
end
-- analisarData separa os dados do conteúdo a seguir, exclui os links e, se possível, retorna uma tabela com os anos
local function analisarData( d )
if trim( d ) then
local analisar = d:match( ' ou ') or d:match( 'entre ' ) or d:match( 'para ' ) or d:match( 'depois ' ) or d:match( 'antes ' )
if analisar then
return d
end
analisar = d:match( 'datetime="([%d-]+)"' ) or d
-- separa os dados (com seus links) de uma referência ou conteúdo que começa com um espaço)
local inicio, fim = analisar:match( '(.-%d%d%d%]*%-?)([\127 ].+)' )
if not inicio then
-- separa os dados do conteúdo começando com <br>
inicio, fim = analisar:match( '(.-%d%d%d%]*%-?)(<br ?/?>.+)' )
end
analisar = inicio or analisar
-- excluir links
analisar = analisar:gsub(
'%[%[([^%[%]|]*)|?([^%[%]]*)%]%]',
function ( l, t )
return trim( t ) or l
end
)
local t, r = fun.separationDiaMesAno( analisar )
if t then
return r, fim
else
return d, fim
end
end
end
-- prefixo adiciona um prefixo dependendo da presença ou ausência do dia se o parâmetro "prefixo sem dia" for definido
local function prefix( dataString )
if dataString then
local datetime = dataString:match( 'datetime="([U%d%-]+)"' )
if datetime and datetime:match('%-%d%d%-%d%d') and trim( args['prefixo'] ) then
return args['prefixo'] .. ' ' .. dataString
end
if trim( args['prefixo sem dia'] ) then
return args['prefixo sem dia'] .. ' ' .. dataString
end
end
return dataString
end
local nascimento = args[1]:match( '^n' ) == 'n'
local morte = args[1]:match( '^m' ) or args[1]:match( 'morte' )
local mostrarData, qualificativo = args[2], args[4]
local mostrarDataTab, resultadoData, complementData
local dataNascimento, dataMorte
if morte then
mostrarData = args[3]
end
if not trim( mostrarData ) then
return
end
if mostrarData:match( '</time>' ) then
-- Se houver links, provavelmente já existe um modelo de dados, evite executá-lo uma segunda vez
if ( nascimento or morte ) and ( mostrarData:match( 'wikidata%-linkback' )) then
dataNascimento = analisarData( args[2] )
dataMorte = analisarData( args[3] )
resultadoData = mostrarData
else
return prefix( mostrarData )
end
else
mostrarDataTab, complementData = analisarData( mostrarData )
if type( mostrarDataTab ) ~= 'table' then
return mostrarDataTab
else
if nascimento then
dataNascimento = mostrarDataTab
dataMorte = analisarData( args[3] )
elseif morte then
dataNascimento = analisarData( args[2] )
dataMorte = mostrarDataTab
else
qualificativo = args[3]
end
mostrarDataTab.nascimento = nascimento
mostrarDataTab.morte = morte
mostrarDataTab.qualificativo = args.qualificativo or qualificativo
mostrarDataTab.nolinks = args.nolinks
mostrarDataTab.nocat = args.nocat
mostrarDataTab.juliano = args.juliano
end
end
resultadoData = resultadoData or fun.modeloData( mostrarDataTab )
local idade, prefixIdade, suffixIdade, calculoIdade = '', ' <span class="noprint">(', ')</span>', nil
if nascimento and
dataNascimento and
not dataMorte and
type( dataNascimento ) == 'table'
then
calculoIdade = fun.idade( dataNascimento.ano, dataNascimento.numMes, dataNascimento.dia )
if calculoIdade and calculoIdade > 120 then
calculoIdade = nil
end
elseif morte and
dataNascimento and
dataMorte and
type( dataNascimento ) == 'table'
and type( dataMorte ) == 'table'
then
calculoIdade = fun.idade(
dataNascimento.ano,
dataNascimento.numMes,
dataNascimento.dia,
dataMorte.ano,
dataMorte.numMes,
dataMorte.dia
)
prefixIdade = ' (a '
suffixIdade = ')'
end
if tonumber( calculoIdade ) then
if calculoIdade > 1 then
idade = prefixIdade .. calculoIdade .. ' anos' .. suffixIdade
elseif calculoIdade == 1 then
idade = prefixIdade .. 'um ano' .. suffixIdade
elseif calculoIdade == 0 then
idade = prefixIdade .. 'menos de um ano' .. suffixIdade
end
if complementData and complementData:match( 'anos?%)' ) then
complementData = ''
end
end
return prefix( resultadoData ) .. ( complementData or '' ) .. idade
end
---
-- a função dataISO retorna um dado no formato aaaa-mm-dd (sem links)
-- o ano pode ser na forma 2013 ou [[2013 na literatura|2013]]
-- o mês pode estar em letra ou em número
-- o dia pode estar no formato '5', ou 'Sexta 13'
function fun.dataISO( frame )
local args = Ferramentas.extractArgs( frame )
local ano = Ferramentas.notEmpty( args.ano, args.year, args.data )
-- extração do ano
if type( ano ) == 'string' then
ano = ( tonumber( ano ) -- match '2013'
or string.match ( ano, '%D(%d%d%d%d)%D' ) -- match '[[2013 na música|2013]]'
or string.match ( ano, '%D(%d%d%d%d)%D$' ) -- match '17 de setembro de 2013'
or string.match ( ano, '^(%d%d%d%d)%D' ) -- match '2013-09-17'
)
end
ano = tonumber( ano )
-- O formato iso de dados é definido de acordo com o calendário gregoriano.
-- Antes do ano de 1583 os dados são calendários é provavelmente do calendário juliano,
-- então se abstenha.
if ano and ano > 1582 then
local mes = Ferramentas.notEmpty( args.mes, args.month )
-- num mês encontra o número de mês, seja númerico ou texto, completo ou abreviado.
local nomeMes, numMes = fun.determinationMes( mes )
if numMes then
mes = '-' .. string.sub( '0' .. numMes, -2 )
local dia = Ferramentas.notEmpty( args.dia, args.day, args['calendário'] )
if type( dia ) == 'string' then
dia = tonumber( dia ) or tonumber( string.match ( dia, '%d+') )
end
dia = tonumber( dia )
if dia and dia <= listaMes[numMes].nDia then
dia = '-' .. string.sub( '0' .. dia, -2 )
return ano .. mes .. dia
else
return ano .. mes
end
else
return tostring( ano )
end
end
end
---
-- Rank do dia no ano
-- Uso: do_dayRank {ano, meu, dia}
function fun.do_dayRank(arguments)
local yr = tonumber(arguments.year or arguments[1]) or 1
local mt = tonumber(arguments.month or arguments[2]) or 1
local dy = tonumber(arguments.day or arguments[3]) or 1
-- Classificações do primeiro do mês
local ranks = {0,31,59,90,120,151,181,212,243,273,304,334}
local rank = (ranks[mt] or 0) + dy - 1
if(fun.isLeapYear(yr) and (mt >= 3)) then
rank = rank+1
end
return rank
end
-- Número de dias entre dois anos (de 1 de janeiro a 1 de janeiro)
-- Segue o calendário gregoriano
function fun.do_daysBetween(arguments)
local yr1 = tonumber(arguments[1]) or 0
local yr2 = tonumber(arguments[2]) or 0
return fun.daysSinceOrigin(yr2) - fun.daysSinceOrigin(yr1)
end
-- Número de dias desde o ano 1 (de 1 de janeiro a 1 de janeiro)
function fun.daysSinceOrigin(year)
local yr = year-1
return 365*yr + math.floor(yr/4) - math.floor(yr/100) + math.floor(yr/400)
end
-- Teste do ano bissexto (segue o calendário gregoriano)
function fun.isLeapYear(year)
local yr = tonumber(year) or 1
return (yr%4 == 0) and ((yr%100 ~= 0) or (yr%400 == 0))
end
-- Convertendo um número em algarismos romanos
function fun.toRoman(number)
local n = math.floor(number)
local letters = {"I","V","X","L","C","D","M","",""}
local pattern = {"","0","00","000","01","1","10","100","1000","02"}
local result = ""
if(n<=0 or n>=4000) then
result = "---"
else
for i=1,7,2 do
local p = pattern[n%10 + 1]
for j=0,2 do
p = string.gsub(p,tostring(j),letters[i+j])
end
result = p .. result
n = math.floor(n/10)
end
end
return result
end
---
-- Calculando um dado no calendário republicano
-- Supõe-se que os anos 4n + 3 são mais sextiles (3, 7, 11 ...)
function fun.do_toRepCal(arguments)
local yr = tonumber(arguments.year or arguments[1]) or 2000
-- gama absoluto do dia solicitado, sendo o dia 0 ou 22 de setembro de 1792 (1º dia do ano I)
local repDays = fun.do_dayRank(arguments) + fun.do_daysBetween{1792,yr} - fun.do_dayRank{1792,9,22}
local repYear = math.floor((repDays+731)/365.25) - 1
local repDayRank = repDays - 365*(repYear-1) - math.floor(repYear/4)
local repMonth, repDay = math.floor(repDayRank/30)+1, (repDayRank%30)+1
return {repYear, repMonth, repDay}
end
---
-- Ver Predefinição:Idade
-- retorna a idade de acordo com os dados fornecidos. O valor retornado é do tipo 'número'
-- Parâmetros:
-- 1, 2, 3: ano, mês dia de nascimento (suposto no calendário gregoriano)
-- 4, 5, 6: year, mês, dia do cálculo (opcional, por padrão, os dados UTC atuais).
function fun.idade( an, mn, jn, ac, mc, jc )
if ac == nil then
local today = os.date( '!*t' )
ac = today.year
mc = today.month
jc = today.day
else
ac = tonumber( ac )
mc = tonumber( mc )
jc = tonumber( jc )
end
local an = tonumber( an )
local mn = tonumber( mn )
local jn = tonumber( jn )
if an == nil or ac == nil or mn == nil or mc == nil then
-- nenhuma mensagem de erro que possa travar a função de chamada
-- para ela gerenciar esse retorno.
return
end
local idade = ac - an
if mc == mn then
if jc == nil or jn == nil then
return
end
return idade-tonumber( jc < jn and 1 or 0 )
else
return idade-tonumber( mc < mn and 1 or 0 )
end
end
function fun.modeloIdade( frame )
local args = frame.getParent().args
local idade = fun.idade (
args[1] or args['ano'],
args[2] or args['mês'] or args['mes'],
args[3] or args['dia'],
args[4],
args[5],
args[6]
)
if idade then
return idade
else
return '<span class="error">Parâmetro incorretos ou insuficientes para calcular a precisão da idade</span>'
end
end
---
-- calcula o dia juliano à partir de uma data do calendário gregoriano
function fun.julianDay( year, month, day, hour, min, sec )
local julian
julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 )
- math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 100 )
+ math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) / 400 )
+ math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400
- 32167.5
return julian
end
---
-- cálculo do dia juliano a partir de um dado do calendário juliano
function fun.julianDayJulian( year, month, day, hour, min, sec )
local julian
julian = math.floor( math.floor( ( year * 12 + month + 57609 ) / 12 - 1 ) * 1461 / 4 )
+ math.floor( ( math.fmod( month + 57609, 12 ) + 4 ) * 153 / 5 )
+ day + ( hour or 12 ) / 24 + ( min or 0 ) / 1440 + ( sec or 0 ) / 86400
- 32205.5
return julian
end
---
-- cálculo de um dado no calendário gregoriano do dia juliano
function fun.julianDayToGregorian( julianDay )
local base = math.floor( julianDay + 32044.5 ) -- 1 March -4800 (proleptic Gregorian data)
local nCentury = math.floor( ( base * 4 + 3 ) / 146097 )
local sinceCentury = base - math.floor( nCentury * 146097 / 4 )
local nYear = math.floor( ( sinceCentury * 4 + 3 ) / 1461 )
local sinceYear = sinceCentury - math.floor( nYear * 1461 / 4 )
local nMonth = math.floor( ( sinceYear * 5 + 2 ) / 153 )
local day = sinceYear - math.floor( ( nMonth * 153 + 2 ) / 5 ) + 1
local month = nMonth - math.floor( nMonth / 10 ) * 12 + 3
local year = math.floor( sinceYear / 306 ) + nYear + 100 * nCentury - 4800
return year, month, day
end
---
-- cálculo de um dado no calendário juliano do dia juliano
function fun.julianDayToJulian( julianDay )
local year = math.modf( ( julianDay * 4 - 6884469 ) / 1461 )
local r2 = julianDay - math.modf( ( 1461 * year + 6884472 ) / 4 )
local month = math.modf( ( 5 * r2 + 461 ) / 153 )
local day = r2 - math.modf( ( 153 * month - 457 ) / 5 ) + 1
if month > 12 then
year = year + 1
month = month - 12
end
return year, month, day
end
---
-- cálculo de um dado no calendário gregoriano a partir de um dado no calendário juliano
function fun.julianToGregorian( year, month, day )
return fun.julianDayToGregorian( fun.julianDayJulian( year, month, day ) )
end
---
-- cálculo de um dado no calendário juliano a partir de um dado no calendário gregoriano
function fun.gregorianToJulian( year, month, day )
year = tonumber(year)
if month then month = tonumber(month) else month = 6 end --leva um valor central para dar um melhor "palpite"
if day then day = tonumber(day) else day = 15 end
return fun.julianDayToJulian( fun.julianDay( year, month, day ) )
end
--[[
Esta função devolve "CET" ou "CEST" dependendo se no pseudo timezone atual
é hora de verão ou inverno.
Esta função só faz sentido para predefinições usados na Europa
Parâmetro opcional sem nome: "sem link": retorna o texto CET / CEST. caso contrário
retornar o mesmo texto com um wikilink para os artigos correspondentes
--]]
function fun.CEST(frame)
-- opção : não crie wikilink
local opt = trim(frame.args[1] or frame:getParent().args[1])
-- recuperamos as informações na zona atual
local t = mw.getContentLanguage():formatDate("I", nil, true)
if (t == "1") then -- hora de Verão
if (opt == "sem link") then
return "CEST"
elseif (opt == "desvio") then
return "2"
else
return "[[Horário de Verão da Europa Central|CEST]]"
end
else -- horário de inverno (ou outra área onde não se aplica)
if (opt == "sem link") then
return "CET"
elseif (opt == "desvio") then
return "1"
else
return "[[Horário da Europa Central|CET]]"
end
end
end
return fun