Migration vers la nouvelle API Java 8 Date Time

1. Vue d'ensemble

Dans ce didacticiel, vous apprendrez à refactoriser votre code afin de tirer parti de la nouvelle API Date Time introduite dans Java 8.

2. Nouvelle API en un coup d'œil

Travailler avec des dates en Java était autrefois difficile. L'ancienne bibliothèque de dates fournie par JDK ne comprenait que trois classes: java.util.Date, java.util.Calendar et java.util.Timezone .

Celles-ci ne convenaient que pour les tâches les plus élémentaires. Pour tout ce qui est même à distance complexe, les développeurs devaient soit utiliser des bibliothèques tierces, soit écrire des tonnes de code personnalisé.

Java 8 a introduit une toute nouvelle API Date Time ( java.util.time. * ) Qui est vaguement basée sur la bibliothèque Java populaire appelée JodaTime. Cette nouvelle API a considérablement simplifié le traitement de la date et de l'heure et a corrigé de nombreuses lacunes de l'ancienne bibliothèque de dates.

1.1. Clarté de l'API

Un premier avantage de la nouvelle API est la clarté - l'API est très claire, concise et facile à comprendre. Il n'y a pas beaucoup d'incohérences trouvées dans l'ancienne bibliothèque telles que la numérotation des champs (dans les mois calendaires sont basés sur zéro, mais les jours de la semaine sont basés sur un).

1.2. Flexibilité de l'API

Un autre avantage est la flexibilité - travailler avec plusieurs représentations du temps . L'ancienne bibliothèque de dates ne comprenait qu'une seule classe de représentation de l'heure - java.util.Date , qui malgré son nom, est en fait un horodatage. Il ne stocke que le nombre de millisecondes écoulées depuis l'époque Unix.

La nouvelle API a de nombreuses représentations temporelles différentes, chacune adaptée à différents cas d'utilisation:

  • Instant - représente un point dans le temps (horodatage)
  • LocalDate - représente une date (année, mois, jour)
  • LocalDateTime - identique à LocalDate , mais inclut l'heure avec une précision de l'ordre de la nanoseconde
  • OffsetDateTime - identique à LocalDateTime , mais avec décalage de fuseau horaire
  • LocalTime - heure avec une précision nanoseconde et sans information de date
  • ZonedDateTime - identique à OffsetDateTime , mais comprend un ID de fuseau horaire
  • OffsetLocalTime - identique à LocalTime , mais avec un décalage de fuseau horaire
  • MonthDay - mois et jour, sans année ni heure
  • YearMonth - mois et année, sans jour ni heure
  • Durée - durée représentée en secondes, minutes et heures. A une précision nanoseconde
  • Période - durée représentée en jours, mois et années

1.3. Immuabilité et sécurité des fils

Un autre avantage est que toutes les représentations temporelles de l'API Java 8 Date Time sont immuables et donc sûres pour les threads.

Toutes les méthodes de mutation renvoient une nouvelle copie au lieu de modifier l'état de l'objet d'origine.

Les anciennes classes telles que java.util.Date n'étaient pas thread-safe et pouvaient introduire des bogues de concurrence très subtils.

1.4. Chaînage de méthodes

Toutes les méthodes de mutation peuvent être chaînées ensemble, permettant de mettre en œuvre des transformations complexes dans une seule ligne de code.

ZonedDateTime nextFriday = LocalDateTime.now() .plusHours(1) .with(TemporalAdjusters.next(DayOfWeek.FRIDAY)) .atZone(ZoneId.of("PST")); 

2. Exemples

Les exemples ci-dessous montrent comment effectuer des tâches courantes avec l'ancienne et la nouvelle API.

Obtenir l'heure actuelle

// Old Date now = new Date(); // New ZonedDateTime now = ZonedDateTime.now(); 

Représenter une heure précise

// Old Date birthDay = new GregorianCalendar(1990, Calendar.DECEMBER, 15).getTime(); // New LocalDate birthDay = LocalDate.of(1990, Month.DECEMBER, 15); 

Extraire des champs spécifiques

// Old int month = new GregorianCalendar().get(Calendar.MONTH); // New Month month = LocalDateTime.now().getMonth(); 

Ajouter et soustraire du temps

// Old GregorianCalendar calendar = new GregorianCalendar(); calendar.add(Calendar.HOUR_OF_DAY, -5); Date fiveHoursBefore = calendar.getTime(); // New LocalDateTime fiveHoursBefore = LocalDateTime.now().minusHours(5); 

Modifier des champs spécifiques

// Old GregorianCalendar calendar = new GregorianCalendar(); calendar.set(Calendar.MONTH, Calendar.JUNE); Date inJune = calendar.getTime(); // New LocalDateTime inJune = LocalDateTime.now().withMonth(Month.JUNE.getValue()); 

Tronquer

La troncature réinitialise tous les champs de temps plus petits que le champ spécifié. Dans l'exemple ci-dessous, les minutes et tout ce qui suit sera mis à zéro

// Old Calendar now = Calendar.getInstance(); now.set(Calendar.MINUTE, 0); now.set(Calendar.SECOND, 0); now.set(Calendar.MILLISECOND, 0); Date truncated = now.getTime(); // New LocalTime truncated = LocalTime.now().truncatedTo(ChronoUnit.HOURS); 

Conversion de fuseau horaire

// Old GregorianCalendar calendar = new GregorianCalendar(); calendar.setTimeZone(TimeZone.getTimeZone("CET")); Date centralEastern = calendar.getTime(); // New ZonedDateTime centralEastern = LocalDateTime.now().atZone(ZoneId.of("CET")); 

Obtenir l'intervalle de temps entre deux points dans le temps

// Old GregorianCalendar calendar = new GregorianCalendar(); Date now = new Date(); calendar.add(Calendar.HOUR, 1); Date hourLater = calendar.getTime(); long elapsed = hourLater.getTime() - now.getTime(); // New LocalDateTime now = LocalDateTime.now(); LocalDateTime hourLater = LocalDateTime.now().plusHours(1); Duration span = Duration.between(now, hourLater); 

Formatage et analyse de l'heure

DateTimeFormatter is a replacement for the old SimpleDateFormat that is thread-safe and provides additional functionality.

// Old SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date now = new Date(); String formattedDate = dateFormat.format(now); Date parsedDate = dateFormat.parse(formattedDate); // New LocalDate now = LocalDate.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); String formattedDate = now.format(formatter); LocalDate parsedDate = LocalDate.parse(formattedDate, formatter); 

Number of days in a month

// Old Calendar calendar = new GregorianCalendar(1990, Calendar.FEBRUARY, 20); int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); // New int daysInMonth = YearMonth.of(1990, 2).lengthOfMonth();

3. Interacting With Legacy Code

In many cases a user might need to ensure interoperability with third-party libraries that rely on the old date library.

In Java 8 old date library classes have been extended with methods that convert them to corresponding objects from new Date API.

New classes provide similar functionalities.

Instant instantFromCalendar = GregorianCalendar.getInstance().toInstant(); ZonedDateTime zonedDateTimeFromCalendar = new GregorianCalendar().toZonedDateTime(); Date dateFromInstant = Date.from(Instant.now()); GregorianCalendar calendarFromZonedDateTime = GregorianCalendar.from(ZonedDateTime.now()); Instant instantFromDate = new Date().toInstant(); ZoneId zoneIdFromTimeZone = TimeZone.getTimeZone("PST").toZoneId(); 

4. Conclusion

In this article we explored the new Date Time API available in Java 8. We took a look at its advantages, compared to the deprecated API and pointed out differences using multiple examples.

Notez que nous avons à peine effleuré la surface des capacités de la nouvelle API Date Time. Assurez-vous de lire la documentation officielle pour découvrir la gamme complète des outils proposés par la nouvelle API.

Des exemples de code peuvent être trouvés dans le projet GitHub.