<?xml version="1.0" encoding="ISO-8859-1"?>

<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<title>LinuxQuestions.org - Blogs - marozsas</title>
		<link>http://www.linuxquestions.org/questions/blog.php?u=228602</link>
		<description>LinuxQuestions.org offers a free Linux forum where Linux newbies can ask questions and Linux experts can offer advice. Topics include security, installation, networking and much more.</description>
		<language>en</language>
		<lastBuildDate>Mon, 23 Nov 2009 13:57:19 GMT</lastBuildDate>
		<generator>vBulletin</generator>
		<ttl>60</ttl>
		<image>
			<url>http://e1h7.simplecdn.net/lqcdn/images/questions/images/misc/rss.jpg</url>
			<title>LinuxQuestions.org - Blogs - marozsas</title>
			<link>http://www.linuxquestions.org/questions/blog.php?u=228602</link>
		</image>
		<item>
			<title>tratando_as_excecoes_da_nova_regra_para_o_Horario_de_Verao_Brasileiro</title>
			<link>http://www.linuxquestions.org/questions/blog.php?b=1183</link>
			<pubDate>Mon, 15 Sep 2008 11:19:43 GMT</pubDate>
			<description>(This blog entry is in portuguese since it matters only for Brazil) 
 
A partir de 2008 os dias em que começam e terminam o horário de verão no...</description>
			<content:encoded><![CDATA[<div>(This blog entry is in portuguese since it matters only for Brazil)<br />
<br />
A partir de 2008 os dias em que começam e terminam o horário de verão no Brasil serão fixos, válido pelo menos até quando o &quot;Grande Colisor de Hádrons&quot; produza um buraco negro que saia do controle e &quot;engula&quot; o planeta ou talvez até o inicio do mandato do próximo presidente, o que ocorrer primeiro. <br />
<br />
Segundo publicado no diário oficial da União, o horário de verão começará sempre no terceiro domingo de Outubro e terminará no terceiro domingo de fevereiro, exceto se tal domingo, for o domingo de carnaval. Nesse caso, termina no quarto domingo.<br />
<br />
As datas fixas facilitam um pouco, pois é possivel uma configuração também fixa. Não levando em conta a exceção, o arquivo de timezone ficaria assim:<br />
<br />
<div style="margin:20px; margin-top:5px">
	<div class="smallfont" style="margin-bottom:2px">Code:</div>
	<pre class="bbcodeblock" dir="ltr" style="
		margin: 0px;
		margin-right: -99999px;
		padding: 3px;
		border: 1px inset;
		width: 98%;
		height: 130px;
		text-align: left;
		overflow: auto">#Rule   NAME  FROM  TO    TYPE  IN   ON 	AT    SAVE  	LETTER/S
Rule    BR    2008  only   -    Feb  17 	0:00  0:00  	S
Rule	BR    2008  MAX    -    Oct  Sun&gt;=15  	0:00  1:00 	D
Rule    BR    2009  MAX    -    Feb  Sun&gt;=15  	0:00  0:00 	S

#Zone   NAME            GMTOFF  RULES/SAVE      FORMAT  [UNTIL]
Zone    Brazil/East      -3:00   BR             BR%s</pre>
</div>e a compilação do arquivo e uma rápida verificação retornaria:<br />
<div style="margin:20px; margin-top:5px">
	<div class="smallfont" style="margin-bottom:2px">Code:</div>
	<pre class="bbcodeblock" dir="ltr" style="
		margin: 0px;
		margin-right: -99999px;
		padding: 3px;
		border: 1px inset;
		width: 98%;
		height: 194px;
		text-align: left;
		overflow: auto">[root@babylon5 ~]# zic -v /tmp/Brazil_East.zic
[root@babylon5 ~]# zdump -v -c 2016 Brazil/East 
Brazil/East  Fri Dec 13 20:45:52 1901 UTC = Fri Dec 13 17:45:52 1901 BRS isdst=0 gmtoff=-10800
Brazil/East  Sat Dec 14 20:45:52 1901 UTC = Sat Dec 14 17:45:52 1901 BRS isdst=0 gmtoff=-10800
Brazil/East  Sun Oct 19 02:59:59 2008 UTC = Sat Oct 18 23:59:59 2008 BRS isdst=0 gmtoff=-10800
Brazil/East  Sun Oct 19 03:00:00 2008 UTC = Sun Oct 19 01:00:00 2008 BRD isdst=1 gmtoff=-7200
Brazil/East  Sun Feb 15 01:59:59 2009 UTC = Sat Feb 14 23:59:59 2009 BRD isdst=1 gmtoff=-7200
Brazil/East  Sun Feb 15 02:00:00 2009 UTC = Sat Feb 14 23:00:00 2009 BRS isdst=0 gmtoff=-10800
Brazil/East  Sun Oct 18 02:59:59 2009 UTC = Sat Oct 17 23:59:59 2009 BRS isdst=0 gmtoff=-10800
Brazil/East  Sun Oct 18 03:00:00 2009 UTC = Sun Oct 18 01:00:00 2009 BRD isdst=1 gmtoff=-7200
...</pre>
</div>Como podem ver, em 2008, o horário de verão começaria em 19 de Outubro e terminaria em 15 de fevereiro.<br />
<br />
Mas a tal exceção me deixou intrigado. A exceção tirou a beleza de uma configuração fixa, o que me fez coçar os neurônios.<br />
<br />
Já conhecia a man page do &quot;zic(1)&quot; há tempos. Lá fala de uma função &quot;yearistype&quot; que é chamada para cada ano, e se retornar verdadeiro (true) a respectiva linha no time zone é aplicada.<br />
<br />
Humm...então essa função poderia então ser usada para tratar a exceção da regra do &quot;Horário de Verão Brasileiro a partir de 2008&quot;.<br />
Para tanto, &quot;basta&quot; saber se o domingo que antecede o carnaval cai no terceiro domingo do mês. Se cair, aplica-se a regra do quarto domingo do mês. Simples não ?<br />
<br />
Humm...o problema é que o carnaval, uma festa pagã, é data móvel, determinada pela data em que cai outro feriado, a páscoa, um feriado sagrado da igreja Católica Romana, outra data móvel, que é determinada, segundo regras estabelecidas pelo concilio de Nicea no longiguo ano de 325, como sendo o primeiro domingo depois da primeira lua cheia depois do equinócio vernal (equinócio da primavera no hemisfério norte).<br />
<br />
Humm...complicado heim ?<br />
<br />
Bem, como Google é pai e wikipedia é mãe, acabei encontrando o algoritmo elaborado por &quot;Meeus/Jones/Butcher&quot; válido para quem usa o calendário gregoriano (somos nós ! - Os católicos ortodoxos orientais usam o calendário Juliano)<br />
(http://en.wikipedia.org/wiki/Computus#Algorithms)<br />
<br />
Humm...então basta eu implementar esse algoritmo para descobrir a data da páscoa de um determinado ano, obter dai a data do carnaval e testar se domingo que o antecede é o terceiro domingo e se for, aplica-se a exceção ! Está ficando simples....<br />
<br />
 Implementei o algoritmo de &quot;Meeus/Jones/Butcher&quot; em &quot;dc(1)&quot;.<br />
(Salve os comandos &quot;dc&quot; abaixo em /usr/local/lib/easter.dc)<br />
<div style="margin:20px; margin-top:5px">
	<div class="smallfont" style="margin-bottom:2px">Code:</div>
	<pre class="bbcodeblock" dir="ltr" style="
		margin: 0px;
		margin-right: -99999px;
		padding: 3px;
		border: 1px inset;
		width: 98%;
		height: 498px;
		text-align: left;
		overflow: auto"># y=year from cmd line
sy
0 k
# a=y mod 19
ly 19 % sa
# b=y/100
ly 100 / sb
# c=y mod 100
ly 100 % sc
# d=b/4
lb 4 / sd
# e=b mod 4
lb 4 % se
# f=(b+8)/25
lb 8 + 25 / sf
# g = (b - f + 1) / 3
lb lf - 1 + 3 / sg
# h = (19 × a + b - d - g + 15) mod 30
19 la * lb + ld - lg - 15 + 30 % sh
# i = c / 4
lc 4 / si
# k = c mod 4
lc 4 % sk
# L = (32 + 2 × e + 2 × i - h - k) mod 7
32 le 2 * + li 2 * + lh - lk - 7 % sl
# m = (a + 11 × h + 22 × L) / 451
la 11 lh * + 22 ll * + 451 / sm
# n=(h + L - 7 × m + 114)
lh ll + 7 lm * - 114 + sn
# month= n/31
ln 31 /
4 k 
100 / 
# day=(n mod 31)+1
0 k
ln 31 % 1 + 
4 k 
10000 / + ly +
p</pre>
</div>...e use-o como:<br />
<div style="margin:20px; margin-top:5px">
	<div class="smallfont" style="margin-bottom:2px">Code:</div>
	<pre class="bbcodeblock" dir="ltr" style="
		margin: 0px;
		margin-right: -99999px;
		padding: 3px;
		border: 1px inset;
		width: 98%;
		height: 130px;
		text-align: left;
		overflow: auto">[miguel@babylon5 ~]$ dc -e 2008 -f /usr/local/lib/easter.dc
2008.0323
[miguel@babylon5 ~]$ dc -e 2009 -f /usr/local/lib/easter.dc
2009.0412
[miguel@babylon5 ~]$ dc -e 2010 -f /usr/local/lib/easter.dc
2010.0404
[miguel@babylon5 ~]$</pre>
</div>(as datas da páscoa dos anos passados na CLI são retornadas no formato &quot;yyyy.mmdd&quot;)<br />
<br />
humm...agora é necessário descobrir a data do carnaval. O tal conselho de Nicea decidiu que deve se haver pelo menos 40 dias entre o final do carnaval e a sexta-feira santa que antecede a páscoa, a fim de que os fieis se preparem e jejuem adequadamente depois daquela esbórnia da festa pagã em homenagem ao deus &quot;Sol&quot; Trocando em miúdos, entre a terça-feira do carnaval e o domingo de páscoa devem haver exatos 47 dias. Mas eu quero saber não quando é a terça feira de carnaval, mas sim, quando é o domingo que antecede tal terça-feira - ora, trivial, são 49 dias.<br />
<br />
humm...como subtrair de uma data (a data da páscoa), um determinado número de dias (47) ? :scratch:<br />
<br />
Os leitores talvez possam sugerir outras alternativas. Eu usei os comandos &quot;jday&quot; e &quot;j2d&quot; do pacote &quot;jday&quot; do fedora 9 (jday-2.4-3.fc9.i386) (http://sourceforge.net/projects/jday/)<br />
<br />
&quot;basta&quot; então converter uma data (a data da páscoa) para um número Juliano (usando o comando &quot;jday&quot;), subtrair 49 (usando &quot;expr&quot;) e converter esse outro número juliano para uma data do calendário gregoriano (usando &quot;j2d&quot;) !<br />
<br />
Usando o código abaixo vcs podem verificar que o domingo que antecede o carnaval no ano de 2009 sera o dia 22 de fevereiro, &quot;simples&quot; não ?<br />
<div style="margin:20px; margin-top:5px">
	<div class="smallfont" style="margin-bottom:2px">Code:</div>
	<pre class="bbcodeblock" dir="ltr" style="
		margin: 0px;
		margin-right: -99999px;
		padding: 3px;
		border: 1px inset;
		width: 98%;
		height: 162px;
		text-align: left;
		overflow: auto">	year=&quot;2009&quot;
	easter=$(dc -e $year -f /usr/local/lib/easter.dc | sed -e 's/\./-/; s/-\([0-9]\{2\}\)/-\1-/')
	jday=$(jday -d $easter 0:0:0 | cut -d. -f 1)

	# The sunday before carnival is 49 dias before easter (48, in jday account)
	jcar=$(expr $jday - 48)

	carnival=$(j2d $jcar | cut -d' ' -f 1)
	echo &quot;Sunday of carnival is on $carnival&quot;</pre>
</div>Voilá...então se o tal dia for maior ou igual a 15 e menor que 22, então é o terceiro domingo ! Fim da coçeira nos neuronios.<br />
<br />
Então já temos tudo para o compilador de zonas criar um arquivo binário com todas a regra do &quot;Horario Brasileiro de Verão&quot;, incluindo as exceções.<br />
<br />
O arquivo de zonas fica assim:<br />
<br />
<div style="margin:20px; margin-top:5px">
	<div class="smallfont" style="margin-bottom:2px">Code:</div>
	<pre class="bbcodeblock" dir="ltr" style="
		margin: 0px;
		margin-right: -99999px;
		padding: 3px;
		border: 1px inset;
		width: 98%;
		height: 146px;
		text-align: left;
		overflow: auto">#Rule   NAME  FROM  TO    TYPE  	IN   ON 	AT    SAVE  	LETTER/S
Rule	BR    2008  MAX    -    	Oct  Sun&gt;=15  	0:00  1:00 	D
Rule    BR    2008  MAX    regular    	Feb  Sun&gt;=15  	0:00  0:00 	S
Rule    BR    2008  MAX    carnOn3rdSun Feb  Sun&gt;=22  	0:00  0:00 	S


#Zone   NAME            GMTOFF  RULES/SAVE      FORMAT  [UNTIL]
Zone    Brazil/East      -3:00   BR             BR%s</pre>
</div>O arquivo acima faz menção a dois tipos de anos: &quot;regular&quot; é o ano onde o horário de verão começa no terceiro domingo. &quot;carnOn3rdSun&quot; é o ano onde o horário de verão começa no quarto domingo porque o domingo que antecede o carnaval cai no terceiro domingo.<br />
<br />
O programa &quot;zic(1)&quot; irá chamar o programa &quot;yearistype&quot; passando como primeiro argumento um ano, e como segundo argumento o conteúdo do campo &quot;TYPE&quot; (&quot;regular&quot; ou &quot;carnOn3rdSun&quot;)<br />
<br />
humm...(é o último, eu prometo), mas cadê o tal programa &quot;yearistype&quot; ? Basicamente é o código imediatamente acima, em &quot;bash&quot;, com o valor de retorno correto. Não fiz a critica sobre os argumentos de entrada, uma vez que esse código é para ser chamado pelo &quot;zic&quot;, sempre da mesma maneira. Mas quem quiser, fique a vontade para fazer o tratamento dos argumentos de entrada.<br />
<br />
Salve o código abaixo em &quot;/usr/local/bin&quot; ou outro lugar que o &quot;zic&quot; (como root) possa chamar o programa.<br />
Verifique se o seu programa &quot;zic&quot; aceita a especificação do path do comando &quot;yearistype&quot;. No Fedora 9 é possivel usar a chave &quot;-y&quot; para indicar o path do comando &quot;yearistype&quot; (zic -y ~miguel/bin/yearistype zone_file.zic)<br />
<div style="margin:20px; margin-top:5px">
	<div class="smallfont" style="margin-bottom:2px">Code:</div>
	<pre class="bbcodeblock" dir="ltr" style="
		margin: 0px;
		margin-right: -99999px;
		padding: 3px;
		border: 1px inset;
		width: 98%;
		height: 498px;
		text-align: left;
		overflow: auto">#!/bin/bash

#
# Usage: yearistype year type
#

year=$1
type=$2

function check3rdSun() {
	easter=$(dc -e $year -f /usr/local/lib/easter.dc | sed -e 's/\./-/; s/-\([0-9]\{2\}\)/-\1-/')
	jday=$(jday -d $easter 0:0:0 | cut -d. -f 1)

	# The sunday before carnival is 48 dias before easter
	jcar=$(expr $jday - 48)

	carnival=$(j2d $jcar | cut -d' ' -f 1)
	#echo &quot;Sunday of carnival is on $carnival&quot;

	# I want only the day of month
	carday=$(echo $carnival | cut -d- -f3)

	# return 0 if it is the 3rd Sunday, 1 otherwise
	[ &quot;$carday&quot; -ge &quot;15&quot; ] &amp;&amp; [ &quot;$carday&quot; -lt &quot;22&quot; ] &amp;&amp; return 0 || return 1
}

check3rdSun $year
rc=$?

case $type in 
	carnOn3rdSun) 
		#echo &quot;carnOn3rdSun: $carnival $rc&quot;	
		exit $rc
		;;
	*) 
		#echo &quot;regular: $carnival $rc&quot;
		[ &quot;$rc&quot; = &quot;0&quot; ] &amp;&amp; exit 1 || exit 0
esac</pre>
</div>O próximo passo é a compilação do arquivo de zonas e teste:<br />
<div style="margin:20px; margin-top:5px">
	<div class="smallfont" style="margin-bottom:2px">Code:</div>
	<pre class="bbcodeblock" dir="ltr" style="
		margin: 0px;
		margin-right: -99999px;
		padding: 3px;
		border: 1px inset;
		width: 98%;
		height: 306px;
		text-align: left;
		overflow: auto">[root@babylon5 ~]# zic -v -y ~miguel/bin/yearistype ~miguel/src/Brazil_East.zic
[root@babylon5 ~]# zdump -v -c 2016 Brazil/East
...
Brazil/East  Sun Feb 17 01:59:59 2008 UTC = Sat Feb 16 23:59:59 2008 BRD isdst=1 gmtoff=-7200
Brazil/East  Sun Feb 17 02:00:00 2008 UTC = Sat Feb 16 23:00:00 2008 BRS isdst=0 gmtoff=-10800
Brazil/East  Sun Oct 19 02:59:59 2008 UTC = Sat Oct 18 23:59:59 2008 BRS isdst=0 gmtoff=-10800
Brazil/East  Sun Oct 19 03:00:00 2008 UTC = Sun Oct 19 01:00:00 2008 BRD isdst=1 gmtoff=-7200
Brazil/East  Sun Feb 15 01:59:59 2009 UTC = Sat Feb 14 23:59:59 2009 BRD isdst=1 gmtoff=-7200
Brazil/East  Sun Feb 15 02:00:00 2009 UTC = Sat Feb 14 23:00:00 2009 BRS isdst=0 gmtoff=-10800
Brazil/East  Sun Oct 18 02:59:59 2009 UTC = Sat Oct 17 23:59:59 2009 BRS isdst=0 gmtoff=-10800
Brazil/East  Sun Oct 18 03:00:00 2009 UTC = Sun Oct 18 01:00:00 2009 BRD isdst=1 gmtoff=-7200
...
Brazil/East  Sun Feb 26 01:59:59 2012 UTC = Sat Feb 25 23:59:59 2012 BRD isdst=1 gmtoff=-7200
Brazil/East  Sun Feb 26 02:00:00 2012 UTC = Sat Feb 25 23:00:00 2012 BRS isdst=0 gmtoff=-10800
...
Brazil/East  Sun Feb 22 01:59:59 2015 UTC = Sat Feb 21 23:59:59 2015 BRD isdst=1 gmtoff=-7200
Brazil/East  Sun Feb 22 02:00:00 2015 UTC = Sat Feb 21 23:00:00 2015 BRS isdst=0 gmtoff=-10800
...</pre>
</div>Em 2009 o horário de verão termina às 0:00hs do dia 15 (voltando a ser 23:00hs do dia 14), seguindo a regra &quot;regular&quot;.<br />
Já em 2012 ocorre uma das exceções: O horário de verão termina em 26 de fevereiro, que é o quarto domingo, porque o carnaval em 2012 ocorre em 21, sendo o domingo que o antecede, o terceiro domingo do mês.<br />
Outra excessão ocorre em 2015, com o horário de verão terminando no quarto domingo, dia 22.<br />
<br />
Para ver todas as excessões, edite o arquivo &quot;yearistype&quot; des-comentando os dois comandos &quot;echo&quot; que existem dentro do &quot;case&quot; (echo &quot;carnOn3rdSun: $carnival $rc&quot;) e (echo &quot;regular: $carnival $rc&quot;).<br />
Depois disso, execute o comando dentro de um laço for do bash:<br />
<div style="margin:20px; margin-top:5px">
	<div class="smallfont" style="margin-bottom:2px">Code:</div>
	<pre class="bbcodeblock" dir="ltr" style="
		margin: 0px;
		margin-right: -99999px;
		padding: 3px;
		border: 1px inset;
		width: 98%;
		height: 178px;
		text-align: left;
		overflow: auto">[miguel@babylon5 ~]$ for y in $(seq 2008 2100 ) ; do ~/bin/yearistype $y carnOn3rdSun; done
carnOn3rdSun: 2008-02-03 1
carnOn3rdSun: 2009-02-22 1
carnOn3rdSun: 2010-02-14 1
carnOn3rdSun: 2011-03-06 1
carnOn3rdSun: 2012-02-19 0
carnOn3rdSun: 2013-02-10 1
carnOn3rdSun: 2014-03-02 1
carnOn3rdSun: 2015-02-15 0
...</pre>
</div>Todas as linhas que terminam com &quot;0&quot; indicam os anos cujo domingo de carnaval cai no terceiro domingo do mes. São as excessões da regra geral.<br />
<br />
Você pode até compilar o arquivo de zonas com o &quot;zic&quot; deixando o arquivo &quot;yearistype&quot; com os &quot;echos&quot; des-comentados.<br />
Você irá ver a invocação, pelo zic, do comando &quot;yearistype&quot; e a respectiva saida. Experimente !<br />
<br />
Há espaço para otimizações.<br />
Eu suspeito que o uso do comando jday/j2c pode ser eliminado, calculando-se diretamente a data do carnaval, não a data da pascóa.<br />
Para isso, é necessário adaptar o algoritmo de &quot;Meeus/Jones/Butcher&quot; para obter uma data (47+2) dias antes, mas é preciso investigar o algoritmo para verificar se isso é realmente possivel e mais fácil do que usar j2c/jday. Fica a sugestão para os mais aventurosos.<br />
<br />
Foi um dia produtivo. Me diverti resolvendo o problema e depois mais ainda em escrever esse blog que espero ser útil para demonstrar a flexibilidade do unix e o poder dos scripts e utilitários.<br />
<br />
<br />
</div>

]]></content:encoded>
			<dc:creator>marozsas</dc:creator>
			<guid isPermaLink="true">http://www.linuxquestions.org/questions/blog.php?b=1183</guid>
		</item>
	</channel>
</rss>
