JSF: Conversão de datas e problemas com fuso horário
Configure corretamente o timezone da sua aplicação ao trabalhar com f:convertDateTime
Uma das funcionalidades mais legais e úteis do JSF são seus conversores, pois através deles conseguimos converter de maneira transparente dados submetidos da tela para aplicação e enviados da aplicação para tela. Por exemplo, imagine que numa aplicação exclusivamente brasileira temos um formulário com um input para receber a data de nascimento de um cliente, como abaixo:
<h:inputText id="data" value="#{bean.cliente.dataDeNascimento}" />
Embora o JSF saiba como converter automaticamente os valores submetidos para os tipos básicos do Java, alguns de seus conversores precisam de informações adicionais para que a conversão ocorra adequadamente, como datas e horários, já que o formato entrado pelo usuário pode variar de acordo com a localidade ou idioma da aplicação. No Brasil, o formato padrão é “dia/mês/ano”, enquanto nos Estados Unidos o formato é “mês/dia/ano”.
Para resolver isso, o JSF nos fornece a tag f:convertDateTime
para personalizar a conversão de data e hora. Como o formato de data da nossa aplicação deve seguir o modelo brasileiro, precisamos adicionar o conversor no corpo do componente h:inputText
e informar no atributo pattern
o formato da data que queremos utilizar, como a seguir:
<h:inputText id="data" value="#{bean.cliente.dataDeNascimento}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:inputText>
A partir de agora, o usuário deverá entrar com uma data no formato “07/03/1984” e o JSF saberá exatamente como convertê-la para o tipo java.util.Date. Vale salientar, que a sintaxe da data/hora informada no atributo pattern
segue o mesmo padrão usado pela API Java java.text.SimpleDateFormat.
Data gravada errada no banco de dados
Digamos que ao submeter o formulário a data entrada pelo usuário seja gravada no banco de dados que está instalado no mesmo servidor físico da aplicação. Se observarmos as datas gravadas no banco, perceberemos que elas estão incorretas. Na verdade, elas estão sendo gravadas com 2 ou 3h de atraso. Isso se deve pois o fuso horário (timezone) da aplicação não está sincronizado com o fuso horário do banco de dados nem do sistema operacional.
Por padrão, ao converter datas, o JSF utiliza o fuso horário UTC, porém no Brasil, nós trabalhamos com o fuso horário UTC-3. Por esse motivo, as datas gravadas no banco de dados estão erradas.
Se o usuário entrar a data "07/03/1984" o que será de fato gravado no banco de dados será o valor "06/03/1984 21:00:00". São 3h a menos em cima do valor "07/03/1984 00:00:00", pois no Brasil o fuso horário é UTC-3.
Uma maneira de resolver isso é informando diretamente no conversor qual fuso horário queremos trabalhar, como a seguir:
<f:convertDateTime pattern="dd/MM/yyyy" timeZone="America/Fortaleza" />
O problema dessa abordagem é que teríamos que informar o fuso horário em todos os conversores da aplicação, o que cedo ou tarde poderia ser esquecido por algum desenvolvedor desatento.
Usando o fuso horário do sistema operacional
É uma prática muito comum que a aplicação utilize o mesmo fuso horário do sistema operacional do servidor na qual ela está hospedada. Na verdade, esse é o comportamento padrão da própria JVM. Se a aplicação seguir o fuso horário da JVM consequentemente ela estará seguindo o fuso horário configurado no sistema operacional.
Deste modo, para que o JSF siga o fuso horário do sistema operacional, precisamos habilitar uma configuração no projeto. Para isso, basta adicionar o parâmetro de contexto abaixo no arquivo web.xml
da aplicação:
<web-app ...>
<!-- outras configurações -->
<context-param>
<param-name>
javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE
</param-name>
<param-value>true</param-value>
</context-param>
</web-app>
O parâmetro DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE
só funciona a partir do JSF 2. Infelizmente para o JSF 1.2 ou inferior a solução é definir o fuso horário em cada conversor ou criar seu conversor customizado e registrá-lo como padrão para objetos do tipo java.util.Date
no JSF.
Esta configuração só terá efeito para os novos cadastros feitos na aplicação. As datas que já foram gravadas no sistema continuarão inconsistentes. Caso você queira corrigi-las, terá que alterar os registros diretamente no banco de dados, seja com ajuda de um DBA ou através do uso de migrations.
Trabalhar com fuso horário é delicado
Ter uma aplicação que se preocupe com diferentes fusos horários não é uma tarefa tão simples assim. É preciso garantir que todos os componentes que se integram com a aplicação estejam usando o mesmo fuso horário, desde a aplicação, a JVM, até o servidor front-end e o banco de dados. Nesse caso, a boa prática diz que devemos trabalhar com o timezone UTC (GMT-0) em todos os componentes e, somente no momento de apresentar a data em uma página ou relatório, é que nos preocuparíamos de formatá-la de acordo com o fuso horário padrão da aplicação ou mesmo do usuário logado.
Ainda que trabalhássemos com um único fuso horário é mais do que recomendado que todos os componentes usem o mesmo fuso horário, seja ele UTC ou o fuso horário de onde está localizado sua aplicação.
Se sua aplicação pode se dar ao luxo de trabalhar com um fuso horário único e padrão então a solução nesse post é suficiente para você. Na minha opinião, a maioria das aplicações se encontram nesse cenário, tanto que esta solução é abordada de forma prática no nosso curso de JSF 2 e Spring.
E você, já teve algum problema com fuso horário usando JSF? Que solução você utilizou?
Desenvolvedor e instrutor na TriadWorks