Introdução ao Joda-Time

Introdução ao Joda-Time

1. Introdução

Joda-Time é a biblioteca de processamento de data e hora mais usada antes do lançamento do Java 8. Seu objetivo era oferecer uma API intuitiva para o processamento de data e hora e também resolver os problemas de design existentes na API Java de Data / Hora.

The central concepts implemented in this library were introduced in the JDK core with the release of the Java 8 version. A nova API de data e hora é encontrada no pacotejava.time (JSR-310). Uma visão geral desses recursos pode ser encontrada nestearticle.

Após o lançamento do Java 8, os autores consideram o projeto praticamente concluído e aconselham o uso da API Java 8, se possível.

2. Por que usar o Joda-Time?

A API de data / hora, antes do Java 8, apresentava vários problemas de design.

Entre os problemas está o fato de que as classesDateeSimpleDateFormatter não são thread-safe. Para resolver esse problema,Joda-Time uses immutable classes for handling date and time.

A classeDate não representa uma data real, mas em vez disso, especifica um instante no tempo, com precisão de milissegundos. O ano em aDate começa em 1900, enquanto a maioria das operações de data geralmente usa a hora da época, que começa em 1º de janeiro de 1970.

Além disso, o deslocamento de dia, mês e ano de aDate é contra-intuitivo. Os dias começam em 0, enquanto o mês começa em 1. Para acessar qualquer um deles, devemos usar a classeCalendar. Joda-Time offers a clean and fluent API for handling dates and time.

Joda-Time também oferecesupport for eight calendar systems, enquanto Java oferece apenas 2: Gregoriano -java.util.GregorianCalendare Japonês -java.util.JapaneseImperialCalendar.

 3. Configuração

Para incluir a funcionalidade da biblioteca Joda-Time, precisamos adicionar a seguinte dependência deMaven Central:


    joda-time
    joda-time
    2.10

 4. Visão geral da biblioteca

Joda-Time modelaconcept of date and time usando as classes do pacoteorg.joda.time.

Entre essas classes, as mais usadas são:

  • LocalDate - representa uma data sem hora

  • LocalTime - representa a hora sem o fuso horário

  • LocalDateTime - representa a data e a hora sem um fuso horário

  • Instant - representa um ponto exato no tempo em milissegundos da época Java de 1970-01-01T00: 00: 00Z

  • Duration - representa a duração em milissegundos entre 2 pontos no tempo

  • Period - semelhante aDuration, mas permitindo acesso a componentes individuais do objeto de data e hora, como anos, mês, dias, etc.

  • Interval - representa o intervalo de tempo entre 2 instantes

Outros recursos importantes sãodate parsers and formatters. Eles podem ser encontrados no pacoteorg.joda.time.format.

As classes específicas decalendar system and time zone podem ser encontradas nos pacotesorg.joda.time.chrono and org.joda.time.tz.

Vamos dar uma olhada em alguns exemplos em que usamos os principais recursos do Joda-Time para lidar com data e hora.

5. Representando Data e Hora

5.1. Data e Hora Atual

The current date, without time information, pode ser obtido usando o métodonow() de the LocalDate class:

LocalDate currentDate = LocalDate.now();

Quando precisamos apenas da hora atual, sem informações de data, podemos usar a classeLocalTime:

LocalTime currentTime = LocalTime.now();

Para obter umrepresentation of the current date and time without considering the time zone, we can use LocalDateTime:

LocalDateTime currentDateAndTime = LocalDateTime.now();

Agora, usandocurrentDateAndTime, podemos convertê-lo para os outros tipos de objetos modelando data e hora.

Podemos obter um objetoDateTime (que leva em consideração o fuso horário) usando o métodotoDateTime(). Quando o tempo não é necessário, podemos convertê-lo emLocalDate com o métodotoLocalDate(), e quando precisamos apenas do tempo, podemos usartoLocalTime() para obter um objetoLocalTime:

DateTime dateTime = currentDateAndTime.toDateTime();
LocalDate localDate = currentDateAndTime.toLocalDate();
LocalTime localTime = currentDateAndTime.toLocalTime();

All the above methods have an overloaded method which accepts a DateTimeZone object para nos ajudar a representar a data ou hora no fuso horário especificado:

LocalDate currentDate = LocalDate.now(DateTimeZone.forID("America/Chicago"));

Além disso, o Joda-Time oferece excelente integração com a API Java Date and Time. Os construtores aceitam um objetojava.util.Date e também, podemos usar o métodotoDate() para retornar um objetojava.util.Date:

LocalDateTime currentDateTimeFromJavaDate = new LocalDateTime(new Date());
Date currentJavaDate = currentDateTimeFromJavaDate.toDate();

5.2. Data e hora personalizadas

Para representar data e hora personalizadas, o Joda-Time nos fornece vários construtores. Podemos especificar os seguintes objetos:

  • umInstant

  • um objeto JavaDate

  • uma representaçãoString da data e hora usando o formato ISO

  • partes da data e hora: ano, mês, dia, hora, minuto, segundo, milissegundo

Date oneMinuteAgoDate = new Date(System.currentTimeMillis() - (60 * 1000));
Instant oneMinutesAgoInstant = new Instant(oneMinuteAgoDate);

DateTime customDateTimeFromInstant = new DateTime(oneMinutesAgoInstant);
DateTime customDateTimeFromJavaDate = new DateTime(oneMinuteAgoDate);
DateTime customDateTimeFromString = new DateTime("2018-05-05T10:11:12.123");
DateTime customDateTimeFromParts = new DateTime(2018, 5, 5, 10, 11, 12, 123);

Outra maneira de definir uma data e hora personalizadas é analisando uma determinada representaçãoString de uma data e hora no formato ISO:

DateTime parsedDateTime = DateTime.parse("2018-05-05T10:11:12.123");

Também podemos analisar representações personalizadas de uma data e hora definindo umDateTimeFormatter personalizado:

DateTimeFormatter dateTimeFormatter
  = DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss");
DateTime parsedDateTimeUsingFormatter
  = DateTime.parse("05/05/2018 10:11:12", dateTimeFormatter);

6. Trabalhando com data e hora

6.1. UsandoInstant

UmInstant representa o número de milissegundos de 1970-01-01T00: 00: 00Z até um determinado momento no tempo. Por exemplo, o momento atual no tempo pode ser obtido usando o construtor padrão ou o métodonow():

Instant instant = new Instant();
Instant.now();

Para criar umInstant para um momento personalizado no tempo, podemos usar um dos construtores ou usar os métodosofEpochMilli()eofEpochSecond():

Instant instantFromEpochMilli
  = Instant.ofEpochMilli(milliesFromEpochTime);
Instant instantFromEpocSeconds
  = Instant.ofEpochSecond(secondsFromEpochTime);

Os construtores aceitam umString representando uma data e hora no formato ISO, um valor JavaDate oulong representando o número de milissegundos de 1970-01-01T00: 00: 00Z:

Instant instantFromString
  = new Instant("2018-05-05T10:11:12");
Instant instantFromDate
  = new Instant(oneMinuteAgoDate);
Instant instantFromTimestamp
  = new Instant(System.currentTimeMillis() - (60 * 1000));

Quando a data e a hora são representadas comoString, temos a opção de analisarString usando o formato desejado:

Instant parsedInstant
  = Instant.parse("05/05/2018 10:11:12", dateTimeFormatter);

Agora que sabemos o queInstant representa e como podemos criar um, vamos ver como ele pode ser usado.

Para comparar com objetosInstant, podemos usarcompareTo() porque ele implementa a interfaceComparable, mas também podemos usar os métodos da API Joda-Time fornecidos na interfaceReadableInstant queInstant também implementa:

assertTrue(instantNow.compareTo(oneMinuteAgoInstant) > 0);
assertTrue(instantNow.isAfter(oneMinuteAgoInstant));
assertTrue(oneMinuteAgoInstant.isBefore(instantNow));
assertTrue(oneMinuteAgoInstant.isBeforeNow());
assertFalse(oneMinuteAgoInstant.isEqual(instantNow));

Outro recurso útil é queInstant can be converted to a DateTime object or event a Java Date:

DateTime dateTimeFromInstant = instant.toDateTime();
Date javaDateFromInstant = instant.toDate();

Quando precisamos acessar partes de uma data e hora, como o ano, a hora e assim por diante, podemos usar o métodoget() e especificar umDateTimeField:

int year = instant.get(DateTimeFieldType.year());
int month = instant.get(DateTimeFieldType.monthOfYear());
int day = instant.get(DateTimeFieldType.dayOfMonth());
int hour = instant.get(DateTimeFieldType.hourOfDay());

Agora que cobrimos a classeInstant, vamos ver alguns exemplos de como podemos usarDuration,PeriodeInterval.

6.2. UsandoDuration,Period eInterval

UmDuration representa o tempo em milissegundos entre dois pontos no tempo ou, neste caso, pode ser doisInstants. We’ll use this when we need to add or subtract a specific amount of time to or from another Instant without considering chronology and time zones:

long currentTimestamp = System.currentTimeMillis();
long oneHourAgo = currentTimestamp - 24*60*1000;
Duration duration = new Duration(oneHourAgo, currentTimestamp);
Instant.now().plus(duration);

Além disso, podemos determinar quantos dias, horas, minutos, segundos ou milissegundos a duração representa:

long durationInDays = duration.getStandardDays();
long durationInHours = duration.getStandardHours();
long durationInMinutes = duration.getStandardMinutes();
long durationInSeconds = duration.getStandardSeconds();
long durationInMilli = duration.getMillis();

A principal diferença entrePeriod eDuration é quePeriod is defined in terms of its date and time components (years, months, hours, etc.) and doesn’t represent an exact number of milliseconds. Ao usar cálculos de data e horaPeriodwill consider the time zone and daylight saving.

Por exemplo, adicionarPeriod de 1 mês a 1º de fevereiro resultará na representação da data de 1º de março. UsandoPeriod, a biblioteca levará em consideração os anos bissextos.

Se usarmos umDuration, o resultado não seria correto, porqueDuration representa uma quantidade fixa de tempo que não leva em consideração a cronologia ou fusos horários:

Period period = new Period().withMonths(1);
LocalDateTime datePlusPeriod = localDateTime.plus(period);

UmInterval, como o nome indica, representa a data e o intervalo de tempo entre dois pontos fixos no tempo representados por dois objetosInstant:

Interval interval = new Interval(oneMinuteAgoInstant, instantNow);

A classe é útil quando precisamos verificar se dois intervalos se sobrepõem ou calcular a diferença entre eles. O métodooverlap() retornará osInterval ounull sobrepostos quando eles não se sobrepõem:

Instant startInterval1 = new Instant("2018-05-05T09:00:00.000");
Instant endInterval1 = new Instant("2018-05-05T11:00:00.000");
Interval interval1 = new Interval(startInterval1, endInterval1);

Instant startInterval2 = new Instant("2018-05-05T10:00:00.000");
Instant endInterval2 = new Instant("2018-05-05T11:00:00.000");
Interval interval2 = new Interval(startInterval2, endInterval2);

Interval overlappingInterval = interval1.overlap(interval2);

A diferença entre os intervalos pode ser calculada usando o métodogap(), e quando queremos saber se o final de um intervalo é igual ao início de outro intervalo podemos usar o métodoabuts():

assertTrue(interval1.abuts(new Interval(
  new Instant("2018-05-05T11:00:00.000"),
  new Instant("2018-05-05T13:00:00.000"))));

6.3. Operações de Data e Hora

Algumas das operações mais comuns são adicionar, subtrair e converter data e hora. A biblioteca fornece métodos específicos para cada uma das classesLocalDate,LocalTime,LocalDateTime eDateTime. É importante observar que essas classes são imutáveis ​​para que cada invocação de método crie um novo objeto de seu tipo.

Vamos pegarLocalDateTime para o momento atual e tentar mudar seu valor:

LocalDateTime currentLocalDateTime = LocalDateTime.now();

Para adicionar um dia extra acurrentLocalDateTime, usamos o métodoplusDays():

LocalDateTime nextDayDateTime = currentLocalDateTime.plusDays(1);

Também podemos usar o métodoplus() para adicionar umPeriod ouDuration ao nossocurrentLocalDateTime:

Period oneMonth = new Period().withMonths(1);
LocalDateTime nextMonthDateTime = currentLocalDateTime.plus(oneMonth);

Os métodos são semelhantes para os outros componentes de data e hora, por exemplo,plusYears() para adicionar anos extras, plusSeconds () para adicionar mais segundos e assim por diante.

Para subtrair um dia de nossocurrentLocalDateTime, podemos usar o métodominusDays():

LocalDateTime previousDayLocalDateTime
  = currentLocalDateTime.minusDays(1);

Além disso, fazendo cálculos com data e hora, também podemos definir partes individuais da data ou hora. Por exemplo, definir a hora como 10 pode ser alcançado usando o métodowithHourOfDay(). Outros métodos que começam com o prefixo“with” podem ser usados ​​para definir os componentes dessa data ou hora:

LocalDateTime currentDateAtHour10 = currentLocalDateTime
  .withHourOfDay(0)
  .withMinuteOfHour(0)
  .withSecondOfMinute(0)
  .withMillisOfSecond(0);

Outro aspecto importante é que podemos converter de um tipo de classe de data e hora para outro. Para fazer isso, podemos usar métodos específicos fornecidos pela biblioteca:

  • toDateTime() - converteLocalDateTime em um objetoDateTime

  • toLocalDate() - converteLocalDateTime em um objetoLocalDate

  • toLocalTime () - converte LocalDateTime em um objeto LocalTime

  • toDate() - converteLocalDateTime em um objeto JavaDate

7. Trabalhando com fusos horários

O Joda-Time facilita o trabalho com diferentes fusos horários e a alteração entre eles. Temos a classe abstrataDateTimeZone que é usada para representar todos os aspectos relativos a um fuso horário.

The default time zone used by Joda-Time is selected from the user.timezone Java system property. A API da biblioteca nos permite especificar, individualmente para cada classe ou cálculo, qual fuso horário deve ser usado. Por exemplo, podemos criar um objeto LocalDateTime

Quando sabemos que usaremos um fuso horário específico em todo o aplicativo, podemos definir o fuso horário padrão:

DateTimeZone.setDefault(DateTimeZone.UTC);

A partir de agora, todas as operações de data e hora, se não especificadas de outra forma, serão representadas no fuso horário UTC.

Para ver todos os fusos horários disponíveis, podemos usar o métodogetAvailableIDs():

DateTimeZone.getAvailableIDs()

Quando precisamos representar a data ou hora em um fuso horário específico, podemos usar qualquer uma das classesLocalTime,LocalDate,LocalDateTime,DateTimee especificar no construtor o ObjetoDateTimeZone:

DateTime dateTimeInChicago
  = new DateTime(DateTimeZone.forID("America/Chicago"));
DateTime dateTimeInBucharest
  = new DateTime(DateTimeZone.forID("Europe/Bucharest"));
LocalDateTime localDateTimeInChicago
  = new LocalDateTime(DateTimeZone.forID("America/Chicago"));

Além disso, ao converter entre essas classes, podemos especificar o fuso horário desejado. O métodotoDateTime() aceita um objetoDateTimeZone etoDate() aceita o objeto java.util.TimeZone:

DateTime convertedDateTime
  = localDateTimeInChicago.toDateTime(DateTimeZone.forID("Europe/Bucharest"));
Date convertedDate
  = localDateTimeInChicago.toDate(TimeZone.getTimeZone("Europe/Bucharest"));

8. Conclusão

O Joda-Time é uma biblioteca fantástica que começou com o objetivo principal de corrigir os problemas no JDK relacionados a operações de data e hora. Ela logo se tornou a bibliotecade facto para manipulação de data e hora e, recentemente, seus principais conceitos foram introduzidos no Java 8.

É importante notar que o autor o considera“to be a largely finished project”e recomenda migrar o código existente para usar a implementação do Java 8.

O código-fonte do artigo está disponívelover on GitHub.