Eustáquio Rangel

Desenvolvedor, pai, metalhead, ciclista

Java e o horário de verão

Publicado em Developer


Eu acho que deveria ter escrito isso antes do horário de verão começar, mas antes tarde do que nunca. Eu peguei a situação antes mas acabei atrasando o texto por causa dos probleminhas que acabaram aparecendo.
Antes de começar o resumo da coisa, dá para se ter uma idéia aqui e aqui.

Para encurtar a conversa: o Java não pega suas configurações de timezones e de horário de verão do sistema operacional. Não adianta você corrigir isso no sistema operacional e deixar redondinho que ele vai dar pau.

Uma medida que poderia funcionar seria a criação de uma SimpleTimeZone com as configurações do horário de verão, que é definido todo ano com o mesmo critério que o sujeito escolhe a cor da meia que vai por de manhã (ainda bem que eu só uso meias brancas eheheh :-). Isso teoricamente poderia funcionar, e até funciona até certo ponto, se não fosse um comportamento interessante dos datas do Java (java.sql.Date, java.util.Date, Calendar e afins). Olhem que interessante.

Vamos pegar de exemplo o horário de verão atual, que começa dia 02/11/2004 e termina 20/02/2005. Experimentem compilar e rodar esse código (desculpem a indentação, está dentro de um XML e não quero mudar muita coisa no XSLT dele por enquanto):

import java.sql.*;
import java.util.*;
import java.text.*;

public class Dt{
public static void main(String args[]){
try{
// define a nossa TimeZone customizada
SimpleTimeZone tmz = new SimpleTimeZone(TimeZone.getDefault().getRawOffset(),"MTZ");
tmz.setStartRule(10,2,0);// começa dia 02/11
tmz.setEndRule(1,20,0);// termina dia 20/02
TimeZone.setDefault(tmz);// altera a TimeZone default

SimpleDateFormat fd = new SimpleDateFormat("dd/MM/yyyy");
java.util.Date ud = fd.parse("02/11/2004");
System.out.println("java.util.Date: "+ud);

java.sql.Date sd = java.sql.Date.valueOf("2004-11-02");
System.out.println("java.sql.Date : "+sd);
}catch(Exception eError){
System.err.println("erro:"+eError.getMessage());
}
}
}

Vocês podem até falar "mas sem alterar a TimeZone default está funcionando", mas experimentem deixar na default no período que começou o horário de verão em algum ponto (no Java ou no horário real) mas no outro não e vão entender o que estou falando. :-)

Mas o X da questão é que o output desse programa é algo como isso:

java.util.Date: Mon Nov 01 23:00:00 GMT-03:00 2004
java.sql.Date : 2004-11-01

Ele transforma o dia 02/11 em dia 01/11! E ainda a hora para 23:00:00! Não sei por que cargas d'água que faz isso, e somente no dia que começa o horário de verão. Experimentem criar as datas como dia 03/11 e tudo fica correto.

Me parece que há algum bug maluco referente à 00:00. E como toda data em Java, se não especificada a hora, é assumido 00:00, toda vez que você se referenciar ao dia 02/11 no construtor de uma data ele vai pensar que é dia 01/11. Para programas que fazem log ou inserem essas datas em algum banco de dados já dá para imaginar o estrago que faz uma coisa dessas.

"Mas peraí, dia 02/11/2004 00:00 nunca vai existir!". Eu sei, o Linux sabe (experimentem ajustar a data do sistema operacional para 02/11/2004 00:00 e vão receber uma mensagem de 'invalid date'), talvez o Java saiba também mas esteja meio bebâdo, por que ele calcula errado.

Para tentar contornar isso, parei de fazer qualquer tipo de conversão de strings vindas de campos texto para java.sql.Date, java.util.Date ou mesmo Calendar. Alterei o formato de input do banco para receber datas como strings formatadas com DD/MM/YYYY e pronto. No Oracle, é só usar o ALTER SESSION SET NLS_DATE_FORMAT="DD/MM/YYYY" e está tudo certo.
No more mister ResultSet.setDate(i,d).

E também fiz uma lib em C para interfacear com o Java, no padrão JNI, para pegar a data e hora corrente do sistema operacional, sem levar em conta os cálculos malucos do Java (inclusive tem aritmética de data e hora).

Preferi fazer isso do que alguma chamada à Runtime.exec por que achei que fica mais leve.
Vou disponibilizar essa lib aqui semana que vem, agora quero ir para casa que essa semana foi puxada (apesar de só ter três dias). Bom fim de semana para vocês. E não se esqueçam de dar uma revisadinha em códigos Java sensíveis à toda essa ladainha de data, hora e horário de verão.:-)


Comentários

Comentários fechados.

Artigos anteriores