06 dez 2011

JodaTime – Java Date que funciona!

JodaTime

Não existe segredo quando se fala da implementação de datas no Java: é ruim de usar. Alguns chegam a dizer que é errado usar inclusive, mas não serei tão extremo.
A API de datas do Java é ruim por vários motivos, como por exemplo, é mal documentada, não é Thread Safe, é difí­cil de manipular datas, e o comportamento nem sempre é o esperado.
Vamos ver como susbtituir a API de datas que vem Out of the Box no Java, por uma mais efetiva, amigável e confiável: JodaTime.

Lembrando do Calendar

Todo programador Java conhece o Calendar, e sabe que para usá-lo, basta seguir o Design Pattern singleton, ou seja, basta chamar o método de classe em Calendar que devolve a instância única do sistema para o Calendar.


Problema: não funciona.


Não funciona porque se a instância é singleton, e não utiliza threadlocking no código, então não é uma instância ThreadSafe. Logo toda vez que chamamos o getInstance() do Calendar, obtemos uma nova instância. Para ilustrar, crie um teste em JUnit 4 com o seguinte código:

1
2
3
4
5
6
7
    Calendar calendar = Calendar.getInstance();
   
    System.out.println(calendar.getTime());
   
    Calendar calendar2 = Calendar.getInstance();
   
    Assert.assertTrue(calendar == calendar2);



Rode o teste e veja a barra do JUnit ficar vermelha. O comparador ‘==’ usado em objetos, compara pelo endereço de memória, o que deveria ser o mesmo se fosse seguido o padrão singleton de verdade.
Pra piorar, todos os métodos que alteram as intâncias do Date estão expostos (por mais que estejam depreciados) para mantêr compatibilidade com versões anteriores da VM. Portanto o Date também não é ThreadSafe, pois não existe controle de concorrência em sua implementação.
Agora vamos deixar o Date e o Calendar de lado, e vamos ao JodaTime.

DateTime

O JodaTime diferencia muito bem os conceitos de data, instante de tempo, perí­odo, etc. A classe mais básica (interface no caso) é a ReadableInstant. Não precisa dizer que todas as modelagens de data implementam essa interface, permitindo comparar todos os tipos de modelagem de tempo pontuais. Um perí­odo não descreve um único instante ou ponto no tempo, por exemplo.
DateTime é talvez o ReadableInstant mais conhecido, e funciona muito parecido com o Date do Java.
Fatores que tornam o DateTime mais amigável são: é ThreadSafe pois é imutável, é muito bem documentado, e é muto fácil realizar operações com data. Vamos escrever um pouco de código para entender o que se passa.
Comece criando um DateTime. Como no Java, este DateTime criado possui o instante atual do sistema. Em seguida para efeito de teste (o teste pode falhar dependendo de quando for executado), adicione um dia na data criada, e verifique que o novo date aponta para amanhã:

1
2
3
4
5
    DateTime date = new DateTime();

    date = date.plusDays(1);

    Assert.assertEquals(new DateTime().getDayOfYear() + 1, date.getDayOfYear());



Repare que tive que reassociar o date para que ele pudesse ser alterado, afinal DateTime é imutável, o mesmo comportamento que o BinInteger possui. Repare também que pra adicionar um dia, basta chamar plusDays. Este método já se encarrega de fazer toda a lógica de adicionar um dia na data, como por exemplo mudar o mês ou ano se for preciso, por isso se esse teste for rodado no dia 31 de dezembro, ele irá falhar pois o DateTime irá adicionar mais um dia a data, e perceberá que se trata do ano seguinte, e portanto getDayOfYear irá devolver ‘1’, e não ‘366’ ou ‘365’ como esperado.
O JodaTime também trata anos bissestos e horário de verão se for selecionado o fuzo correto.
Existe uma API bem completa em DateTime para manipular todos os campos possí­veis da data, sendo assim fica muito mais fácil iterar ao longo dos dias, sem precisar delegar pro Calendar a tarefa, e depois recuperar o resultado.
Não vou abordar muito da API do JodaTime, pois está muito bem documentada e existem muitos exemplos nas internet. O objetivo desse post é tratar do assunto do próximo tópico.

JodaTime e Hibernate

Pior que manipular datas, é persistir datas. Cada banco persiste data do seu próprio jeito, e cada implementação de ORM trata o Date do seu próprio jeito. Mas se você está utilizando o Hibernate, o JodaTime tem uma solução de padronização pra você: JodaTime Hibernate.
Com o JodaTime Hibernate é possí­vel mapear diversos tipos de representação de data em suas classes Java, com ou sem TimeZone, como String ou bigint, como perí­odo ou duração, etc.
Para se ter uma idéia do que é possí­vel, basta verificar a documentação online.
E para utiliza é muito fácil. Imagine que você tenha uma entidade com um campo DateTime, que se chama entryDate, portanto temos o getter:

1
2
3
4
    @Column(nullable = false)
    public DateTime getEntryDate() {
        return entryDate;
    }



Para tornar este DateTime uma data que é padrão do banco que será utilizado, por exemplo, basta adicionar a seguinte anotação:

1
2
3
4
5
    @Column(nullable = false)
    @Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
    public DateTime getEntryDate() {
        return entryDate;
    }



Estamos falando para o hibernate utilzar o tipo de coluna descrito pelo PersistentDateTime, e utilizar o mesmo para converter a data novamente para DateTime quando for recuperado.
Caso você esteja fazendo engenharia reversa de algum banco, recomendo ler a descrição de todos os tipo disponí­veis pra fazer a melhor escolha.
Com isso conseguimos obter todos os benefí­cios do JodaTime em nossas entidades, facilitando controlar as datas no domí­nio de nossas aplicações.


Espero ter despertado sua curiosidade com o JodaTime. Na minha opinião é uma das melhores bibliotecas Java disponí­veis, mas não quero falar muito sobre suas funcionalidades, pois um dos pontos mais fortes da biblioteca é a facilidade de se acostumar com ela, e principalmente utilizar todos seus recursos. Quero que vocês tenham um pouco desse gostinho :).

Por @Gust4v0_H4xx0r

Comments

  • Mario Junior
    dezembro 12, 2011 Responder

    Realmente, JodaTime é muito superior ao “default” Calendar!

    Só precisa ter um cuidado para o pessoal que for integrar client-sides Flex/Flash com a des/serialização correta do tipo de dado. No caso, para quem usa o BlazeDS/LCDS “puro”, precisa estender o flex.messaging.endpoint.AMFEndpoint.class passando outras novas classes “CustomAMF3Input”/”CustomAMF3Output” e, dentro destas, tratar o tipo DateTime (instanceOf) convertendo-o para Date/Calendar, assim o Flash Player irá receber um “Date” e “entender” que se trata de um objeto Data/Hora.

    Ou, para quem usar spring-flex-integration (que é mais fácil e recomendável), basta usar um “Custom Converter” (http://static.springsource.org/spring-flex/docs/1.5.x/reference/html/index.html#amf-custom-converters)

    ɉ uma pena nao ter um “ASodaTime” (for as3) … o mais perto q vi é o swiftz (http://code.google.com/p/swiftz/) mas q ainda é muito prematuro (e nem sei se há continuação dele).

    []’s

Leave a Comment