Módulo:Data

Fonte: Enciclopédia de conhecimento da Igreja de Deus
Saltar para a navegação Saltar para a pesquisa

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( '&nbsp;', ' ' )
												:gsub( '&#160;', ' ' )
												-- narrow nbsp
												:gsub( '\226\128\175', ' ' )
												:gsub( '&#8239;', ' ' )
												-- thin space
												:gsub( '\226\128\137', ' ' )
												:gsub( '&thinsp;', ' ' )
												:gsub( '&#8201;', ' ' )
												-- simple space
												:gsub( '&#32;', ' ' )
												-- 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