Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
By marozsas at 2008-09-15 06:19
(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 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:
Code:
#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:
Code:
[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)
Code:
# 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:
Code:
[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 ?
Code:
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 coçeira 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:
Code:
#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)
Code:
#!/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:
Code:
[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:
Code:
[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.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.