Neste artigo estudaremos a manipulação de uma classe muito utilizada quando estamos trabalhando com datas em Java: GregorianCalendar.

A Classe GregorianCalendar é uma implementação da classe abstrata Calendar. Pelo fato desta última ser abstrata não podemos instanciá-la, por isso faz-se necessário usar classes concretas como é o caso da GregorianCalendar.

Pense nesta classe como uma encapsuladora da classe Date, adicionando um maior poder de manipulação a data na qual estamos trabalhando. Funciona como um Wrapper, assim como o Integer, Boolean, Double e etc.

A seguir vamos começar a usar a classe GregorianCalendar em um projeto básico de manipulação de datas, além disso veremos outros conceitos importantes como formatação de datas e seus padrões.

Iniciando com GregorianCalendar

Nosso foco será criar uma encapsulador da classe GregorianCalendar, um DataHelper, como vemos o código completo na Listagem 1. Nosso DataHelper será responsável por realizar as operações necessárias e retornar os dados que formulário deseja, tais como: adição de dias, remoção de dias, conversões e etc. É claro que poderíamos usar diretamente o GregorianCalendar no nosso formulário e realizar as operações necessárias, mas iremos deixar nosso projeto mais organizado encapsulando o mesmo.


import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
 
public class DataHelper {
         
         private GregorianCalendar gc;
         private Date dataParaManipular;
         SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
         
         public DataHelper(Date data){
                   this.gc = new GregorianCalendar();
                   this.gc.setTime(data);
                   this.dataParaManipular = data;
         }
         
         public DataHelper(String data){
                   try {
                   this.gc = new GregorianCalendar();
                   this.gc.setTime(formatter.parse(data));
                   } catch (ParseException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                   }
         }
         
         public Date getData(){
                   return this.dataParaManipular;
         }
         
         public void adicionarDias(int quantidade){
                   adicionar(quantidade, Calendar.DAY_OF_MONTH);
         }
         
         public void adicionarMeses(int quantidade){
                   adicionar(quantidade, Calendar.MONTH);
         }
         
         public void adicionarAnos(int quantidade){
                   adicionar(quantidade, Calendar.YEAR);
         }
                   
         private void adicionar(int quantidade, int tipoCampo){
                   gc.add(tipoCampo, quantidade);
                   dataParaManipular = gc.getTime();
         }        
         
         public int getMinutos(){
                   return getCampo(Calendar.MINUTE);
         }
         
         public int getHoras(){
                   return getCampo(Calendar.HOUR);
         }
         
         private int getCampo(int tipoCampo){
                   return gc.get(tipoCampo);
         }
         
         public Integer comparar(String data){
                   try {
                            return comparar(formatter.parse(data));
                   } catch (ParseException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                   }
                   return null;
         }        
         
         public int comparar(Date data){
                   Calendar c = new GregorianCalendar();
                   c.setTime(data);
                   return gc.compareTo(c);
         }        
         
         public String getAsString(){
                   return formatter.format(dataParaManipular);
         }
         
}
Listagem 1. DataHelper

Vamos analisar a Listagem 1 por partes. Observe o código a seguir:


private GregorianCalendar gc;
private Date dataParaManipular;
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");

O que temos aqui são três atributos importantes para manipulação da data. O “gc” que é do tipo GregorianCalendar irá ser responsável por realizar a maior parte das tarefas com o segundo atributo “dataParaManipular” que irá injetar no “gc”. O último atributo, e não menos importante, será responsável por realizar as transformações da nossa data de String para Date e vice-versa, perceba que o formato utilizado é “dd/MM/yyyy hh:mm:ss” e isto significa que as datas obrigatoriamente deverão seguinte este formato contendo horas minutos e segundos. Ex: “12/01/2015 15:20:00”.

Claro que poderíamos realizar melhorias aceitando diferentes formatações, com horas, sem horas, formato americano e etc. Mas este é assunto para um próximo artigo.

Agora, observe a Listagem 2 que tem um trecho retirado da Listagem 1.


public DataHelper(Date data){
 this.gc = new GregorianCalendar();
 this.gc.setTime(data);
 this.dataParaManipular = data;
}
         
public DataHelper(String data){
   try {
         this.gc = new GregorianCalendar();
         this.gc.setTime(formatter.parse(data));
       } catch (ParseException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
               }
}
Listagem 2. Construtores – DataHelper

Temos dois construtores no nosso DataHelper: O primeiro recebendo uma variável do tipo Date e o segundo recebendo uma variável do tipo String, ambos contendo valores de Datas. Ambos os construtores inicializam a variável “gc” com o tipo GregorianCalendar() e a partir daqui já podemos referenciar “gc” em qualquer parte do nosso DataHelper.

O primeiro construtor é mais simples apenas injetando a data no “gc” e inicializando o atributo “dataParaManipular”. Porém o segundo exige um pouco mais de complexidade pois precisamos primeiro converter a data que está em String para um formato Date que o GregorianCalendar possa aceitar. Para isso usamos a classe Calendar chamando o método parse() que recebe uma data como String e retorna uma data como Date. Lembre-se, para isso seja possível o SimpleDateFormat deve estar com o padrão de data já configurado, em nosso caso “dd/MM/yyyy hh:mm:ss” como dito anteriormente.

Ainda no segundo construtor, somos obrigados a colocar um bloco try-catch pois o parse() lança uma exceção ParseException caso algum erro ocorra nesta conversão.

Uma nota importante é que o atributo “dataParaManipular” é inicializado sempre no construtor e ele é utilizado durante todas as manipulações, não sendo necessário que os valores de data sejam passados toda vez que alguma alteração for realizada.

Por exemplo, quando adicionarmos 10 dias a uma data inicial, esta será alterada internamente no DataHelper e na próxima vez que adicionarmos mais cinco dias a data inicial terá 15 dias a mais do que a data original que foi passada pelo construtor, ou seja, a data passada pelo construtor será manipulada internamente.

Agora observe a adição dos valores que foi retirada da Listagem 1.


public void adicionarDias(int quantidade){
  adicionar(quantidade, Calendar.DAY_OF_MONTH);
}
         
public void adicionarMeses(int quantidade){
  adicionar(quantidade, Calendar.MONTH);
}
         
public void adicionarAnos(int quantidade){
   adicionar(quantidade, Calendar.YEAR);
}
               
private void adicionar(int quantidade, int tipoCampo){
   gc.add(tipoCampo, quantidade);
   dataParaManipular = gc.getTime();
}
Listagem 3. Adição de valores – DataHelper

Na Listagem 3 temos quatro métodos para os quais nós adicionamos ou removemos valores da data que está sendo manipulada. Os métodos adicionarDias(), adicionarMeses() e adicionarAnos() chamam o método adicionar() que contém toda lógica necessária para tal operação.

A classe Calendar possui constantes que definem o tipo de campo que você deseja manipular, ex: Ano, Dia, Mês, Hora, Minuto e etc. Para usarmos basta chamar “Calendar.NOME_DO_CAMPO”, igual como foi feito em “Calendar.MONTH”. Dessa forma quando chamamos o método add() do GregorianCalendar ele exige que seja passado o campo que desejamos trabalhar e a quantidade que desejamos adicionar. Ex: “gc.add(Calendar.MONTH, 10), aqui estamos adicionando 10 meses ao GregorianCalendar.

Note que em cada método nós passamos um tipo de campo diferente: Para o adicionarDias() usamos o DAY_OF_MONTH, para o adicionarMeses() usamos o MONTH, para o adicionarAnos() usamos o YEAR. Poderíamos criar outros métodos para adicionar minutos e etc, mas como nosso foco é apenas acadêmico não iremos nos estender no repetitivo. Como uma simples regra matemática: o positivo adicionar e o negativo retira, então você já deve imaginar que nosso método adicionar() também remove se passarmos um valor negativo. Por exemplo, adicionar(-1, Calendar.DAY), neste caso estamos removendo um dia da nossa data.

Após usar o método add() do GregorianCalendar, nós atribuímos a nova data ao atributo dataParaManipular recuperando-a através do método getTime() que retorna um Date.


public int getMinutos(){
  return getCampo(Calendar.MINUTE);
}
         
public int getHoras(){
  return getCampo(Calendar.HOUR);
}
         
private int getCampo(int tipoCampo){
  return gc.get(tipoCampo);
}
Listagem 4. Retornando campos – DataHelper

Na Listagem 4 temos os métodos getMinutos() e getHoras(), retirados da Listagem 1 e, como o próprio nome já sugere, retorna a quantidade de minutos ou horas presentes na data que está sendo manipulada. Essa quantidade diz respeito ao campo hora e campo minuto e não a conversão de toda a data em minutos ou horas. Por exemplo, 12/01/2015 17:20:00 é uma data que possui 17 horas e 20 minutos em seus respectivos campos.

O método private getCampo() é o responsável por chamar o método get() do GregorianCalendar passando o tipo do campo como argumento, ou seja, MINUTE para minuto e HOUR para hora. Poderíamos também adicionar campos como mês, ano, dia e etc., mas estes dois já são suficientes para entendimento da lógica.


public Integer comparar(String data){
  try {
   return comparar(formatter.parse(data));
  } catch (ParseException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
  }
     return null;
}      
         
public int comparar(Date data){
   Calendar c = new GregorianCalendar();
   c.setTime(data);
   return gc.compareTo(c);
}
Listagem 5. Comparações – DataHelper

Na Listagem 5 temos dois métodos públicos chamados comparar, porém um receber uma data como String e outro recebe uma data como Date, ambos retornam um valor inteiro.

Primeiro entenda como funciona a comparação no GregorianCalendar:

  • A comparação é realizada através de duas datas: a data que já está sendo usada pelo mesmo e a data que é passada via parâmetro ao método compareTo().
  • O método compareTo() é da classe Calendar e não possui implementação no GregorianCalendar.
  • Para um retorno IGUAL a 0, significa que a data do parâmetro é igual a data do Calendar.
  • Para um retorno MENOR que 0, significa que a data do Calendar é menor que a data do parâmetro.
  • Para um retorno MAIRO que 0, significa que a data do Calendar é maior que a data do parâmetro.

Dito isto, nós já conseguimos entender como funciona o método comparar() e porque ele retorna um inteiro. Poderíamos fazer ainda melhor, criando um “public enum COMPARACAO{MAIOR, MENOR, IGUAL}” onde o retorno do comparar seria o ENUM especificando de forma mais “legível” a comparação realizada.


public String getAsString(){
   return formatter.format(dataParaManipular);
}
   
public Date getData(){
  return this.dataParaManipular;
}
Listagem 6. Retornando a data manipulada – DataHelper

Na Listagem 6 existem duas formas de retornar a data que trabalhamos (adicionamos, removemos, convertemos e etc.): apenas enviando o atributo dataParaManipular que é do tipo Date ou retornando a data como uma String formatada, usando o método format() do SimpleDateFormat.

Você pode perguntar-se qual a utilidade de retornar a quantidade de minutos de um data se podemos ver estes minutos de forma explícita apenas olhando a data? A questão não é a nossa visualização, mas a visualização do próprio software. Pelo fato de ser um “Helper” subtende-se que esta classe será utilizada como auxiliar para outras funcionalidades. Ex: Suponha que você deseje gravar a apenas a hora e minuto que determinado funcionário registrou seu ponto, extraindo essa informação da data atual.

Para finalizar vamos ver a aplicação simples do DataHelper através de uma classe com o método main(), como mostram as Listagens 7 e 8.


private static void showMenu() {
   
   System.out.println("1. Adicionar dias");
   System.out.println("2. Comparar datas");
   System.out.println("3. Retornar horas");
   System.out.println("4. Retornar minutos");
   System.out.println("5. Remover dias");
   System.out.println("0. Sair");
   System.out.println("Escolha uma das opções acima: ");
   opcao = Integer.valueOf(readConsole());
}
 
private static String readConsole() {
   Scanner sc = new Scanner(System.in);
    return sc.next();
}
Listagem 7. Aplicando o DataHelper – Parte 1

Nossa classe que irá usar o DataHelper possuirá entrada via console, então precisamos de uma forma para ler estas entradas e, para isso, usamos o método readConsole().

O método showMenu() apenas mostra as opções que o usuário tem e armazena na variável opção.


public static void main(String[] args) {
    System.out
     .println("Digite sua data no formato 'dd/MM/yyyy hh:mm:ss': ");
    data = readConsole();
   
     DataHelper dh = new DataHelper(data);
   
      do {
   
        showMenu();
   
        switch (opcao) {
        case 0:{
          System.out.println("Até mais...");
          break;
        }                   
        case 1: {
           System.out.println("Digite a quantidade de dias: ");
           int qtd = Integer.valueOf(readConsole());
           dh.adicionarDias(qtd);
           System.out.println("Nova data: " + dh.getAsString());
           break;
        }
        case 2: {
          System.out
             .println("Digite a data para comparar (formato dd/MM/yyyy hh:mm:ss):");
          String dataToCompare = readConsole();
          int c = dh.comparar(dataToCompare);
          if (c < 0) {
             System.out.println("A data original é menor que esta data");
          } else if (c > 0) {
              System.out.println("A data original é maior que esta data");
          } else {
             System.out.println("A data original é igual a esta data");
          }
          break;
        }
        case 3: {
           System.out.println("Horas = " + dh.getHoras());
           break;
        }
        case 4: {
          System.out.println("Minutos = " + dh.getMinutos());
          break;
        }
        case 5: {
          System.out.println("Digite a quantidade de dias a deletar: ");
          int qtd = Integer.valueOf(readConsole());
          dh.adicionarDias(-qtd);
          System.out.println("Nova data: " + dh.getAsString());
          break;
       }
       default:
          System.err.println("Opcao invalida");
          break;
       }
   
  } while (opcao != 0);
   
}
Listagem 8. Aplicando o DataHelper – Parte 2

Na Listagem 8 temos nosso método acima que executa os seguintes passos em sequência:

  1. Usando o método readConsole() captura a data que o usuário deseja manipular, esta data deve estar obrigatoriamente no formato mostrado na mensagem.
  2. Instância a variável “dh” do tipo DataHelper passando como argumento a data que foi digitada pelo usuário.
  3. O do-while irá garantir que ao menos uma vez entraremos no laço e continuará entrando até que a variável opção seja diferente de 0, que seria a opção de sair.
  4. Toda vez que entrarmos neste laço do-while nós mostraremos ao usuário o menu de opções para que ele possa realizar outras operações com a data que foi digitada logo no início.
  5. Cada um dos cases tem sua peculiaridade, vejamos:· Case 0: apenas mostra a mensagem que o programa será encerrado;· Case 1: Solicita a quantidade de dias a serem adicionados e depois retorna como ficou a data com os dias adicionados;· Case 2: Solicita a data que será comparada, e retorna uma mensagem especificando se ela é menor, igual ou maior a data original;· Case 3: Retorna a quantidade de horas da data;· Case 4: Retorna a quantidade de minutos da data;· Case 5: Remove a quantidade de dias desejadas da data e mostra a nova data;· Default: Caso nenhuma das opções acima seja escolhida.

Com isso, vemos a nossa classe completa no código da Listagem 9.


import java.util.Scanner;
 
public class UseDataHelper {
 
private static String data;
private static int opcao;

/**
* @param args
*/
public static void main(String[] args) {
  System.out
   .println("Digite sua data no formato 'dd/MM/yyyy hh:mm:ss': ");
  data = readConsole();
 
  DataHelper dh = new DataHelper(data);
 
   do {
 
        showMenu();
 
        switch (opcao) {
         case 0:{
           System.out.println("Até mais...");
           break;
        }                   
        case 1: {
           System.out.println("Digite a quantidade de dias: ");
           int qtd = Integer.valueOf(readConsole());
           dh.adicionarDias(qtd);
           System.out.println("Nova data: " + dh.getAsString());
          break;
        }
        case 2: {
           System.out
            .println("Digite a data para comparar (formato dd/MM/yyyy hh:mm:ss):");
           String dataToCompare = readConsole();
           int c = dh.comparar(dataToCompare);
           if (c < 0) {
              System.out.println("A data original é menor que esta data");
           } else if (c > 0) {
              System.out.println("A data original é maior que esta data");
            } else {
               System.out.println("A data original é igual a esta data");
            }
          break;
        }
        case 3: {
         System.out.println("Horas = " + dh.getHoras());
         break;
        }
        case 4: {
          System.out.println("Minutos = " + dh.getMinutos());
          break;
        }
        case 5: {
         System.out.println("Digite a quantidade de dias a deletar: ");
         int qtd = Integer.valueOf(readConsole());
         dh.adicionarDias(-qtd);
         System.out.println("Nova data: " + dh.getAsString());
         break;
        }
        default:
        System.err.println("Opcao invalida");
          break;
      }
 
   } while (opcao != 0);
}
 
private static void showMenu() {
 
             System.out.println("1. Adicionar dias");
             System.out.println("2. Comparar datas");
             System.out.println("3. Retornar horas");
             System.out.println("4. Retornar minutos");
             System.out.println("5. Remover dias");
             System.out.println("0. Sair");
             System.out.println("Escolha uma das opções acima: ");
             opcao = Integer.valueOf(readConsole());
}
 
private static String readConsole() {
             Scanner sc = new Scanner(System.in);
             return sc.next();
 }
 
}
Listagem 9. Classe UsaDataHelper.java completa

Este artigo teve como principal objetivo demonstrar o uso da classe GregorianCalendar e como consequência disto aprender também sobre a classe Calendar. Criamos uma encapsulador chamado DataHelper que nos auxilia a manipular determinada data usando GregorianCalendar, Calendar e SimpleDateFormat.

Deixamos para você leitor, como desafio, a tarefa de criar uma parametrizador do formato da data que será necessário, pois se você perceber nós usamos o formato “dd/MM/yyyy HH:mm:ss” que é um pouco trabalhoso para um usuário que apenas deseja passar o dia, mês e ano, por isso você deverá possibilitar que diferentes formatos sejam passados. Outro desafio será validar a data passada antes de passar ao DataHelper, evitando assim erros de parse lançados pelo ParseException.