Java Calendar: Encapsulando o GregorianCalendar com um DataHelper

Veja neste artigo como manipular o GregorianCalendar do Java através de um encapsulador que será criado.

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:

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.

Artigos relacionados