Java e o horário de verão
Publicado em Developer
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
- Pull requests em modo raiz - sex, 22 de dezembro de 2023, 09:57:09 -0300
- Qual a idade do seu repositório? - ter, 27 de dezembro de 2022, 12:50:35 -0300
- Utilizando ctags em projetos Rails mais recentes - qui, 24 de junho de 2021, 08:23:43 -0300
- Fazendo o seu projeto brotar - seg, 15 de julho de 2019, 08:57:05 -0300
- Learn Functional Programming with Elixir - sex, 02 de março de 2018, 18:47:13 -0300
- Ambiente mínimo - Driver Driven Development - qua, 23 de agosto de 2017, 15:15:03 -0300
- Ambiente mínimo - repositórios de código - dom, 16 de abril de 2017, 13:02:14 -0300
- Ambiente mínimo - terminal e navegador - dom, 02 de abril de 2017, 21:43:29 -0300
- Utilizando muitas gems no seu projeto? - sáb, 29 de outubro de 2016, 11:57:55 -0200
- Desenvolvedores e inteligência artificial - seg, 11 de julho de 2016, 09:09:38 -0300
Zé, eu utilizo agora só os setString() e getString() mesmo em campos data, e tem dado certo. O duro que perdemos uma ótima chance de acabar com essa ladainha de vez se nosso exmo. Presidente não tivesse condicionado o final do horário de verão dependente do Carnaval, não é mesmo?
ja estou ficando louco com essa confusao miseravel que o java faz com locale,timezone, horario de verao. estou fazendo manipulacoes de datas como subtracao e soma e o java torna isso algo de outro mundo. estou a mais de 3 dias tentando sem sucesso algum.
voce setou a coluna do banco como string é isso? nao entendi bem sua solução.
Eu resolvi em grande parte, apesar que tem uns XP que ainda pipocam com isso, mas a solução está lá no cliente. Vou para lá amanhã e dou uma olhada para "refrescar" as idéias, apesar de poder acessar remoto, hoje é Domingão né.
Olá Eustaquio,
Você conseguiu resolver este problema do horário de verão apenas com o Java? sem utilizar JNI? Como você fez?