De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
Colaboração: Miguel Angelo Rozsas
Data de Publicação: 20 de September de 2008
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 "Grande Colisor de Hádrons" produza um buraco negro que saia do controle e "engula" o planeta ou talvez até o inicio do mandato do próximo presidente, o que ocorrer primeiro.
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.
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:
#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>=15 0:00 1:00 D Rule BR 2009 MAX - Feb Sun>=15 0:00 0:00 S #Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL] Zone Brazil/East -3:00 BR BR%s
e a compilação do arquivo e uma rápida verificação retornaria:
[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 ...
Como podem ver, em 2008, o horário de verão começaria em 19 de Outubro e terminaria em 15 de fevereiro.
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.
Já conhecia a man page do zic(1)
há tempos. Lá fala de uma função
yearistype
que é chamada para cada ano, e se retornar verdadeiro (true)
a respectiva linha no time zone é aplicada.
Humm...então essa função poderia então ser usada para tratar a exceção da regra do "Horário de Verão Brasileiro a partir de 2008". Para tanto, "basta" 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 ?
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).
Humm...complicado heim ?
Bem, como Google é pai e wikipedia é mãe, acabei encontrando o algoritmo elaborado por "Meeus/Jones/Butcher" válido para quem usa o calendário gregoriano (somos nós ! - Os católicos ortodoxos orientais usam o calendário Juliano) (http://en.wikipedia.org/wiki/Computus#Algorithms)
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....
Implementei o algoritmo de "Meeus/Jones/Butcher" em dc(1)
. (Salve os
comandos dc
abaixo em /usr/local/lib/easter.dc)
# 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
...e use-o como:
[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 ~]$
(as datas da páscoa dos anos passados na CLI são retornadas no formato yyyy.mmdd
)
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 "Sol" 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.
humm...como subtrair de uma data (a data da páscoa), um determinado número de dias (47) ? :scratch:
Os leitores talvez possam sugerir outras alternativas. Eu usei os comandos jday
e j2d
do pacote jday
do fedora 9 (jday-2.4-3.fc9.i386) (http://sourceforge.net/projects/jday/)
"basta" então converter uma data (a data da páscoa) para um número Juliano (usando o comando jday
), subtrair 49 (usando expr
) e converter esse outro número juliano para uma data do calendário gregoriano (usando j2d
) !
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, "simples" não ?
year="2009" 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 "Sunday of carnival is on $carnival"
Voilá...então se o tal dia for maior ou igual a 15 e menor que 22, então é o terceiro domingo ! Fim da coceira nos neuronios.
Então já temos tudo para o compilador de zonas criar um arquivo binário com todas a regra do "Horario Brasileiro de Verão", incluindo as exceções.
O arquivo de zonas fica assim:
#Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule BR 2008 MAX - Oct Sun>=15 0:00 1:00 D Rule BR 2008 MAX regular Feb Sun>=15 0:00 0:00 S Rule BR 2008 MAX carnOn3rdSun Feb Sun>=22 0:00 0:00 S #Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL] Zone Brazil/East -3:00 BR BR%s
O arquivo acima faz menção a dois tipos de anos: "regular" é o ano onde o horário de verão começa no terceiro domingo. "carnOn3rdSun" é 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.
O programa zic(1)
irá chamar o programa yearistype
passando como
primeiro argumento um ano, e como segundo argumento o conteúdo do campo
TYPE
("regular" ou carnOn3rdSun
)
humm...(é o último, eu prometo), mas cadê o tal programa
yearistype
? Basicamente é o código imediatamente acima, em "bash",
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 zic
, sempre da
mesma maneira. Mas quem quiser, fique a vontade para fazer o tratamento dos
argumentos de entrada.
Salve o código abaixo em /usr/local/bin
ou outro lugar que o zic
(como root) possa chamar o programa. Verifique se o seu programa zic
aceita a especificação do path do comando yearistype
. No Fedora 9 é
possivel usar a chave -y
para indicar o path do comando yearistype
(zic -y ~miguel/bin/yearistype zone_file.zic)
#!/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 "Sunday of carnival is on $carnival" # I want only the day of month carday=$(echo $carnival | cut -d- -f3) # return 0 if it is the 3rd Sunday, 1 otherwise [ "$carday" -ge "15" ] && [ "$carday" -lt "22" ] && return 0 || return 1 } check3rdSun $year rc=$? case $type in carnOn3rdSun) #echo "carnOn3rdSun: $carnival $rc" exit $rc ;; *) #echo "regular: $carnival $rc" [ "$rc" = "0" ] && exit 1 || exit 0 esac
O próximo passo é a compilação do arquivo de zonas e teste:
[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 ...
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 "regular". 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. Outra excessão ocorre em 2015, com o horário de verão terminando no quarto domingo, dia 22.
Para ver todas as excessões, edite o arquivo yearistype
des-comentando os
dois comandos echo
que existem dentro do case
(echo ``carnOn3rdSun:
$carnival $rc) e (echo ``regular: $carnival $rc
).
Depois disso, execute o comando dentro de um laço for do bash:
[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 ...
Todas as linhas que terminam com 0
indicam os anos cujo domingo de carnaval
cai no terceiro domingo do mes. São as excessões da regra geral.
Vocè pode até compilar o arquivo de zonas com o zic
deixando o arquivo
yearistype
com os echos
des-comentados. Você irá ver a invocação,
pelo zic, do comando yearistype
e a respectiva saida. Experimente !
Há espaço para otimizações.
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.
Para isso, é necessário adaptar o algoritmo de "Meeus/Jones/Butcher" 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.
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.
# 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 #!/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 "Sunday of carnival is on $carnival" # I want only the day of month carday=$(echo $carnival | cut -d- -f3) # return 0 if it is the 3rd Sunday, 1 otherwise [ "$carday" -ge "15" ] && [ "$carday" -lt "22" ] && return 0 || return 1 } check3rdSun $year rc=$? case $type in carnOn3rdSun) #echo "carnOn3rdSun: $carnival $rc" exit $rc ;; *) #echo "regular: $carnival $rc" [ "$rc" = "0" ] && exit 1 || exit 0 esac
This policy contains information about your privacy. By posting, you are declaring that you understand this policy:
This policy is subject to change at any time and without notice.
These terms and conditions contain rules about posting comments. By submitting a comment, you are declaring that you agree with these rules:
Failure to comply with these rules may result in being banned from submitting further comments.
These terms and conditions are subject to change at any time and without notice.
Comentários