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);
}
}
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();
}
}
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();
}
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);
}
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);
}
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;
}
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();
}
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);
}
Na Listagem 8 temos nosso método acima que executa os seguintes passos em sequência:
- Usando o método readConsole() captura a data que o usuário deseja manipular, esta data deve estar obrigatoriamente no formato mostrado na mensagem.
- Instância a variável “dh” do tipo DataHelper passando como argumento a data que foi digitada pelo usuário.
- 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.
- 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.
- 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();
}
}
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
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Vídeo