LocalDate, LocalTime și LocalDateTime: ce este, lucrul cu data

Autor: IT Sectr Publicat: 2026-07-13 Timp de citire: 12 min

LocalDate, LocalTime și LocalDateTime — clasele principale ale pachetului java.time, care oferă lucrul cu data și ora fără legătură cu fusul orar. Conform documentației Oracle (Java 17, 2024), aceste tipuri sunt proiectate ca immutable și thread-safe, ceea ce le face sigure pentru aplicații multi-thread. Au devenit disponibile pe Android prin desugaring începând cu API 26, iar pentru versiunile mai vechi — prin biblioteca ThreeTenABP.

Principalele puncte

  • LocalDate — clasă immutable pentru reprezentarea datei (an, lună, zi) fără oră și fus orar.
  • LocalTime — clasă immutable pentru reprezentarea orei (oră, minut, secundă, nanosecundă) fără dată și fus orar.
  • LocalDateTime — combinație între LocalDate și LocalTime, care stochează atât data, cât și ora fără legătură cu zona.
  • Toate trei clase acceptă operații aritmetice — adăugarea și scăderea zilelor, lunilor, orelor prin metodele plus și minus.
  • Pe Android aceste tipuri sunt disponibile prin desugaring (API 26+) sau biblioteca ThreeTenABP (API < 26).

Ce sunt LocalDate, LocalTime și LocalDateTime?

LocalDate — clasă care reprezintă data în format an-lună-zi fără informații despre oră și fus orar. Este folosită pentru stocarea unor date precum ziua de naștere, data evenimentului sau data expirării.

LocalDate stochează anul în intervalul de la -999999999 la +999999999, luna de la 1 la 12 și ziua lunii ținând cont de anii bisecți. Clasa este complet immutable — orice operație returnează un obiect nou.

LocalTime reprezintă ora din zi: ore, minute, secunde și nanosecunde. Precizia maximă — până la nanosecundă. LocalTime nu conține informații despre dată și fus orar, ceea ce îl face convenabil pentru stocarea orei de deschidere a magazinului sau a duratei unui proces.

LocalDateTime combină LocalDate și LocalTime într-un singur obiect. Este cel mai frecvent utilizat tip atunci când trebuie să stocăm atât data, cât și ora, dar legătura cu fusul orar nu este necesară. De exemplu, data și ora unui concert în format local.

Conform Oracle Java Documentation (2024), toate trei clase sunt proiectate pe baza ideilor din biblioteca Joda-Time, dar cu arhitectură îmbunătățită și integrare completă în biblioteca standard.

Cum este structurat pachetul java.time?

Pachetul java.time a apărut în Java 8 ca înlocuitor al claselor învechite Date, Calendar și SimpleDateFormat. Arhitectura sa este construită pe principiile obiectelor immutable și interfeței fluent.

Caracteristica cheie — toate clasele principale sunt value-based. Aceasta înseamnă că instanțele lor sunt comparate după valoare, nu după referință, și nu pot fi moștenite. Pentru compararea a două obiecte se folosește metoda equals, nu operatorul ==.

Pachetul este împărțit în mai multe categorii. Tipurile fără fus orar — LocalDate, LocalTime, LocalDateTime — sunt folosite pentru date și ore locale. Tipurile cu fus orar — ZonedDateTime, OffsetDateTime, OffsetTime — adaugă informații despre decalaj sau zonă. Tipurile instantanee — Instant — reprezintă un punct pe axa timpului în UTC.

Această divizare rezolvă problema caracteristică API-ului vechi: programatorul nu știa niciodată dacă obiectul Date conține informații despre fusul orar sau nu. În java.time fiecare tip declară explicit semantica sa.

LocalDate: lucrul cu data

Clasa LocalDate oferă numeroase metode pentru crearea, citirea și modificarea datei. Data curentă poate fi obținută prin metoda statică now(). O dată specifică — prin metoda of(int year, int month, int dayOfMonth).

Pentru citirea componentelor datei se folosesc gettere: getYear(), getMonthValue(), getDayOfMonth(), getDayOfWeek(), getDayOfYear(). Metoda getMonth() returnează enum-ul Month, iar getDayOfWeek() — enum-ul DayOfWeek.

LocalDate acceptă verificarea datelor. Metodele isBefore(), isAfter() și isEqual() permit compararea datelor. Metoda isLeapYear() verifică dacă anul este bisect. Metoda lengthOfMonth() returnează numărul de zile în lună, iar lengthOfYear() — în an.

Pentru modificare se folosesc metodele withYear(), withMonth(), withDayOfMonth(), care returnează un obiect nou cu componenta modificată. Metodele plusDays(), minusMonths() și similare efectuează aritmetica datei.

LocalTime: lucrul cu ora

LocalTime reprezintă ora din zi cu precizie până la nanosecundă. Formatul standard — ISO-8601 (HH:mm:ss.nnnnnnnnn). Valoarea minimă — 00:00, maximă — 23:59:59.999999999.

Se poate crea un obiect LocalTime prin now() pentru ora curentă, of(int hour, int minute), of(int hour, int minute, int second) sau of(int hour, int minute, int second, int nanoOfSecond). Metoda parse(CharSequence text) parsează un șir în format ISO-8601.

Getterele includ getHour(), getMinute(), getSecond(), getNano(). Metoda toSecondOfDay() returnează numărul de secunde de la începutul zilei, iar toNanoOfDay() — nanosecundele. Acest lucru este convenabil pentru calcule de durată în cadrul unei singure zile.

LocalTime acceptă aceleași operații de comparare și modificare ca și LocalDate: plusHours(), minusMinutes(), withHour(), withMinute(). Metodele isBefore() și isAfter() funcționează ținând cont de natura ciclică a timpului în cadrul zilei.

LocalDateTime: combinația datei și orei

LocalDateTime combină capacitățile LocalDate și LocalTime într-o singură clasă. Stochează atât data, cât și ora, dar fără fus orar. Este cel mai flexibil tip dintre cele locale, dar necesită prudență atunci când este folosit în sisteme distribuite.

Se poate crea LocalDateTime prin metodele statice now(), of(LocalDate date, LocalTime time), of(int year, Month month, int dayOfMonth, int hour, int minute) și supraîncărcările lor. De asemenea, se pot combina LocalDate și LocalTime prin metoda atTime().

LocalDateTime oferă acces la toate câmpurile de dată și oră prin gettere corespunzătoare: toLocalDate() și toLocalTime() returnează componente separate. Metoda truncatedTo(TemporalUnit unit) permite rotunjirea orei la o precizie dată — de exemplu, la minute.

Pentru conversia în fusul orar se folosește metoda atZone(ZoneId zone), care returnează ZonedDateTime. Acesta este singurul mod de a adăuga un fus orar la LocalDateTime.

Cum să creăm obiecte de dată și oră?

Toate trei clase folosesc un model unitar de creare prin metode statice de fabrică. Constructorii claselor sunt declarați private — nu se poate crea un obiect direct prin new.

Principalele moduri de creare:

  • now() — data/ora curentă din ceasul sistemului
  • of(...) — din componente (an, lună, zi etc.)
  • parse(String) — dintr-un șir în format ISO-8601
  • from(TemporalAccessor) — dintr-un alt obiect temporal

Metoda of are numeroase supraîncărcări. Pentru LocalDate sunt necesari anul, luna și ziua. Pentru LocalTime — orele și minutele (opțional secundele și nanosecundele). Pentru LocalDateTime — anul, luna, ziua, orele, minutele. Luna poate fi transmisă ca int (1-12) sau ca enum 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)

Conversia între tipuri

Clasele java.time sunt proiectate pentru o conversie ușoară între ele. LocalDate poate fi convertit în LocalDateTime prin metoda atTime(LocalTime) sau atStartOfDay(). LocalTime — prin atDate(LocalDate).

LocalDateTime poate fi convertit înapoi în LocalDate prin toLocalDate() și în LocalTime prin toLocalTime(). Pentru conversia în ZonedDateTime se folosește metoda atZone(ZoneId).

Conversia în java.util.Date (pentru compatibilitate cu codul vechi) necesită un pas intermediar prin Instant și fusul orar. Conform Baeldung (2024), această operație se realizează prin 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"))

Formatare și parsare

Pentru formatare și parsare se folosește clasa DateTimeFormatter. Aceasta oferă formate predefinite prin constante (ISO_LOCAL_DATE, ISO_LOCAL_TIME, ISO_LOCAL_DATE_TIME) și posibilitatea de a crea propriile formate prin șabloane.

Șabloanele de formatare folosesc simboluri: yyyy — an, MM — lună (două cifre), dd — zi, HH — oră (0-23), mm — minut, ss — secundă. Metoda format() se apelează pe obiectul dată-oră sau prin DateTimeFormatter.

DateTimeFormatter acceptă și localizarea prin metodele statice ofLocalizedDate(FormatStyle), ofLocalizedTime(FormatStyle) și ofLocalizedDateTime(FormatStyle). Sunt disponibile stilurile SHORT, MEDIUM, LONG și 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")
)

Compararea obiectelor dată-oră

Toate trei clase implementează interfața Comparable, ceea ce permite compararea lor naturală. Metoda compareTo() returnează un număr negativ, zero sau pozitiv în funcție de ordine. Metodele isBefore(), isAfter() și isEqual() returnează boolean.

Pentru LocalDate compararea se face cronologic — data mai devreme este mai mică. Pentru LocalTime — după ora zilei. Pentru LocalDateTime — mai întâi după dată, apoi după oră. Toate comparațiile iau în considerare corect anii bisecți și numărul de zile în luni.

O diferență importantă față de API-ul vechi: equals() pentru LocalDate, LocalTime și LocalDateTime compară valorile, nu referințele. Aceasta înseamnă că două obiecte cu aceleași câmpuri vor fi egale, chiar dacă sunt instanțe diferite.

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

if (d1.isBefore(d2)) {
    Log.d("Data", "d1 este înainte de d2")
}

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

Aritmetica datei și orei

Toate trei clase acceptă operații aritmetice prin metodele plus și minus. Pentru LocalDate sunt disponibile plusDays(), plusWeeks(), plusMonths(), plusYears() și metode minus similare. LocalTime acceptă plusHours(), plusMinutes(), plusSeconds(), plusNanos().

LocalDateTime moștenește toate operațiile aritmetice ale ambelor tipuri. Caracteristica LocalDate: la adăugarea unei luni, rezultatele gestionează corect lungimile diferite ale lunilor. De exemplu, 31 ianuarie + 1 lună = 28 (29 în an bisect) februarie.

Pentru operații mai complexe există clasele Period (pentru date) și Duration (pentru timp). Metodele plus(TemporalAmount) și minus(TemporalAmount) acceptă aceste obiecte.

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)

Exemple de cod în Kotlin

Să luăm un exemplu practic: o aplicație pentru evidența schimburilor de lucru. Trebuie să calculăm durata schimbului și să determinăm dacă acesta cade în timpul nopții. Folosim LocalTime pentru ora de început și sfârșit, LocalDate pentru dată și LocalDateTime pentru calculele schimburilor care trec de miezul nopții.

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()
    }
}

Al doilea exemplu — calculul vârstei utilizatorului. Folosim LocalDate pentru data nașterii și o comparăm cu data curentă, ținând cont de ziua și luna nașterii.

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

Al treilea exemplu — lucrul cu notificări. LocalDateTime este folosit pentru planificarea mementourilor. Verificăm dacă ora planificată a sosit.

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

Suport în Android: nivel API și desugaring

Suportul încorporat pentru java.time a apărut pe Android începând cu API 26 (Android 8.0 Oreo). Pentru dispozitivele cu versiuni mai vechi de Android este necesar să folosim desugaring — un mecanism care adaugă suport pentru noile API-uri Java în versiunile anterioare.

Desugaring în Android Gradle Plugin se configurează prin compileOptions în build.gradle. Este suficient să setăm isCoreLibraryDesugaringEnabled = true și să adăugăm biblioteca desugar_jdk_libs. După aceasta, java.time devine disponibil pentru toate nivelurile API începând cu 14.

Pentru proiectele care nu pot folosi desugaring (de exemplu, proiecte legacy pe AGP sub 4.0), există biblioteca ThreeTenABP — backport-ul java.time. Aceasta oferă aceleași clase (LocalDate, LocalTime, LocalDateTime), dar în pachetul org.threeten.bp.

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

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

Erori tipice și cum să le evităm

Prima eroare frecventă — utilizarea LocalDateTime în sisteme distribuite fără a ține cont de fusul orar. Dacă serverul se află în Europe/Moscow, iar clientul în Asia/Tokyo, LocalDateTime va fi interpretat diferit. Soluție: folosiți Instant sau ZonedDateTime pentru date globale.

A doua eroare — parsarea incorectă a șirurilor. Implicit, LocalDate.parse() așteaptă formatul ISO-8601 (yyyy-MM-dd). Dacă șirul este într-un alt format, trebuie să transmiteți DateTimeFormatter explicit. De asemenea, trebuie să gestionați DateTimeParseException pentru ca aplicația să nu pice la o intrare incorectă.

A treia eroare — ignorarea siguranței la null. LocalDate, LocalTime și LocalDateTime sunt obiecte care pot fi null. în Kotlin se recomandă utilizarea tipurilor nullable cu verificare explicită sau operatorul Elvis. în Java — verificați null înainte de a apela metode.

A patra eroare — confuzia între LocalDateTime și ZonedDateTime. LocalDateTime nu conține nicio informație despre fusul orar. Dacă trebuie să transmiteți un moment absolut de timp — folosiți tipurile zonale. Dacă timpul local este suficient — tipurile locale.

Întrebări frecvente

Care este diferența între LocalDate și Date în Java?

Date stochează numărul de milisecunde de la 1970-01-01 UTC, iar LocalDate stochează anul, luna și ziua fără legătură cu fusul orar. Date este mutabil și nu este thread-safe, LocalDate — imutable și thread-safe. Date este învechit începând cu Java 8.

Pot folosi LocalDateTime într-o bază de date?

Da, LocalDateTime se mapează bine pe tipul SQL TIMESTAMP WITHOUT TIME ZONE. JPA și Room îl acceptă prin TypeConverter. Pentru TIMESTAMP WITH TIME ZONE folosiți ZonedDateTime sau OffsetDateTime.

Cum obțin numărul de zile între două date?

Folosiți ChronoUnit.DAYS.between(startDate, endDate). Această metodă returnează long — diferența în zile. Pentru calcule mai detaliate, folosiți Period.between(), care returnează un Period cu ani, luni și zile.

Ce fac dacă trebuie să păstrez precizia timpului până la milisecunde?

LocalTime acceptă precizie până la nanosecunde (9 zecimale). Dacă aveți nevoie de precizie până la milisecunde, folosiți truncateTo(ChronoUnit.MILLIS) înainte de salvare. Aceasta previne problemele de rotunjire la serializare.

De ce LocalDate.now() returnează date diferite pe dispozitive diferite?

Metoda now() folosește ceasul de sistem al dispozitivului și fusul orar implicit. Dacă dispozitivele se află în fusuri orare diferite, data poate diferi. Pentru un marcaj temporal unitar folosiți Instant.now(), care returnează întotdeauna ora în UTC.

Concluzii

  • LocalDate — clasă immutable pentru dată fără oră și fus orar. Folosită pentru stocarea zilelor de naștere, termenelor, datelor evenimentelor.
  • LocalTime — clasă immutable pentru ora zilei cu precizie până la nanosecundă. Potrivită pentru stocarea orelor de deschidere, duratelor proceselor.
  • LocalDateTime — combinația datei și orei fără legătură cu zona. Cel mai flexibil tip local, dar nepotrivit pentru sisteme distribuite.
  • Toate trei clase acceptă aritmetică, comparare, formatare și parsare printr-un API unitar bazat pe DateTimeFormatter.
  • Pe Android, java.time este disponibil prin suport încorporat de la API 26 sau prin desugaring pentru versiunile mai vechi.
  • Pentru marcajele temporale globale și datele cu fus orar folosiți ZonedDateTime sau Instant în locul tipurilor locale.
  • La parsarea șirurilor, transmiteți întotdeauna DateTimeFormatter pentru formatele nestandard și gestionați DateTimeParseException.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și