Introduction à Joda-Time

1. Introduction

Joda-Time est la bibliothèque de traitement de la date et de l'heure la plus largement utilisée, avant la sortie de Java 8. Son objectif était de proposer une API intuitive pour le traitement de la date et de l'heure et également de résoudre les problèmes de conception qui existaient dans l'API Java Date / Time.

Les concepts centraux implémentés dans cette bibliothèque ont été introduits dans le noyau JDK avec la sortie de la version Java 8. La nouvelle API de date et d'heure se trouve dans le package java.time (JSR-310). Vous trouverez un aperçu de ces fonctionnalités dans cet article.

Après la sortie de Java 8, les auteurs considèrent que le projet est presque terminé et conseillent d'utiliser l'API Java 8 si possible.

2. Pourquoi utiliser Joda-Time?

L'API date / heure, avant Java 8, présentait plusieurs problèmes de conception.

Parmi les problèmes, il y a le fait que les classes Date et SimpleDateFormatter ne sont pas thread-safe. Pour résoudre ce problème, Joda-Time utilise des classes immuables pour gérer la date et l'heure.

La classe Date ne représente pas une date réelle, mais à la place, elle spécifie un instant dans le temps, avec une précision de la milliseconde. L'année d'une date commence à partir de 1900, alors que la plupart des opérations de date utilisent généralement l'heure de l'époque qui commence à partir du 1er janvier 1970.

De plus, le décalage du jour, du mois et de l'année d'une date est contre-intuitif. Les jours commencent à 0, tandis que le mois commence à 1. Pour accéder à l'un d'entre eux, nous devons utiliser la classe Calendar . Joda-Time propose une API propre et fluide pour gérer les dates et l'heure.

Joda-Time prend également en charge huit systèmes de calendrier , tandis que Java n'en propose que 2: grégorien - java.util.GregorianCalendar et japonais - java.util.JapaneseImperialCalendar .

3. Configuration

Pour inclure la fonctionnalité de la bibliothèque Joda-Time, nous devons ajouter la dépendance suivante à partir de Maven Central:

 joda-time joda-time 2.10 

4. Aperçu de la bibliothèque

Joda-Time modélise le concept de date et d'heure en utilisant les classes du package org.joda.time .

Parmi ces classes, les plus couramment utilisées sont:

  • LocalDate - représente une date sans heure
  • LocalTime - représente l'heure sans le fuseau horaire
  • LocalDateTime - représente à la fois la date et l'heure sans fuseau horaire
  • Instant - représente un moment exact en millisecondes depuis l'époque Java de 1970-01-01T00: 00: 00Z
  • Durée - représente la durée en millisecondes entre 2 points dans le temps
  • Période - similaire à la durée , mais permettant l'accès à des composants individuels de l'objet date et heure, comme les années, le mois, les jours, etc.
  • Intervalle - représente l'intervalle de temps entre 2 instants

D'autres fonctionnalités importantes sont les analyseurs de date et les formateurs . Ceux-ci peuvent être trouvés dans le package org.joda.time.format .

Le système de calendrier et le fuseau horaire des cours spécifiques de se trouvent dans le org.joda.time.chrono et org.joda.time.tz paquets.

Jetons un coup d'œil à quelques exemples dans lesquels nous utilisons les fonctionnalités clés de Joda-Time pour gérer la date et l'heure.

5. Représentation de la date et de l'heure

5.1. Date et heure actuelles

La date actuelle, sans informations d'heure, peut être obtenue en utilisant la méthode now () de la classe LocalDate :

LocalDate currentDate = LocalDate.now();

Lorsque nous n'avons besoin que de l'heure actuelle, sans informations de date, nous pouvons utiliser la classe LocalTime :

LocalTime currentTime = LocalTime.now();

Pour obtenir une représentation de la date et de l'heure actuelles sans tenir compte du fuseau horaire, nous pouvons utiliser LocalDateTime :

LocalDateTime currentDateAndTime = LocalDateTime.now();

Maintenant, en utilisant currentDateAndTime , nous pouvons le convertir en d'autres types d'objets modélisant la date et l'heure.

On peut obtenir un objet DateTime (qui prend en compte le fuseau horaire) en utilisant la méthode toDateTime () . Lorsque le temps n'est pas nécessaire, nous pouvons le convertir en LocalDate avec la méthode toLocalDate () , et lorsque nous n'avons besoin que du temps, nous pouvons utiliser toLocalTime () pour obtenir un objet LocalTime :

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

Toutes les méthodes ci-dessus ont une méthode surchargée qui accepte un objet DateTimeZone pour nous aider à représenter la date ou l'heure dans le fuseau horaire spécifié:

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

En outre, Joda-Time offre une excellente intégration avec l'API Java Date and Time. Les constructeurs acceptent un objet java.util.Date et aussi, nous pouvons utiliser la méthode toDate () pour renvoyer un objet java.util.Date :

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

5.2. Date et heure personnalisées

Pour représenter la date et l'heure personnalisées, Joda-Time nous fournit plusieurs constructeurs. Nous pouvons spécifier les objets suivants:

  • un instant
  • un objet Java Date
  • a Représentation sous forme de chaîne de la date et de l'heure au format ISO
  • parties de la date et de l'heure: année, mois, jour, heure, minute, seconde, milliseconde
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); 

Une autre façon de définir une date et une heure personnalisées consiste à analyser une représentation String donnée d'une date et d'une heure au format ISO:

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

Nous pouvons également analyser les représentations personnalisées d'une date et d'une heure en définissant un DateTimeFormatter personnalisé :

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

6. Utilisation de la date et de l'heure

6.1. Utiliser Instant

Un instant représente le nombre de millisecondes entre 1970-01-01T00: 00: 00Z et un moment donné. Par exemple, le moment actuel dans le temps peut être obtenu en utilisant le constructeur par défaut ou la méthode now () :

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

To create an Instant for a custom moment in time we can use either one of the constructors or use the methods ofEpochMilli() and ofEpochSecond():

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

The constructors accept a String representing a date and time in the ISO format, a Java Date or a long value representing the number of milliseconds from 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));

When date and time are represented as a String we have the option to parse the String using our desired format:

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

Now that we know what Instant represents and how we can create one, let's see how it can be used.

To compare to Instant objects we can use compareTo() because it implements the Comparable interface, but also we can use the Joda-Time API methods provided in the ReadableInstant interface which Instant also implements:

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

Another helpful feature is that Instant can be converted to a DateTime object or event a Java Date:

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

When we need to access parts of a date and time, like the year, the hour and so on, we can use the get() method and specify a DateTimeField:

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

Now that we covered the Instant class let's see some examples of how we can use Duration, Period and Interval.

6.2. Using Duration, Period and Interval

A Duration represents the time in milliseconds between two points in time or in this case it could be two Instants. 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);

Also, we can determine how many days, hours, minutes, seconds or milliseconds the duration represents:

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

The main difference between Period and Duration is that Period is defined in terms of its date and time components (years, months, hours, etc.) and doesn't represent an exact number of milliseconds. When using Period date and time calculations will consider the time zone and daylight saving.

For example, adding a Period of 1 month to the February 1st will result in the date representation of March 1st. By using Period the library will take into account leap years.

If we're to use a Duration we the result wouldn't be correct, because the Duration represents a fixed amount of time that doesn't take into account chronology or time zones:

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

An Interval, as the name states, represents the date and time interval between two fixed points in time represented by two Instant objects:

Interval interval = new Interval(oneMinuteAgoInstant, instantNow);

The class is useful when we need to check whether two intervals overlap or calculate the gap between them. The overlap() method will return the overlapping Interval or null when they don't overlap:

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);

The difference between intervals can be calculated using the gap() method, and when we want to know whether the end of an interval is equal to the start of another interval we can use the abuts() method:

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

6.3. Date and Time Operations

Some of the most common operations are to add, subtract and convert date and time. The library provides specific methods for each of the classes LocalDate, LocalTime, LocalDateTime, and DateTime. It's important to note that these classes are immutable so that every method invocation will create a new object of its type.

Let's take LocalDateTime for the current moment and try to change its value:

LocalDateTime currentLocalDateTime = LocalDateTime.now();

To add an extra day to currentLocalDateTime we use the plusDays() method:

LocalDateTime nextDayDateTime = currentLocalDateTime.plusDays(1);

We can also use plus() method to add a Period or Duration to our currentLocalDateTime:

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

The methods are similar for the other date and time components, for example, plusYears() for adding extra years, plusSeconds() for adding more seconds and so on.

To subtract a day from our currentLocalDateTime we can use the minusDays() method:

LocalDateTime previousDayLocalDateTime = currentLocalDateTime.minusDays(1);

Besides, doing calculations with date and time, we can also, set individual parts of the date or time. For example, setting the hour to 10 can be achieved using the withHourOfDay() method. Other methods that start with the prefix “with” can be used to set components of that date or time:

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

Another important aspect is that we can convert from a date and time class type to another. To do this, we can use specific methods provided by the library:

  • toDateTime() – converts LocalDateTime to a DateTime object
  • toLocalDate() – converts LocalDateTime to a LocalDate object
  • toLocalTime() – converts LocalDateTime to a LocalTime object
  • toDate() – converts LocalDateTime to a Java Date object

7. Working with Time Zones

Joda-Time makes it easy for us to work with different time zones and changed between them. We have the DateTimeZone abstract class which is used to represent all aspects regarding a time zone.

The default time zone used by Joda-Time is selected from the user.timezone Java system property. The library API lets us specify, individually for each class or calculation what timezone should be used. For example, we can create a LocalDateTime object

When we know that we'll use a specific time zone across the whole application, we can set the default time zone:

DateTimeZone.setDefault(DateTimeZone.UTC);

From now one all the date and time operations, if not otherwise specified, will be represented in the UTC time zone.

To see all the available time zones we can use the method getAvailableIDs():

DateTimeZone.getAvailableIDs()

When we need to represent the date or time in a specific time zone we can use any of the classes LocalTime, LocalDate, LocalDateTime, DateTime and specify in the constructor the DateTimeZone object:

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

Also, when converting between those classes we can specify the desired time zone. The method toDateTime() accepts a DateTimeZone object and toDate() accepts java.util.TimeZone object:

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

8. Conclusion

Joda-Time est une bibliothèque fantastique qui a commencé avec l'objectif principal de résoudre les problèmes dans le JDK concernant les opérations de date et d'heure. Elle est rapidement devenue la bibliothèque de facto pour la gestion de la date et de l'heure et récemment ses principaux concepts ont été introduits dans Java 8.

Il est important de noter que l'auteur le considère comme «un projet largement terminé» et recommande de migrer le code existant pour utiliser l'implémentation Java 8.

Le code source de l'article est disponible à l'adresse over sur GitHub.