Problema com TimeZone - America/Sao_Paulo

8 respostas
R

Pessoal, estou tendo um problema de Calendar com timezone no Java. Não sei se alguém já passou por isso aqui, mas o problema é um tanto quanto estranho. Estou com um projetinho de console que usa Hibernate Annotations e Banco MySQL. Tudo está funcionando muito bem, exceto os campos de data.

Eu estou desenvolvendo meu projeto em uma máquina Windows com Java 6. Porém o projeto irá rodar em uma máquina com Debian, também Java 6 só que o fuso da máquina onde irá rodar o projeto é PST (Pacific Standard Time -08:00 horas). Então, para não ter problemas com fuso (quero sempre saber a hora que rodou pelo horário oficial brasileiro), estou usando a seguinte forma de se obter a data:
Calendar saoPauloDate = Calendar.getInstance(TimeZone.getTimeZone("America/Sao_Paulo"));
Quando eu rodo em minha máquina Windows esse código eu tenho a seguinte data:
21/2/2011 13:42
// Método de formatação de data omitido

No servidor com Debian, a hora também está voltando correta. Só que quando essa hora é persistida no Banco de Dados MySQL, volta-se 5 horas! Ou seja, se aqui em São Paulo é 10h00 e a aplicação roda, a data persistida é de 05h00 da madrugada!

Para tirar as dúvidas sobre o retorno de datas na minha máquina Windows e na máquina Debian, montei o seguinte código e rodei nos dois ambientes. Exportei via Eclipse como Runnable Jar File e executei usando o comando java -jar dateTest.jar. Veja o resultado abaixo:

Código rodado:
import java.util.Calendar;
import java.util.TimeZone;

public class Main {

	public static void main(String[] args) {

		Calendar systemDate = Calendar.getInstance();
		Calendar saoPauloDate = Calendar.getInstance(TimeZone.getTimeZone("America/Sao_Paulo"));
		Calendar brazilEastDate = Calendar.getInstance(TimeZone.getTimeZone("Brazil/East"));

		System.out.println("Sem Timezone: " + Main.getFormatedDate(systemDate));
		System.out.println("America/São_Paulo: " + Main.getFormatedDate(saoPauloDate));
		System.out.println("Brazil/East: " + Main.getFormatedDate(brazilEastDate));
	}

	private static String getFormatedDate(Calendar date) {
		StringBuffer formattedDate = new StringBuffer();
		formattedDate.append(date.get(Calendar.DAY_OF_MONTH)).append("/");
		formattedDate.append(date.get(Calendar.MONTH) + 1).append("/");
		formattedDate.append(date.get(Calendar.YEAR)).append(" ");
		formattedDate.append(date.get(Calendar.HOUR_OF_DAY)).append(":");
		formattedDate.append(date.get(Calendar.MINUTE));
		return formattedDate.toString();
	}
}
Resultado na máquina Windows:
Sem Timezone: 21/2/2011 13:42
America/São_Paulo: 21/2/2011 13:42
Brazil/East: 21/2/2011 13:42
Resultado na máquina Debian:
Sem Timezone: 21/2/2011 8:49
America/São_Paulo: 21/2/2011 13:49
Brazil/East: 21/2/2011 13:49
Desconsiderem as diferenças de minutos. Quero que vejam que os resultados batem. Para finalizar o teste, rodei o comando date do Linux via SSH para obter a data e horário da máquina servidora e obtive o seguinte resultado:
Mon Feb 21 08:51:05 PST 2011

Chego a conclusão que o problema deva ser com o MySQL. Alguma configuração errada ou talvez anotação errada no Hibernate. Não sei. Alguém sabe o que pode estar acontecendo? É a primeira vez que trabalho com fuso horário no Java e estou apanhando demais.

Minha classe Hibernate que é persistida:
@Entity
@Table(name = "tm_app_execution")
public class AppExecution implements Serializable {

	private static final long serialVersionUID = -1461955698187380141L;
	
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "APP_EXECUTION_ID")
	private Long appExecutionID;
	
    @Column(name = "DATE_STARTED", nullable = false)
	private Calendar dateStarted;
	
    @Column(name = "DATE_FINISHED", nullable = true)
	private Calendar dateFinished;
	
    @Column(name = "EXECUTION_TIME", nullable = true)
	private Integer executionTime;
	
    @Column(name = "CONSOLE_PARAMS_USED", nullable = true)
	private String consoleParamsUsed;
	
    @Column(name = "HAVE_ERROR", nullable = true)
	private Boolean haveError;

	public Long getAppExecutionID() {
		return appExecutionID;
	}

	public void setAppExecutionID(Long appExecutionID) {
		this.appExecutionID = appExecutionID;
	}

	public Calendar getDateStarted() {
		return dateStarted;
	}

	public void setDateStarted(Calendar dateStarted) {
		this.dateStarted = dateStarted;
	}

	public Calendar getDateFinished() {
		return dateFinished;
	}

	public void setDateFinished(Calendar dateFinished) {
		this.dateFinished = dateFinished;
	}

	public Integer getExecutionTime() {
		return executionTime;
	}

	public void setExecutionTime(Integer executionTime) {
		this.executionTime = executionTime;
	}

	public String getConsoleParamsUsed() {
		return consoleParamsUsed;
	}

	public void setConsoleParamsUsed(String consoleParamsUsed) {
		this.consoleParamsUsed = consoleParamsUsed;
	}

	public Boolean getHaveError() {
		return haveError;
	}

	public void setHaveError(Boolean haveError) {
		this.haveError = haveError;
	}
}

No MySQL estou usando InnoDB.

Resumindo o problema após toda essa explicação: Quando vou persistir um objeto Calendar usando Hibernate e Timezone America/Sao_Paulo em um banco de dados MySQL rodando em uma máquina Linux Debian configurado com o fuso horário PST, ele está gravando no horário PST ao invés de usar o fuso America/Sao_Paulo.

Se alguém precisar de mais alguma informação, é só pedir. Esse erro está bem chato de resolver.

Obrigado

8 Respostas

felipeguerra

o que retorna no sysdate do banco?

R

Vamos lá.

Executando o NOW() = 2011-02-21 09:27:07
Executando o CURDATE() = 2011-02-21 00:00:00
Executando o UTC_DATE() = 2011-02-21 17:28:23
Executando o UTC_TIMESTAMP() = 2011-02-21 17:29:25

Bem estranho.

felipeguerra

mais uma coisa, posta aí o SQL que é gerado no INSERT pelo Hibernate.

R

Aí ferrou, porque o Hibernate usa PreparedStatments, da uma olhada:

insert into tm_app_execution (CONSOLE_PARAMS_USED, DATE_FINISHED, DATE_STARTED, EXECUTION_TIME, HAVE_ERROR) values (?, ?, ?, ?, ?)

Essa tabela também sofre updates, no entanto o problema com as datas está ocorrendo em todas as tabelas cuja colunas estão mapeadas como Calendar.

update tm_app_execution set CONSOLE_PARAMS_USED=?, DATE_FINISHED=?, DATE_STARTED=?, EXECUTION_TIME=?, HAVE_ERROR=? where APP_EXECUTION_ID=?

L

bom, provavelmente você terá que configurar o timezone também no mysql
http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html

felipeguerra

configura o log para o nível de DEBUG e pega o que ele passa como parâmetro.

R

Recuperei os valores de insert do Hibernate, e para minha surpresa ele está inserindo a data errada. Não sei porque:

{dateFinished=null, consoleParamsUsed=, executionTime=null, dateStarted=2011-02-21 15:34:45, haveError=null, appExecutionID=29}

Rodei agora as 20h34!

R

lordcarlos:
bom, provavelmente você terá que configurar o timezone também no mysql
http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html

Eu não tenho o controle total do Banco de Dados. É um shared server e por isso meus poderes são limitados. Mesmo assim, tem como eu setar o timezone somente para eu banco?

Outra alternativa seria eu usar um timezone diferente na aplicação de forma que bata com o horário de Brasília.

Valeu

Criado 21 de fevereiro de 2011
Ultima resposta 23 de fev. de 2011
Respostas 8
Participantes 3