Estou curioso como o java.util.Date, java.sql.Date e o Calendar calculam as horas no horário de verão.
Como vocês sabem algum esperto me coloca o mardito para começar bem no finados, Terça-Feira, legal né, e termina no dia 20 de Fevereiro.
Fiz um programinha para criar uma TimeZone customizada para dar conta disso (vocês sabiam que o Java não pega essas configurações do SO, pelo menos no Linux, e sim de umas configurações malucas que ele tem no $JAVA_HOME/jre/lib/zi? vai entender…)
e tentar fazer um cálculo mais exato das datas (não reparem que está em inglês que eu postei em developers.sun.com):
import java.sql.*;
import java.util.*;
import java.text.*;
public class DateParsing {
public static void main(String args[]){
// here I run the program the default way
System.out.println("
BEFORE
--------------------------------------------------------------------");
showDate("01/11/2004");
showDate("02/11/2004");
showDate("03/11/2004");
// here I create a new TimeZone, with user defined
// start and end rules
SimpleTimeZone mtz = new SimpleTimeZone(TimeZone.getDefault().getRawOffset(),"America/Sao_Paulo");
mtz.setStartRule(10,2,0); // November, 2, 00:00, starts, goes to November, 3, 01:00, right?
mtz.setEndRule(1,20,0); // February, 20, 00:00, ends, goes to February, 19, 23:00, right?
TimeZone.setDefault(mtz); // set this zone as the default one
// now print the values
System.out.println("
AFTER
---------------------------------------------------------------------");
showDate("01/11/2004");
showDate("02/11/2004");
showDate("03/11/2004");
}
public static void showDate(String date){
try{
SimpleDateFormat fmt = new SimpleDateFormat("dd/MM/yyyy");
// regular Date
java.util.Date dt = fmt.parse(date);
System.out.println("java.util.date ("+date+") : "+dt);
// SQL date
String day = date.substring(0,2);
String month= date.substring(3,5);
String year = date.substring(6);
String sql = year+"-"+month+"-"+day;
java.sql.Date sd = java.sql.Date.valueOf(sql);
System.out.println("java.sql.date ("+sql+") : "+sd);
// Calendar date
Calendar cal = Calendar.getInstance();
cal.set(cal.DAY_OF_MONTH,Integer.parseInt(day));
cal.set(cal.MONTH,Integer.parseInt(month)-1);
cal.set(cal.YEAR,Integer.parseInt(year));
cal.set(cal.HOUR_OF_DAY,0);
cal.set(cal.MINUTE,0);
cal.set(cal.SECOND,0);
System.out.println("calendar date ("+sql+") : "+cal.getTime());
// Calendar date plus one hour
cal = Calendar.getInstance();
cal.set(cal.DAY_OF_MONTH,Integer.parseInt(day));
cal.set(cal.MONTH,Integer.parseInt(month)-1);
cal.set(cal.YEAR,Integer.parseInt(year));
cal.set(cal.HOUR_OF_DAY,1);
cal.set(cal.MINUTE,0);
cal.set(cal.SECOND,0);
System.out.println("calendar date +("+sql+") : "+cal.getTime());
System.out.println("");
}catch(Exception e){
System.err.println("ERROR SHOWING "+date+" : "+e.getMessage());
}
}
}
Rodando esse programa eu tenho esse output:
BEFORE
--------------------------------------------------------------------
java.util.date (01/11/2004) : Mon Nov 01 00:00:00 BRST 2004
java.sql.date (2004-11-01) : 2004-11-01
calendar date (2004-11-01) : Mon Nov 01 00:00:00 BRST 2004
calendar date +(2004-11-01) : Mon Nov 01 01:00:00 BRST 2004
java.util.date (02/11/2004) : Tue Nov 02 00:00:00 BRST 2004
java.sql.date (2004-11-02) : 2004-11-02
calendar date (2004-11-02) : Tue Nov 02 00:00:00 BRST 2004
calendar date +(2004-11-02) : Tue Nov 02 01:00:00 BRST 2004
java.util.date (03/11/2004) : Wed Nov 03 00:00:00 BRST 2004
java.sql.date (2004-11-03) : 2004-11-03
calendar date (2004-11-03) : Wed Nov 03 00:00:00 BRST 2004
calendar date +(2004-11-03) : Wed Nov 03 01:00:00 BRST 2004
AFTER
---------------------------------------------------------------------
java.util.date (01/11/2004) : Mon Nov 01 00:00:00 BRT 2004
java.sql.date (2004-11-01) : 2004-11-01
calendar date (2004-11-01) : Mon Nov 01 00:00:00 BRT 2004
calendar date +(2004-11-01) : Mon Nov 01 01:00:00 BRT 2004
java.util.date (02/11/2004) : Mon Nov 01 23:00:00 BRT 2004
java.sql.date (2004-11-02) : 2004-11-01
calendar date (2004-11-02) : Mon Nov 01 23:00:00 BRT 2004
calendar date +(2004-11-02) : Tue Nov 02 01:00:00 BRST 2004
java.util.date (03/11/2004) : Wed Nov 03 00:00:00 BRST 2004
java.sql.date (2004-11-03) : 2004-11-03
calendar date (2004-11-03) : Wed Nov 03 00:00:00 BRST 2004
calendar date +(2004-11-03) : Wed Nov 03 01:00:00 BRST 2004
Com a time zone default, tudo funcionou ok (até que entrasse no daylight da zona definida pelo Java né), mas olhem os valores DEPOIS que eu alterei a time zone com os valores corretos do horário de verão:
-
1 de Novembro está ok;
-
2 de Novembro (quando começa o horário de verão) SEMPRE é calculado como 1 de Novembro, no java.util.Date, java.sql.Date e Calendar. Quando eu seto a HOUR_OF_DAY do Calendar para 1, fica correto. E deêm uma olhada na timestamp do java.util.Date and Calendar: 23:00! Mas por que ele decrementa a hora, se ele precisa incrementar? Desse modo, um simples parse usando java.util.Date, java.sql.Date e Calendar resulta em um erro. Eu tive alguns valores errados inseridos em umas tabelas do banco de dados por causa desse cálculo maluco.
-
3 de Novembro está ok de novo;
Então, o que acontece? Por que quando eu digo “ei, dê um parse nessa string ‘02/11/2004’” o Java insiste que é 1 de Novembro? Me parece que as timestamps criadas sem hora definida explicitamente pegam por default 00:00, mas por que o horário de verão está decrementando e não adicionando uma hora ali?
Ah, e cadê a 00:00 do dia 2 nesse rolo todo? E por que dia 3 está correto?
Arrrgh. 
Obrigado!
