Data e Hora em Java

O tratamento de data e hora em uma aplicação tem sido freqüentemente um pesadelo para os programadores de qualquer linguagem. O problema é ainda mais grave se for considerado que praticamente qualquer sistema de informação precisa em algum momento desse tipo de dado. Ao contrário de algumas estruturas de dados mais simples, como inteiros ou strings, datas são tipos bastante irregulares. Algumas das dificuldades encontradas nesse tipo de controle são:

  • meses com dias irregulares;
  • anos bissextos;
  • ausência de um ano 0 (vai de -1 a.C para 1 a.C);
  • dias da semana úteis e não-úteis;
  • feriados nacionais, estaduais e municipais;
  • minutos e segundos na base 60 (embora medidas mais precisas como centésimos e milésimos de segundo estejam em outra base);
  • hora AM e PM;
  • fuso horário;
  • horário de verão;

O problema com datas e horas é tão comum, que até o famoso bug do ano 2000 foi acarretado pelo armazenamento de anos com dois dígitos (01/07/48) ao invés dos quatro dígitos atuais (01/07/1948). Esperamos que daqui a 7.990 anos os programadores não fiquem muito chateados de não termos considerado a virada para o ano 10.000.

Essa extensa lista de obstáculos no tratamento de datas faz com que operações simples, como adicionar ou subtrair uma hora de uma data, se torne algo complexo. Em Java não poderia ser diferente: a especificação inicial da Sun conta com vários métodos depreciados (deprecated), representação ambígua e incapacidade em lidar com intervalos e períodos de tempo de forma simples. Fala-se de uma reformulação na API de data e hora a partir da versão SE 7 (vide especificação JSR 310).

Datas e Calendários

De qualquer forma, vale a pena estudar as principais maneiras de manipular objetos do tipo data na linguagem. Java possui a classe java.util.Date para representação de uma data simples. O comando a seguir cria uma instância de um objeto com a data corrente do sistema:

Date dt1 = new Date();

O tipo Date armazena internamente um long que representa o número de millisegundos que se passaram desde meia-noite do dia 01/01/1970. Ou seja, é possível intercambiar os dois tipos de dados da seguinte forma:

long agora = System.currentTimeMillis();

data dtAgora = new Date (agora);

 
E o método getTime() da classe Date retorna também esse long. Então ao imprimir o long agora e a data dtAgora obteremos algo similar aos seguintes resultados:

Fri Jan 15 03:35:06 BRST 2010

1263533706375

Apenas por curiosidade, o número é um pouco maior que 1.261.440.000.000, que é 40 * 365 * 24 * 60 * 60 * 1000 (40 anos * 365 dias * 24 horas * 60 minutos * 60 segundos * 1000 millisegundos), pois não foram considerados anos bissextos e já se encontrava no dia 15 de janeiro de 2010, algumas horas após a meia-noite. Isso significa que o tipo Date NÃO é para ser operado diretamente, como no exemplo abaixo:

Date amanha = new Date(agora.getTime() + Timer.ONE_DAY);

Pois não estão sendo considerados fatores como horário de verão (um dia do ano possui 23 horas, e um outro dia possui 25 horas), além de ser muito trabalhoso programar rotinas desse tipo. Felizmente, existe a classe java.util.Calendar que se encarrega de fornecer métodos para manipular as datas de forma eficaz.

GregorianCalendar cal = new GregorianCalendar();

cal.setTime(dt1);

Por que foi utilizada a classe GregorianCalendar? Ela é uma subclasse da Calendar, e considera o nosso calendário gregoriano, de 365 dias, anos bissextos, etc. A partir de uma instância dessa classe, é possível obter cada informação da data:

System.out.print(“Ano: “ + cal.get(Calendar.YEAR));

System.out.print(“Mês: “ + cal.get(Calendar.MONTH));

System.out.print(“Dia: “ +  cal.get(Calendar.DAY_OF_MONTH));

System.out.print(“Semana: “ + cal.get(Calendar.DAY_OF_WEEK));

A saída será: Ano: 2010, Mês: 0, Dia: 15 e Semana: 6. Janeiro é o mês zero, e o dia da semana 6 é a quinta-feira, pois começa a contagem do domingo. YEAR, MONTH, DAY_OF_MONTH e DAY_OF_WEEK são constantes definidas na classe Calendar e que referenciam os diferentes campos da data. É recomendável verificar essas constantes na API da classe (ver referências) para entender o seu funcionamento.

É possível usar essas constantes para operações de calendário:

cal.add(gc.MONTH, 1) // soma um mês à data

cal.add(gc.HOUR, -3) // substrai 3 horas à data

O método cal.isLeapYear() retorna true caso o ano da instância do calendário seja um ano bissexto, e false caso contrário. Os métodos after() e before() podem ser usados para comparar duas datas:

if(cal1.after(cal2)) // cal1 é depois de cal2

if(cal1.before(cal2)) // cal1 é antes de cal2

Finalmente, a data pode ser retornada ao tipo java.util.Date por meio do método getTime() da classe GregorianCalendar:

Date dt = cal.getTime();

 

Entrada e Saída de Datas

Uma operação comum com datas é a leitura a partir da entrada do usuário (por ex: data de nascimento em um formulário web). Outra operação freqüente é a impressão de uma data em um formato padronizado e legível para o usuário.

Para essas duas operações é recomendado o uso da classe SimpleDateFormat (do pacote java.text).

Date dts;

String s="20/02/2010";

 

SimpleDateFormat sdf= new SimpleDateFormat("dd/MM/yyyy");

try {

       dts= sdf.parse(s);

}

catch(ParseException pe) {

       System.out.println(pe.getMessage());

       dts = null;

}

 

System.out.println(dts);

O funcionamento do código acima é o seguinte: a partir de uma String s (que pode ser obtida por entrada em um formulário), é criado um objeto Date, usando a máscara “dd/MM/yyyy” especificada no objeto do tipo SimpleDateFormat. Note que o MM em maiúsculas corresponde ao MONTH da classe Calendar. A saída do código é algo como “Sat Feb 20 00:00:00 BRST 2010”, ou seja, 20 de fevereiro de 2010, e a hora por padrão é meia-noite. Outras máscaras podem ser usadas, como a seguir:

// 15/01/2010 03:35:16

DateFormat df1 = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");

 

// Formato Americano: 01/15/2010

DateFormat df1 = new SimpleDateFormat("MM/dd/yyyy");

 

// 15/01/2010 03h35

DateFormat df1 = new SimpleDateFormat("dd/MM/yyyy HH'h'mm");

A operação inversa, ou seja, imprimir formatado uma data, é bem simples:

SimpleDateFormat sdf= new SimpleDateFormat("dd/MM/yyyy");

Date dt1 = new Date();

System.out.print(sdf.format(dt1));

A saída será algo do tipo “15/01/2010”. 

 

Atenção!

É preciso tomar cuidado com a maneira em que as datas são especificadas. MM indica o mês, enquanto mm indica os minutos. Já HH em maiúsculas representa as horas no formato de 24 horas (ex: 14 horas), e hh em minúsculas representa as horas no formato de 12 horas (ex: 2 horas da tarde).


Referências


API da classe java.util.Calendar

http://java.sun.com/j2se/1.5.0/docs/api/java/util/Calendar.html


Esse artigo faz parte do livro Java e Web para Concursos.




Comments