É hora da resposta moderna.
java.time e ThreeTenABP
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d/M/u"); String validUntil = "1/1/1990"; LocalDate validDate = LocalDate.parse(validUntil, dateFormatter); LocalDate currentDate = LocalDate.now(ZoneId.of("Pacific/Efate")); if (currentDate.isAfter(validDate)) { System.out.println("Catalog is outdated"); }Quando executei este código agora, o resultado foi:
Catálogo está desatualizado
Como nunca é a mesma data em todos os fusos horários, forneça um fuso horário explícito a LocalDate.now. Se você deseja que o catálogo expire ao mesmo tempo em todos os fusos horários, você pode fornecerZoneOffset.UTC , desde que informe aos usuários que está usando UTC.
Estou usando java.time, a API moderna de data e hora do Java. As classes de data e hora que você usou Calendar, SimpleDateFormate Date, são todas mal projetadas e, felizmente, há muito desatualizadas. Além disso, apesar do nome, a Datenão representa uma data, mas um ponto no tempo. Uma consequência disso é: embora hoje seja 15 de fevereiro de 2019, um Dateobjeto recém-criado já está após (não é igual a) um Dateobjeto da análise 15/02/2019. Isso confunde alguns. Ao contrário disso, o moderno LocalDateé uma data sem hora do dia (e sem fuso horário), então dois LocalDates representando a data de hoje sempre serão iguais.
Pergunta: Posso usar java.time no Android?
Sim, java.time funciona bem em dispositivos Android mais antigos e mais recentes. Requer apenas pelo menos Java 6 .
- No Java 8 e posterior e em dispositivos Android mais recentes (da API de nível 26), a API moderna vem embutida.
- Em Java 6 e 7, obtenha o Backport ThreeTen, o backport das classes modernas (ThreeTen para JSR 310; consulte os links na parte inferior).
- No Android (mais antigo), use a edição Android do ThreeTen Backport. É chamado ThreeTenABP. E certifique-se de importar as classes de data e hora org.threeten.bpcom subpacotes.
Links
A classe Date representa um instante específico no tempo, com precisão de milissegundos. A classe Date do pacote java.util implementa as interfaces Serializable, Cloneable e Comparable. Ele fornece construtores e métodos para lidar com data e hora com java. A seguir estão os métodos para comparar datas em java
Em diversas aplicaç�es � necess�rio manipular datas de alguma forma, seja para salvar a data de algum evento que aconteceu, como a data de nascimento de uma pessoa, a data de admiss�o de uma pessoa em uma empresa ou de uma festa. Algumas das poss�veis operaç�es de datas s�o: comparaç�o entre elas, criaç�o de um filtro para encontrar um evento que aconteceu entre duas datas e o c�lculo do n�mero de dias ou meses que faltam para um evento. Por�m, em Java isso sempre foi um problema, pois a API nativa de manipulaç�o de datas sempre foi bastante limitada.
Algumas tentativas para resolver esses problemas surgiram como o framework JodaTime, que tem um conjunto de funcionalidades para a manipulaç�o de datas muito maior que a API do Java at� a vers�o 7. Mas apesar de o JodaTime ser bastante �til os programadores Java, sempre pediram que a API do Java fosse revista para melhorar o tratamento de datas e isso s� aconteceu na �ltima vers�o do Java, onde toda a API foi revista.
Para mostrar como usar as novas funcionalidades desta API, esse artigo mostra as principais classes, os principais m�todos e como utilizar algumas delas. Para a execuç�o dos exemplos desse artigo � necess�rio ter instalada a vers�o 8 da JDK e voc� pode usar qualquer IDE.
Principais funcionalidades da API
A nova API disponibiliza uma grande quantidade de novas classes e m�todos. A classe principal dessa nova especificaç�o � a classe java.time.LocalDate, que tem a mesma funç�o que a antiga classe java.util.Date, mas com muito mais funcionalidades implementadas. A Listagem 1 mostra algumas das possibilidades de utilizaç�o da classe LocalDate.
LocalDate localDate = LocalDate.now(); System.out.println(localDate); System.out.println("Dia da semana: " + localDate.getDayOfWeek().name()); System.out.println("Dia da semana: " + localDate.getDayOfWeek().ordinal()); System.out.println("Mes: " + localDate.getMonthValue()); System.out.println("Mes: " + localDate.getMonth().name()); System.out.println("Ano: " + localDate.getYear());
Listagem 1. Classe LocalDate
No c�digo � mostrado como recuperar diversos dados de uma data como o dia da semana, o m�s e o ano. Al�m disso, � poss�vel recuperar o nome do dia da semana, como String (segunda, terça, quarta,...) e tamb�m o n�mero (1 a 7). � poss�vel recuperar tamb�m o m�s e o ano. Tudo isso de uma forma muito mais f�cil do que era at� na vers�o 7 do Java e sem nenhuma API adicional. A Listagem 2 mostra o resultado da execuç�o desse c�digo.
2016-02-06 Dia da semana: SATURDAY Dia da semana: 5 Mes: 2 Mes: FEBRUARY Ano: 2016
Listagem 2. Execuç�o do c�digo da Listagem 1
Classe Instant
Outra classe nova da API � a classe java.time.Instant, que serve para representar um instante qualquer. Nas vers�es anteriores do Java, o instante era simplesmente representado por um long, que adicionava um milissegundo a cada instante desde 01/01/1970, que � o instante inicial que a plataforma Java considerava. A classe Instant tem diversos usos, como calcular a duraç�o da execuç�o de algum algoritmo, ou a duraç�o da execuç�o de uma busca, entre diversos outros poss�veis usos.
A Listagem 3 mostra como fazer a comparaç�o de instante na API antiga de datas do Java. O c�digo funciona, mas n�o � muito elegante, al�m de s� funcionar para eventos a partir de 01/01/1970. O Thread.sleep(1000) foi adicionado apenas para que os dois instantes sejam diferentes.
long instateInicial = System.currentTimeMillis(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } long instanteFinal = System.currentTimeMillis(); long duracaoEmMilesegundos = instanteFinal - instateInicial; System.out.println("Duraç�o em segundos: " + ( duracaoEmMilesegundos / 1000 ) % 60 );
Listagem 3. Calculando a duraç�o de um evento na API de datas antiga
A Listagem 4 mostra como fazer essa comparaç�o na nova API com a nova classe Instant. Al�m de muito mais elegante, o c�digo funciona para qualquer instante recuperado atrav�s de uma data.
Instant iInicial = Instant.now(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Instant iFinal = Instant.now(); Duration duracao = Duration.between(iInicial, iFinal); System.out.println("Duraç�o em nanos segundos: " + duracao.toNanos()); System.out.println("Duraç�o em minutos: " + duracao.toMinutes()); System.out.println("Duraç�o em horas: " + duracao.toHours()); System.out.println("Duraç�o em milisegundos: " + duracao.toMillis()); System.out.println("Duraç�o em dias: " + duracao.toDays());
Listagem 4. Calculando a duraç�o de um evento na nova API de datas
Como mostrado no c�digo, � poss�vel recuperar a duraç�o de um evento em nano segundos, minutos, horas e milissegundos, que � outra vantagem da nova API, pois no m�todo antigo o tempo era recuperado sempre em milissegundos e era necess�rio fazer a convers�o dos dados.
A Listagem 5 mostra o resultado da execuç�o da Listagem 4 e, como o intervalo � de apenas um segundo, a duraç�o em horas e minutos � arredonda para 0.
Duraç�o em nanos segundos: 1001000000 Duraç�o em minutos: 0 Duraç�o em horas: 0 Duraç�o em milisegundos: 1001
Listagem 5. Resultado da execuç�o do c�digo da Listagem 4
Comparaç�o entre datas
Outra coisa bastante utilizada quando trabalhamos com datas � a comparaç�o entre elas como, por exemplo, se uma data � antes ou depois da outra ou quantos meses de diferença existem entre duas datas. Tudo isso � poss�vel fazer com as novas classes da API de data. A Listagem 6 mostra as principais maneiras de comparar datas com os m�todos isAfter, isBefore e isEqual.
LocalDate localDateAntigo = LocalDate.of(2010, 3, 7); LocalDate localDateNovo = LocalDate.of(2015, 3,5); System.out.println(localDateAntigo.isAfter(localDateNovo)); System.out.println(localDateAntigo.isBefore(localDateNovo)); System.out.println(localDateAntigo.isEqual(localDateNovo)); Period periodo = Period.between(localDateAntigo, localDateNovo); System.out.println(periodo.getYears() + " Anos " + periodo.getMonths() + " Meses " + periodo.getDays() + " Dias"); System.out.println("Apenas meses: " + periodo.toTotalMonths());
Listagem 6. Comparando duas datas
Veja que a pr�pria classe LocalData tem alguns m�todos para a comparaç�o de duas datas, como os m�todos isAfter e isBefore, que verificam se uma data � antes ou depois de outra, respectivamente. Para descobrir o tempo passado entre uma data e outra � utilizada a classe Period, onde � poss�vel recuperar a diferença de anos, dias e meses.
A Listagem 7 mostra o resultado da execuç�o desse trecho de c�digo.
false true false 4 Anos 11 Meses 26 Dias Apenas meses: 59
Listagem 7. Execuç�o do c�digo da Listagem 6
Na nova API � tamb�m poss�vel fazer operaç�es na Data diretamente na classe LocalDate, n�o � necess�rio utilizar o Calendar, como na antiga classe java.util.Date.
A Listagem 8 mostra o c�digo de algumas poss�veis operaç�es na classe LocalDate, dentre elas existem diversas operaç�es como o plusDays(), que adiciona os dias passados como par�metro a data, o plusWeeks que adiciona as semanas passadas como par�metro a data e o minusYears(), que remove os anos passados como par�metro a data.
LocalDate dataManipulacao = LocalDate.now(); System.out.println("Mais 5 dias:" + dataManipulacao.plusDays(5)); System.out.println("Mais 5 semanas:" + dataManipulacao.plusWeeks(5)); System.out.println("Mais 5 anos:" + dataManipulacao.plusYears(5)); System.out.println("Mais 2 meses:" + dataManipulacao.plusMonths(2)); System.out.println("Menos 1 ano:" + dataManipulacao.minusYears(1)); System.out.println("Menos 1 m�s:" + dataManipulacao.minusMonths(1)); System.out.println("Menos 3 dia: " + dataManipulacao.minusDays(3)); System.out.println("Data Original:" + dataManipulacao);
Listagem 8. Operaç�es em um LocalDate
Recordando as vers�es antigas do Java, para fazer essas operaç�es era necess�rio usar a classe Calendar e os m�todos que ela disponibiliza, o que dava bastante trabalho. Na nova API � muito mais simples.
A Listagem 9 mostra o resultado da execuç�o desse c�digo.
Mais 5 dias: 2016-02-11 Mais 5 semanas: 2016-03-12 Mais 5 anos: 2021-02-06 Mais 2 meses: 2016-04-06 Menos 1 ano: 2015-02-06 Menos 1 m�s: 2016-01-06 Data Original: 2016-02-06
Listagem 9. Execuç�o do c�digo da Listagem 8
Uma coisa importante e bem diferente da API antiga e que � poss�vel visualizar no c�digo da Listagem 8 � que a classe LocalDate funciona como a classe String, isto �, ela � imut�vel. Quando chamamos um m�todo, como o plusDays(), o objeto dataManipulacao n�o � alterado, e sim criada uma nova inst�ncia da classe LocalDate. � poss�vel verificar isso, pois a �ltima linha do c�digo mostrou a data original e pela execuç�o do c�digo mostrada na Listagem 9, � poss�vel perceber que a data n�o foi modificada.
Outra funcionalidade muito interessante da nova API � utilizar o fuso hor�rio de cidades diferentes para a comparaç�o de datas. Por exemplo, � poss�vel pegar uma data qualquer e definir o fuso hor�rio dela em duas cidades diferentes e depois comparar a diferença de tempo entre essas duas datas considerando o fuso. A Listagem 10 mostra como fazer isso, onde inicialmente � definida uma hora qualquer, depois � criada a vari�vel horaSaoPaulo utilizando o fuso de S�o Paulo e a horaParis utilizando o fuso de Paris. Em seguida � feita a comparaç�o entre as duas datas e � mostrada a diferença em horas entre elas.
LocalDateTime hora = LocalDateTime.of(2016, Month.APRIL, 4, 22, 30); ZoneId fusoHorarioDeSaoPaulo = ZoneId.of("America/Sao_Paulo"); ZonedDateTime horaSaoPaulo = ZonedDateTime.of(hora, fusoHorarioDeSaoPaulo); System.out.println(horaSaoPaulo); ZoneId fusoHorarioDeParis = ZoneId.of("Europe/Paris"); ZonedDateTime horaParis = ZonedDateTime.of(hora, fusoHorarioDeParis); System.out.println(horaParis); Duration diferencaDeHoras = Duration.between(horaSaoPaulo, horaParis); System.out.println(diferencaDeHoras.getSeconds() / 60 / 60);
Listagem 10. Comparando datas com fuso hor�rio
A Listagem 11 mostra a execuç�o do c�digo, onde foram criados dois objetos: um com a hora de S�o Paulo e outro com a hora de Paris, e depois foi feita a comparaç�o entre essas duas datas. Nessa comparaç�o foi poss�vel ver que a diferença do fuso hor�rio � de cinco horas.
2016-04-04T22:30-03:00[America/Sao_Paulo] 2016-04-04T22:30+02:00[Europe/Paris] Diferença de fuso hor�rio: -5
Listagem 11. Resultado da execuç�o do c�digo da Listagem 10
Formataç�o de datas
A formataç�o de data para diferentes padr�es tamb�m ficou um pouco mais simples nessa nova vers�o, para isso, agora � criado um formatador de dado com a classe DateTimeFormatter e a pr�pria classe LocalDate tem um m�todo format que retorna uma String com a data formatada no padr�o passado como par�metro. A Listagem 11 mostra essa classe sendo utilizada e na Listagem 12 a sua execuç�o.
LocalDate hoje = LocalDate.now(); DateTimeFormatter formatadorBarra = DateTimeFormatter.ofPattern("dd/MM/yyyy"); DateTimeFormatter formatadorTraco = DateTimeFormatter.ofPattern("dd-MM-yyyy"); System.out.println("Data com /: " + hoje.format(formatadorBarra)); System.out.println("Data com -: " + hoje.format(formatadorTraco));
Listagem 11. Formatando data com a classe DateTimeFormatter
Data com /: 06/02/2016 Data com -: 06-02-2016
Listagem 12. Datas formatadas
Repare que foram utilizados dois formatadores: o primeiro que formata a data separando dia, m�s e ano com o �/�, e o segundo que formata a data com o -.
Os exemplos anteriores cobriram boa parte da nova API de datas do Java, por�m existem muitas outras possibilidades, a API � bastante completa e facilita muito a manipulaç�o de datas. Apenas como exemplo de mais algumas coisas que podem ser feitas, a Listagem 13 mostra alguns m�todos interessantes como, por exemplo, o que verifica se o ano da data � bissexto, o n�mero de dias do m�s e do ano, e tamb�m a maior e menor data poss�vel na API.
LocalDate data = LocalDate.now(); System.out.println("Ano bissexto: " + data.isLeapYear()); System.out.println("N�mero de dias do m�s: " + data.lengthOfMonth()); System.out.println("N�mero de dias do ano: " + data.lengthOfYear()); System.out.println("Menor data: " + LocalDate.MIN); System.out.println("Maior data: " + LocalDate.MAX);
Listagem 13. Mais alguns m�todos interessantes
A Listagem 14 mostra o resultado da execuç�o do c�digo e como � poss�vel verificar no c�digo, a primeira linha mostra que 2016 � um ano bissexto, a segunda linha mostra que fevereiro tem 29 dias e que o ano de 2016 tem 366 dias. A data m�xima suportada pelo Java � +999999999-12-31 e a m�nima � -999999999-01-01, em ambas o primeiro n�mero � o ano.
Ano bissexto: true N�mero de dias do m�s: 29 N�mero de dias do ano: 366 Menor data: -999999999-01-01 Maior data: +999999999-12-31
Listagem 14. Execuç�o do c�digo da Listagem 13
Para mostrar todo o c�digo desenvolvido nesse exemplo e para mostrar como importar as classes da nova API, a Listagem 15 mostra o c�digo completo desenvolvido, e que foi mostrado e explicado parte a parte nas listagens anteriores. � poss�vel observar que todas as novas classes inclu�das nessa nova API est�o contidas no pacote java.time.
Listagem 15. C�digo completo do exemplo desenvolvido nesse artigo.
package data; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; public class Main { public static void main(String[] args) { // criaç�o de datas com a nova classe LocalDate e LocalDate localDate = LocalDate.now(); System.out.println(localDate); System.out.println("Dia da semana: " + localDate.getDayOfWeek().name()); System.out.println("Dia da semana: " + localDate.getDayOfWeek().ordinal()); System.out.println("Mes: " + localDate.getMonthValue()); System.out.println("Mes: " + localDate.getMonth().name()); System.out.println("Ano: " + localDate.getYear()); // comparaç�o entre diferentes instantes de tempo Instant iInicial = Instant.now(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Instant iFinal = Instant.now(); Duration duracao = Duration.between(iInicial, iFinal); System.out.println("Duraç�o em nanos segundos: " + duracao.toNanos()); System.out.println("Duraç�o em minutos: " + duracao.toMinutes()); System.out.println("Duraç�o em horas: " + duracao.toHours()); System.out.println("Duraç�o em milisegundos: " + duracao.toMillis()); System.out.println("Duraç�o em dias: " + duracao.toDays()); // comparaç�o de datas (antes, depois, per�odo entre duas datas) LocalDate localDateAntigo = LocalDate.of(2010, 3, 7); LocalDate localDateNovo = LocalDate.of(2015, 3, 5); System.out.println(localDateAntigo.isAfter(localDateNovo)); System.out.println(localDateAntigo.isBefore(localDateNovo)); System.out.println(localDateAntigo.isEqual(localDateNovo)); Period periodo = Period.between(localDateAntigo, localDateNovo); System.out.println(periodo.getYears() + " Anos " + periodo.getMonths() + " Meses " + periodo.getDays() + " Dias"); System.out.println("Apenas meses: " + periodo.toTotalMonths()); // operaç�es em datas como adiç�o e subtraç�o de dias, meses e anos LocalDate dataManipulacao = LocalDate.now(); System.out.println("Mais 5 dias: " + dataManipulacao.plusDays(5)); System.out.println("Mais 5 semanas: " + dataManipulacao.plusWeeks(5)); System.out.println("Mais 5 anos: " + dataManipulacao.plusYears(5)); System.out.println("Mais 2 meses: " + dataManipulacao.plusMonths(2)); System.out.println("Menos 1 ano: " + dataManipulacao.minusYears(1)); System.out.println("Menos 1 m�s: " + dataManipulacao.minusMonths(1)); System.out.println("Menos 3 dia: " + dataManipulacao.minusDays(3)); //classe LocalDate � imut�vel System.out.println("Data Original: " + dataManipulacao); // comparaç�o de datas utilizando o fuso hor�rio LocalDateTime hora = LocalDateTime.of(2016, Month.APRIL, 4, 22, 30); ZoneId fusoHorarioDeSaoPaulo = ZoneId.of("America/Sao_Paulo"); ZonedDateTime horaSaoPaulo = ZonedDateTime.of(hora, fusoHorarioDeSaoPaulo); System.out.println(horaSaoPaulo); ZoneId fusoHorarioDeParis = ZoneId.of("Europe/Paris"); ZonedDateTime horaParis = ZonedDateTime.of(hora, fusoHorarioDeParis); System.out.println(horaParis); Duration diferencaDeHoras = Duration.between(horaSaoPaulo, horaParis); System.out.println("Diferença de fuso hor�rio: " + diferencaDeHoras.getSeconds() / 60 / 60); // formataç�o de datas com a nova API LocalDate hoje = LocalDate.now(); DateTimeFormatter formatadorBarra = DateTimeFormatter.ofPattern("dd/MM/yyyy"); DateTimeFormatter formatadorTraco = DateTimeFormatter.ofPattern("dd-MM-yyyy"); System.out.println("Data com /: " + hoje.format(formatadorBarra)); System.out.println("Data com -: " + hoje.format(formatadorTraco)); // m�todos interessantes LocalDate data = LocalDate.now(); System.out.println("Ano bissexto: " + data.isLeapYear()); System.out.println("N�mero de dias do m�s: " + data.lengthOfMonth()); System.out.println("N�mero de dias do ano: " + data.lengthOfYear()); System.out.println("Menor data: " + LocalDate.MIN); System.out.println("Maior data: " + LocalDate.MAX); } }
Com isso vimos na pr�tica que a nova API de manipulaç�o de datas do Java 8 � bastante poderosa e facilita muito o desenvolvimento de aplicaç�es que precisam considerar datas. Agora o desenvolvedor n�o precisa gastar tanto tempo com v�rias classes para fazer o retorno das datas e nem precisa perder tempo com a convers�o das mesmas.
Espero que esse artigo tenha seja �til. At� a pr�xima!
Links:
- Descriç�o oficial da nova API no site da Oracle
- JavaDoc da nova API