LocalDate, LocalTime et LocalDateTime : ce qu'ils sont, travail avec la date

Auteur : IT Sectr Publié le : 2026-07-13 Temps de lecture : 12 min

LocalDate, LocalTime et LocalDateTime sont les principales classes du paquetage java.time qui fournissent la gestion de la date et de l'heure sans liaison à un fuseau horaire. Selon la documentation Oracle (Java 17, 2024), ces types sont conçus comme immuables et thread-safe, ce qui les rend sûrs pour les applications multithread. Ils sont devenus disponibles sur Android via le desugaring à partir de l'API 26 et, pour les versions plus anciennes — via la bibliothèque ThreeTenABP.

Points clés

  • LocalDate — une classe immuable pour représenter une date (année, mois, jour) sans heure ni fuseau horaire.
  • LocalTime — une classe immuable pour représenter l'heure (heure, minute, seconde, nanoseconde) sans date ni fuseau horaire.
  • LocalDateTime — une combinaison de LocalDate et LocalTime qui stocke à la fois la date et l'heure sans liaison à un fuseau horaire.
  • Les trois classes prennent en charge les opérations arithmétiques — ajout et soustraction de jours, mois, heures via les méthodes plus et minus.
  • Sur Android, ces types sont disponibles via le desugaring (API 26+) ou la bibliothèque ThreeTenABP (API < 26).

Que sont LocalDate, LocalTime et LocalDateTime ?

LocalDate — une classe qui représente une date au format année-mois-jour sans informations d'heure ni de fuseau horaire. Elle est utilisée pour stocker des données telles que les anniversaires, les dates d'événements ou les dates d'expiration.

LocalDate stocke une année dans la plage de -999999999 à +999999999, un mois de 1 à 12 et un jour du mois en tenant compte des années bissextiles. La classe est complètement immuable — toute opération retourne un nouvel objet.

LocalTime représente l'heure de la journée : heures, minutes, secondes et nanosecondes. La précision maximale est d'une nanoseconde. LocalTime ne contient aucune information de date ou de fuseau horaire, ce qui le rend pratique pour stocker les heures d'ouverture ou la durée des processus.

LocalDateTime combine LocalDate et LocalTime en un seul objet. C'est le type le plus couramment utilisé lorsque vous devez stocker à la fois la date et l'heure, mais qu'aucune liaison de fuseau horaire n'est requise. Par exemple, la date et l'heure d'un concert au format local.

Selon la documentation Oracle Java (2024), les trois classes sont conçues sur la base d'idées de la bibliothèque Joda-Time, mais avec une architecture améliorée et une intégration complète dans la bibliothèque standard.

Comment fonctionne le paquetage java.time ?

Le paquetage java.time a été introduit dans Java 8 en remplacement des classes obsolètes Date, Calendar et SimpleDateFormat. Son architecture est construite sur les principes d'objets immuables et d'interface fluide.

Une caractéristique clé — toutes les classes principales sont value-based. Cela signifie que leurs instances sont comparées par valeur, non par référence, et qu'elles ne peuvent pas être héritées. Pour comparer deux objets, utilisez la méthode equals, pas l'opérateur ==.

Le paquetage est divisé en plusieurs catégories. Types sans fuseau horaire — LocalDate, LocalTime, LocalDateTime — sont utilisés pour les dates et heures locales. Types avec fuseau horaire — ZonedDateTime, OffsetDateTime, OffsetTime — ajoutent des informations de décalage ou de zone. Types instantanés — Instant — représentent un point sur la ligne temporelle en UTC.

Cette séparation résout un problème inhérent à l'ancienne API : le développeur ne savait jamais si un objet Date contenait des informations de fuseau horaire ou non. En java.time, chaque type déclare explicitement sa sémantique.

LocalDate : travail avec la date

La classe LocalDate fournit de nombreuses méthodes pour créer, lire et modifier les dates. La date actuelle peut être obtenue via la méthode statique now(). Une date spécifique — via la méthode of(int year, int month, int dayOfMonth).

Pour lire les composants de la date, des getters sont utilisés : getYear(), getMonthValue(), getDayOfMonth(), getDayOfWeek(), getDayOfYear(). La méthode getMonth() retourne l'énumération Month, et getDayOfWeek() retourne l'énumération DayOfWeek.

LocalDate prend en charge la vérification des dates. Les méthodes isBefore(), isAfter() et isEqual() permettent de comparer des dates. La méthode isLeapYear() vérifie si l'année est bissextile. La méthode lengthOfMonth() retourne le nombre de jours dans le mois, et lengthOfYear() le nombre de jours dans l'année.

Pour la modification, utilisez les méthodes withYear(), withMonth(), withDayOfMonth(), qui retournent un nouvel objet avec le composant modifié. Les méthodes plusDays(), minusMonths() et similaires effectuent l'arithmétique des dates.

LocalTime : travail avec l'heure

LocalTime représente l'heure de la journée avec une précision à la nanoseconde. Le format standard est ISO-8601 (HH:mm:ss.nnnnnnnnn). La valeur minimale est 00:00, la maximale est 23:59:59.999999999.

Vous pouvez créer un objet LocalTime en utilisant now() pour l'heure actuelle, of(int hour, int minute), of(int hour, int minute, int second) ou of(int hour, int minute, int second, int nanoOfSecond). La méthode parse(CharSequence text) analyse une chaîne au format ISO-8601.

Les getters incluent getHour(), getMinute(), getSecond(), getNano(). La méthode toSecondOfDay() retourne le nombre de secondes depuis le début de la journée, et toNanoOfDay() retourne les nanosecondes. C'est pratique pour calculer la durée au sein d'une même journée.

LocalTime prend en charge les mêmes opérations de comparaison et de modification que LocalDate : plusHours(), minusMinutes(), withHour(), withMinute(). Les méthodes isBefore() et isAfter() fonctionnent en considérant que l'heure est cyclique au sein d'une journée.

LocalDateTime : combinaison de date et d'heure

LocalDateTime combine les capacités de LocalDate et LocalTime en une seule classe. Il stocke à la fois la date et l'heure, mais sans fuseau horaire. C'est le type local le plus flexible, mais il nécessite de la prudence lors de son utilisation dans des systèmes distribués.

Vous pouvez créer LocalDateTime via les méthodes statiques now(), of(LocalDate date, LocalTime time), of(int year, Month month, int dayOfMonth, int hour, int minute) et leurs surcharges. Vous pouvez également combiner LocalDate et LocalTime via la méthode atTime().

LocalDateTime fournit un accès à tous les champs de date et d'heure via des getters correspondants : toLocalDate() et toLocalTime() retournent des composants individuels. La méthode truncatedTo(TemporalUnit unit) permet d'arrondir l'heure à une précision donnée — par exemple, aux minutes.

Pour convertir vers un fuseau horaire, utilisez la méthode atZone(ZoneId zone), qui retourne ZonedDateTime. C'est la seule façon d'ajouter un fuseau horaire à LocalDateTime.

Comment créer des objets de date et d'heure ?

Les trois classes utilisent un modèle de création unifié via des méthodes statiques d'usine. Les constructeurs des classes sont déclarés private — vous ne pouvez pas créer un objet directement avec new.

Méthodes principales de création :

  • now() — date/heure actuelle depuis l'horloge système
  • of(...) — à partir de composants (année, mois, jour, etc.)
  • parse(String) — à partir d'une chaîne au format ISO-8601
  • from(TemporalAccessor) — à partir d'un autre objet temporel

La méthode of a de nombreuses surcharges. Pour LocalDate, vous avez besoin de l'année, du mois et du jour. Pour LocalTime — des heures et des minutes (éventuellement des secondes et des nanosecondes). Pour LocalDateTime — année, mois, jour, heures, minutes. Le mois peut être passé comme int (1-12) ou comme l'énumération Month.

kotlin
val today = LocalDate.now()
val specificDate = LocalDate.of(2026, Month.JULY, 21)
val parsedDate = LocalDate.parse("2026-07-21")

val currentTime = LocalTime.now()
val lunchTime = LocalTime.of(13, 30, 0)
val parsedTime = LocalTime.parse("13:30:00")

val now = LocalDateTime.now()
val meeting = LocalDateTime.of(2026, 7, 21, 15, 0)

Conversion entre les types

Les classes java.time sont conçues pour une conversion pratique entre elles. LocalDate peut être converti en LocalDateTime via la méthode atTime(LocalTime) ou atStartOfDay(). LocalTime — via atDate(LocalDate).

LocalDateTime peut être reconverti en LocalDate via toLocalDate() et en LocalTime via toLocalTime(). Pour convertir en ZonedDateTime, utilisez la méthode atZone(ZoneId).

La conversion en java.util.Date (pour la compatibilité avec le code existant) nécessite une étape intermédiaire via Instant et un fuseau horaire. Selon Baeldung (2024), cette opération est effectuée via Date.from(instant).

kotlin
val date = LocalDate.of(2026, 7, 21)
val dateTime = date.atTime(LocalTime.of(10, 30))

val time = LocalTime.of(14, 0)
val dateTimeFromTime = time.atDate(date)

val extractedDate = dateTime.toLocalDate()
val extractedTime = dateTime.toLocalTime()

val zoned = dateTime.atZone(ZoneId.of("Europe/Moscow"))

Formatage et analyse

Pour le formatage et l'analyse, la classe DateTimeFormatter est utilisée. Elle fournit des formats prédéfinis via des constantes (ISO_LOCAL_DATE, ISO_LOCAL_TIME, ISO_LOCAL_DATE_TIME) et la possibilité de créer des formats personnalisés via des chaînes de motif.

Les motifs de formatage utilisent des symboles : yyyy — année, MM — mois (deux chiffres), dd — jour, HH — heure (0-23), mm — minute, ss — seconde. La méthode format() est appelée sur l'objet date-heure ou via DateTimeFormatter.

DateTimeFormatter prend également en charge la localisation via les méthodes statiques ofLocalizedDate(FormatStyle), ofLocalizedTime(FormatStyle) et ofLocalizedDateTime(FormatStyle). Les styles disponibles sont SHORT, MEDIUM, LONG et FULL.

kotlin
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
val formatted = LocalDateTime.now().format(formatter)

val parsed = LocalDate.parse(
    "21.07.2026",
    DateTimeFormatter.ofPattern("dd.MM.yyyy")
)

Comparaison des objets date-heure

Les trois classes implémentent l'interface Comparable, ce qui permet de les comparer naturellement. La méthode compareTo() retourne un nombre négatif, zéro ou positif selon l'ordre. Les méthodes isBefore(), isAfter() et isEqual() retournent un booléen.

Pour LocalDate, la comparaison est chronologique — une date antérieure est considérée plus petite. Pour LocalTime — par heure de la journée. Pour LocalDateTime — d'abord par date, puis par heure. Toutes les comparaisons tiennent correctement compte des années bissextiles et du nombre de jours dans les mois.

Une différence importante avec l'ancienne API : equals() pour LocalDate, LocalTime et LocalDateTime compare les valeurs, pas les références. Cela signifie que deux objets avec les mêmes champs seront égaux, même s'il s'agit d'instances différentes.

kotlin
val d1 = LocalDate.of(2026, 7, 21)
val d2 = LocalDate.of(2026, 12, 25)

if (d1.isBefore(d2)) {
    Log.d("Date", "d1 est avant d2")
}

val sortedDates = listOf(d2, d1).sorted()

Arithmétique de date et d'heure

Les trois classes prennent en charge les opérations arithmétiques via les méthodes plus et minus. Pour LocalDate, plusDays(), plusWeeks(), plusMonths(), plusYears() et les méthodes minus correspondantes sont disponibles. LocalTime prend en charge plusHours(), plusMinutes(), plusSeconds(), plusNanos().

LocalDateTime hérite de toutes les opérations arithmétiques des deux types. Une caractéristique notable de LocalDate : lors de l'ajout d'un mois, les résultats gèrent correctement les différentes longueurs des mois. Par exemple, 31 janvier + 1 mois = 28 (29 en année bissextile) février.

Pour des opérations plus complexes, il existe les classes Period (pour les dates) et Duration (pour le temps). Les méthodes plus(TemporalAmount) et minus(TemporalAmount) acceptent ces objets.

kotlin
val today = LocalDate.now()
val nextWeek = today.plusDays(7)
val nextMonth = today.plusMonths(1)
val lastYear = today.minusYears(1)

val now = LocalTime.now()
val inTwoHours = now.plusHours(2)
val halfHourAgo = now.minusMinutes(30)

Exemples de code en Kotlin

Prenons un exemple pratique : une application de suivi des quarts de travail. Nous devons calculer la durée du quart et déterminer s'il tombe en période nocturne. Nous utilisons LocalTime pour les heures de début et de fin, LocalDate pour la date et LocalDateTime pour calculer les quarts qui chevauchent minuit.

kotlin
data class Shift(
    val startTime: LocalTime,
    val endTime: LocalTime,
    val date: LocalDate
) {
    fun isOvernight(): Boolean = endTime.isBefore(startTime)

    fun durationInMinutes(): Long {
        val start = LocalDateTime.of(date, startTime)
        val end = LocalDateTime.of(
            if (isOvernight()) date.plusDays(1) else date,
            endTime
        )
        return Duration.between(start, end).toMinutes()
    }
}

Un deuxième exemple — calcul de l'âge d'un utilisateur. Nous utilisons LocalDate pour la date de naissance et la comparons à la date actuelle, en tenant compte du jour et du mois de naissance.

kotlin
fun calculateAge(birthDate: LocalDate): Int {
    val today = LocalDate.now()
    val period = Period.between(birthDate, today)
    return period.years
}

Un troisième exemple — travail avec les notifications. LocalDateTime est utilisé pour planifier des rappels. Nous vérifions si l'heure planifiée est arrivée.

kotlin
data class Reminder(
    val id: Long,
    val scheduledAt: LocalDateTime
) {
    fun isDue(): Boolean =
        LocalDateTime.now().isAfter(scheduledAt)
}

Compatibilité Android : niveau d'API et desugaring

La prise en charge intégrée de java.time est apparue sur Android à partir de l'API 26 (Android 8.0 Oreo). Pour les appareils avec des versions Android plus anciennes, vous devez utiliser le desugaring — un mécanisme qui ajoute la prise en charge des nouvelles API Java dans les versions antérieures.

Le desugaring dans Android Gradle Plugin est configuré via compileOptions dans build.gradle. Il suffit de définir isCoreLibraryDesugaringEnabled = true et d'ajouter la bibliothèque desugar_jdk_libs. Après cela, java.time devient disponible pour tous les niveaux d'API à partir de 14.

Pour les projets qui ne peuvent pas utiliser le desugaring (par exemple, les projets existants avec AGP inférieur à 4.0), il existe la bibliothèque ThreeTenABP — un backport de java.time. Elle fournit les mêmes classes (LocalDate, LocalTime, LocalDateTime), mais dans le paquetage org.threeten.bp.

groovy
@Suppress("UnstableApiUsage")
android {
    compileOptions {
        isCoreLibraryDesugaringEnabled = true
    }
}

dependencies {
    "coreLibraryDesugaring"("com.android.tools:desugar_jdk_libs:2.1.4")
}

Erreurs courantes et comment les éviter

La première erreur courante — utiliser LocalDateTime dans des systèmes distribués sans tenir compte des fuseaux horaires. Si le serveur est en Europe/Moscow et le client en Asia/Tokyo, LocalDateTime sera interprété différemment. Solution : utilisez Instant ou ZonedDateTime pour les données globales.

La deuxième erreur — une analyse incorrecte des chaînes. Par défaut, LocalDate.parse() attend le format ISO-8601 (yyyy-MM-dd). Si la chaîne est dans un autre format, vous devez passer explicitement un DateTimeFormatter. Vous devez également gérer DateTimeParseException pour que l'application ne plante pas en cas de saisie invalide.

La troisième erreur — ignorer la sécurité null. LocalDate, LocalTime et LocalDateTime sont des objets qui peuvent être null. En Kotlin, il est recommandé d'utiliser des types nullables avec des vérifications explicites ou l'opérateur Elvis. En Java — vérifiez null avant d'appeler des méthodes.

La quatrième erreur — confondre LocalDateTime et ZonedDateTime. LocalDateTime ne contient aucune information de fuseau horaire. Si vous devez transmettre un moment absolu dans le temps — utilisez des types zonaux. Si l'heure locale suffit — utilisez des types locaux.

Foire aux questions

Quelle est la différence entre LocalDate et Date en Java ?

Date stocke le nombre de millisecondes depuis 1970-01-01 UTC, tandis que LocalDate stocke l'année, le mois et le jour sans liaison à un fuseau horaire. Date est mutable et non thread-safe, LocalDate est immuable et thread-safe. Date est obsolète depuis Java 8.

Peut-on utiliser LocalDateTime dans une base de données ?

Oui, LocalDateTime se mappe bien sur le type SQL TIMESTAMP WITHOUT TIME ZONE. JPA et Room le prennent en charge via TypeConverter. Pour TIMESTAMP WITH TIME ZONE, utilisez ZonedDateTime ou OffsetDateTime.

Comment obtenir le nombre de jours entre deux dates ?

Utilisez ChronoUnit.DAYS.between(startDate, endDate). Cette méthode retourne un long — la différence en jours. Pour un calcul plus détaillé, utilisez Period.between(), qui retourne un Period avec les années, mois et jours.

Que faire si vous devez préserver la précision du temps jusqu'aux millisecondes ?

LocalTime prend en charge la précision à la nanoseconde (9 décimales). Si la précision à la milliseconde suffit, utilisez truncateTo(ChronoUnit.MILLIS) avant de sauvegarder. Cela évite les problèmes d'arrondi lors de la sérialisation.

Pourquoi LocalDate.now() retourne-t-il des dates différentes sur différents appareils ?

La méthode now() utilise l'horloge système et le fuseau horaire par défaut de l'appareil. Si les appareils sont dans des fuseaux horaires différents, la date peut différer. Pour un horodatage unifié, utilisez Instant.now(), qui retourne toujours l'heure UTC.

Résumé

  • LocalDate — une classe immuable pour la date sans heure ni fuseau horaire. Utilisée pour stocker les anniversaires, délais, dates d'événements.
  • LocalTime — une classe immuable pour l'heure de la journée avec précision à la nanoseconde. Convient pour stocker les heures d'ouverture, durées de processus.
  • LocalDateTime — combinaison de date et d'heure sans liaison à un fuseau horaire. Le type local le plus flexible, mais pas adapté aux systèmes distribués.
  • Les trois classes prennent en charge l'arithmétique, la comparaison, le formatage et l'analyse via une API unifiée basée sur DateTimeFormatter.
  • Sur Android, java.time est disponible via la prise en charge intégrée depuis l'API 26 ou via le desugaring pour les versions antérieures.
  • Pour les horodatages globaux et les données avec fuseau horaire, utilisez ZonedDateTime ou Instant au lieu des types locaux.
  • Lors de l'analyse de chaînes, passez toujours un DateTimeFormatter pour les formats non standard et gérez DateTimeParseException.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi