LocalDate, LocalTime en LocalDateTime: wat is het, werken met datum

Auteur: IT Sectr Gepubliceerd: 2026-07-13 Leestijd: 12 min

LocalDate, LocalTime en LocalDateTime — de belangrijkste klassen van het java.time-pakket die werken met datum en tijd mogelijk maken zonder koppeling aan een tijdzone. Volgens Oracle-documentatie (Java 17, 2024) zijn deze typen ontworpen als immutable en thread-safe, wat ze veilig maakt voor multi-threaded applicaties. Ze zijn beschikbaar geworden op Android via desugaring vanaf API 26, en voor oudere versies via de ThreeTenABP-bibliotheek.

Belangrijkste punten

  • LocalDate — immutable klasse voor weergave van datum (jaar, maand, dag) zonder tijd en tijdzone.
  • LocalTime — immutable klasse voor weergave van tijd (uur, minuut, seconde, nanoseconde) zonder datum en tijdzone.
  • LocalDateTime — combinatie van LocalDate en LocalTime, die zowel datum als tijd opslaat zonder koppeling aan een zone.
  • Alle drie klassen ondersteunen rekenkundige bewerkingen — optellen en aftrekken van dagen, maanden, uren via plus en minus methoden.
  • Op Android zijn deze typen beschikbaar via desugaring (API 26+) of de ThreeTenABP-bibliotheek (API < 26).

Wat zijn LocalDate, LocalTime en LocalDateTime?

LocalDate — klasse die datum weergeeft in jaar-maand-dag formaat zonder informatie over tijd en tijdzone. Het wordt gebruikt voor het opslaan van gegevens zoals geboortedatum, datum van een evenement of vervaldatum.

LocalDate slaat het jaar op in het bereik van -999999999 tot +999999999, maand van 1 tot 12 en dag van de maand rekening houdend met schrikkeljaren. De klasse is volledig immutable — elke bewerking retourneert een nieuw object.

LocalTime vertegenwoordigt de tijd van de dag: uren, minuten, seconden en nanoseconden. Maximale precisie — tot nanoseconde. LocalTime bevat geen informatie over datum en tijdzone, wat het handig maakt voor het opslaan van openingstijden van winkels of procesduur.

LocalDateTime combineert LocalDate en LocalTime in één object. Het is het meest gebruikte type wanneer zowel datum als tijd moeten worden opgeslagen, maar koppeling aan een tijdzone niet vereist is. Bijvoorbeeld datum en tijd van een concert in lokaal formaat.

Volgens Oracle Java Documentation (2024) zijn alle drie klassen ontworpen op basis van ideeën uit de Joda-Time-bibliotheek, maar met verbeterde architectuur en volledige integratie in de standaardbibliotheek.

Hoe is het java.time-pakket gestructureerd?

Het java.time-pakket verscheen in Java 8 als vervanging voor de verouderde klassen Date, Calendar en SimpleDateFormat. De architectuur is gebaseerd op de principes van immutable objecten en een fluent-interface.

Het belangrijkste kenmerk — alle hoofdklassen zijn value-based. Dit betekent dat hun instanties worden vergeleken op waarde, niet op referentie, en ze kunnen niet worden overgeërfd. Voor het vergelijken van twee objecten wordt de methode equals gebruikt, niet de operator ==.

Het pakket is verdeeld in verschillende categorieën. Typen zonder tijdzone — LocalDate, LocalTime, LocalDateTime — worden gebruikt voor lokale data en tijden. Typen met tijdzone — ZonedDateTime, OffsetDateTime, OffsetTime — voegen informatie over afwijking of zone toe. Momentane typen — Instant — vertegenwoordigen een punt op de tijdlijn in UTC.

Deze verdeling lost het probleem op dat kenmerkend was voor de oude API: de programmeur wist nooit of het Date-object informatie over de tijdzone bevatte of niet. In java.time declareert elk type expliciet zijn semantiek.

LocalDate: werken met datum

De klasse LocalDate biedt vele methoden voor het maken, lezen en wijzigen van een datum. De huidige datum kan worden verkregen via de statische methode now(). Een specifieke datum — via de methode of(int year, int month, int dayOfMonth).

Voor het lezen van datumcomponenten worden getters gebruikt: getYear(), getMonthValue(), getDayOfMonth(), getDayOfWeek(), getDayOfYear(). De methode getMonth() retourneert enum Month, en getDayOfWeek() — enum DayOfWeek.

LocalDate ondersteunt het controleren van data. De methoden isBefore(), isAfter() en isEqual() maken het mogelijk data te vergelijken. De methode isLeapYear() controleert of het jaar een schrikkeljaar is. De methode lengthOfMonth() retourneert het aantal dagen in de maand, en lengthOfYear() — in het jaar.

Voor wijziging worden de methoden withYear(), withMonth(), withDayOfMonth() gebruikt, die een nieuw object retourneren met de gewijzigde component. De methoden plusDays(), minusMonths() en soortgelijke voeren datum rekenkunde uit.

LocalTime: werken met tijd

LocalTime vertegenwoordigt de tijd van de dag met precisie tot nanoseconde. Standaard formaat — ISO-8601 (HH:mm:ss.nnnnnnnnn). Minimale waarde — 00:00, maximale — 23:59:59.999999999.

Een LocalTime-object kan worden gemaakt via now() voor de huidige tijd, of(int hour, int minute), of(int hour, int minute, int second) of of(int hour, int minute, int second, int nanoOfSecond). De methode parse(CharSequence text) parseert een string in ISO-8601 formaat.

Getters omvatten getHour(), getMinute(), getSecond(), getNano(). De methode toSecondOfDay() retourneert het aantal seconden sinds het begin van de dag, en toNanoOfDay() — nanoseconden. Dit is handig voor duurberekeningen binnen één dag.

LocalTime ondersteunt dezelfde vergelijkings- en wijzigingsbewerkingen als LocalDate: plusHours(), minusMinutes(), withHour(), withMinute(). De methoden isBefore() en isAfter() werken rekening houdend met de cycliciteit van tijd binnen een dag.

LocalDateTime: combinatie van datum en tijd

LocalDateTime combineert de mogelijkheden van LocalDate en LocalTime in één klasse. Het slaat zowel datum als tijd op, maar zonder tijdzone. Het is het meest flexibele lokale type, maar vereist voorzichtigheid bij gebruik in gedistribueerde systemen.

LocalDateTime kan worden gemaakt via de statische methoden now(), of(LocalDate date, LocalTime time), of(int year, Month month, int dayOfMonth, int hour, int minute) en hun overbelastingen. Ook kunnen LocalDate en LocalTime worden gecombineerd via de methode atTime().

LocalDateTime biedt toegang tot alle datum- en tijdvelden via de bijbehorende getters: toLocalDate() en toLocalTime() retourneren afzonderlijke componenten. De methode truncatedTo(TemporalUnit unit) maakt het mogelijk de tijd af te ronden op een bepaalde precisie — bijvoorbeeld tot minuten.

Voor conversie naar een tijdzone wordt de methode atZone(ZoneId zone) gebruikt, die ZonedDateTime retourneert. Dit is de enige manier om een tijdzone aan LocalDateTime toe te voegen.

Hoe maak je datum- en tijdobjecten?

Alle drie klassen gebruiken een uniform creatiepatroon via statische fabrieksmethoden. De constructors van de klassen zijn gedeclareerd als private — een object kan niet direct via new worden gemaakt.

Belangrijkste manieren van maken:

  • now() — huidige datum/tijd van de systeemklok
  • of(...) — uit componenten (jaar, maand, dag, enz.)
  • parse(String) — uit een string in ISO-8601 formaat
  • from(TemporalAccessor) — uit een ander temporeel object

De methode of heeft vele overbelastingen. Voor LocalDate zijn jaar, maand en dag nodig. Voor LocalTime — uren en minuten (optioneel seconden en nanoseconden). Voor LocalDateTime — jaar, maand, dag, uren, minuten. Maand kan worden doorgegeven als int (1-12) of als 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)

Conversie tussen typen

De java.time-klassen zijn ontworpen voor eenvoudige conversie tussen elkaar. LocalDate kan worden geconverteerd naar LocalDateTime via de methode atTime(LocalTime) of atStartOfDay(). LocalTime — via atDate(LocalDate).

LocalDateTime kan worden teruggeconverteerd naar LocalDate via toLocalDate() en naar LocalTime via toLocalTime(). Voor conversie naar ZonedDateTime wordt de methode atZone(ZoneId) gebruikt.

Conversie naar java.util.Date (voor compatibiliteit met oude code) vereist een tussenstap via Instant en tijdzone. Volgens Baeldung (2024) wordt deze bewerking uitgevoerd 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"))

Formatteren en parsen

Voor formatteren en parsen wordt de klasse DateTimeFormatter gebruikt. Deze biedt vooraf gedefinieerde formaten via constanten (ISO_LOCAL_DATE, ISO_LOCAL_TIME, ISO_LOCAL_DATE_TIME) en de mogelijkheid om eigen formaten te maken via patronen.

Formatteringspatronen gebruiken symbolen: yyyy — jaar, MM — maand (twee cijfers), dd — dag, HH — uur (0-23), mm — minuut, ss — seconde. De methode format() wordt aangeroepen op het datum-tijd object of via DateTimeFormatter.

DateTimeFormatter ondersteunt ook lokalisatie via de statische methoden ofLocalizedDate(FormatStyle), ofLocalizedTime(FormatStyle) en ofLocalizedDateTime(FormatStyle). De stijlen SHORT, MEDIUM, LONG en FULL zijn beschikbaar.

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

Vergelijken van datum-tijd objecten

Alle drie klassen implementeren de interface Comparable, wat natuurlijke vergelijking mogelijk maakt. De methode compareTo() retourneert een negatief getal, nul of positief afhankelijk van de volgorde. De methoden isBefore(), isAfter() en isEqual() retourneren boolean.

Voor LocalDate wordt vergelijking chronologisch gedaan — een eerdere datum is kleiner. Voor LocalTime — volgens de tijd van de dag. Voor LocalDateTime — eerst op datum, daarna op tijd. Alle vergelijkingen houden correct rekening met schrikkeljaren en het aantal dagen in maanden.

Een belangrijk verschil met de oude API: equals() voor LocalDate, LocalTime en LocalDateTime vergelijkt waarden, niet referenties. Dit betekent dat twee objecten met dezelfde velden gelijk zullen zijn, zelfs als het verschillende instanties zijn.

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

if (d1.isBefore(d2)) {
    Log.d("Datum", "d1 is vóór d2")
}

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

Datum- en tijdaritmetica

Alle drie klassen ondersteunen rekenkundige bewerkingen via de methoden plus en minus. Voor LocalDate zijn plusDays(), plusWeeks(), plusMonths(), plusYears() en vergelijkbare minus-methoden beschikbaar. LocalTime ondersteunt plusHours(), plusMinutes(), plusSeconds(), plusNanos().

LocalDateTime erft alle rekenkundige bewerkingen van beide typen. Kenmerk van LocalDate: bij het toevoegen van een maand worden de resultaten correct afgestemd op de verschillende lengtes van maanden. Bijvoorbeeld 31 januari + 1 maand = 28 (29 in een schrikkeljaar) februari.

Voor complexere bewerkingen bestaan de klassen Period (voor data) en Duration (voor tijd). De methoden plus(TemporalAmount) en minus(TemporalAmount) accepteren deze objecten.

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)

Kotlin codevoorbeelden

Laten we een praktisch voorbeeld bekijken: een applicatie voor het registreren van werkploegen. We moeten de duur van een ploeg berekenen en bepalen of deze in de nachtelijke uren valt. We gebruiken LocalTime voor begin- en eindtijd, LocalDate voor de datum en LocalDateTime voor berekeningen van ploegen die middernacht overschrijden.

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

Tweede voorbeeld — berekening van de leeftijd van een gebruiker. We gebruiken LocalDate voor de geboortedatum en vergelijken deze met de huidige datum, rekening houdend met de dag en maand van geboorte.

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

Derde voorbeeld — werken met meldingen. LocalDateTime wordt gebruikt voor het plannen van herinneringen. We controleren of de geplande tijd is aangebroken.

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

Ondersteuning in Android: API-niveau en desugaring

Ingebouwde ondersteuning voor java.time verscheen op Android vanaf API 26 (Android 8.0 Oreo). Voor apparaten met oudere versies van Android moet desugaring worden gebruikt — een mechanisme dat ondersteuning voor nieuwe Java API's toevoegt aan eerdere versies.

Desugaring in Android Gradle Plugin wordt geconfigureerd via compileOptions in build.gradle. Het is voldoende om isCoreLibraryDesugaringEnabled = true in te stellen en de desugar_jdk_libs bibliotheek toe te voegen. Hierna wordt java.time beschikbaar voor alle API-niveaus vanaf 14.

Voor projecten die geen desugaring kunnen gebruiken (bijv. legacy-projecten op AGP lager dan 4.0), bestaat de bibliotheek ThreeTenABP — een backport van java.time. Deze biedt dezelfde klassen (LocalDate, LocalTime, LocalDateTime), maar in het pakket org.threeten.bp.

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

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

Veelvoorkomende fouten en hoe ze te vermijden

De eerste veelvoorkomende fout — het gebruik van LocalDateTime in gedistribueerde systemen zonder rekening te houden met de tijdzone. Als de server zich in Europe/Moscow bevindt en de client in Asia/Tokyo, zal LocalDateTime anders worden geïnterpreteerd. Oplossing: gebruik Instant of ZonedDateTime voor globale gegevens.

De tweede fout — onjuist parsen van strings. Standaard verwacht LocalDate.parse() het formaat ISO-8601 (yyyy-MM-dd). Als de string in een ander formaat is, moet DateTimeFormatter expliciet worden doorgegeven. Ook moet DateTimeParseException worden afgehandeld om te voorkomen dat de applicatie crasht bij ongeldige invoer.

De derde fout — het negeren van null-veiligheid. LocalDate, LocalTime en LocalDateTime zijn objecten die null kunnen zijn. In Kotlin wordt het gebruik van nullable types met expliciete controle of de Elvis-operator aanbevolen. In Java — controleer op null voordat methoden worden aangeroepen.

De vierde fout — verwarring tussen LocalDateTime en ZonedDateTime. LocalDateTime bevat geen informatie over de tijdzone. Als je een absoluut tijdstip moet doorgeven — gebruik dan zonale typen. Als lokale tijd voldoende is — lokale typen.

Veelgestelde vragen

Wat is het verschil tussen LocalDate en Date in Java?

Date slaat het aantal milliseconden op sinds 1970-01-01 UTC, terwijl LocalDate jaar, maand en dag opslaat zonder koppeling aan een tijdzone. Date is mutabel en niet thread-safe, LocalDate — immutable en thread-safe. Date is verouderd sinds Java 8.

Kan ik LocalDateTime gebruiken in een database?

Ja, LocalDateTime wordt goed gemapt op het SQL-type TIMESTAMP WITHOUT TIME ZONE. JPA en Room ondersteunen het via TypeConverter. Gebruik voor TIMESTAMP WITH TIME ZONE ZonedDateTime of OffsetDateTime.

Hoe krijg ik het aantal dagen tussen twee data?

Gebruik ChronoUnit.DAYS.between(startDate, endDate). Deze methode retourneert long — het verschil in dagen. Voor meer gedetailleerde berekening gebruikt u Period.between(), die een Period retourneert met jaren, maanden en dagen.

Wat moet ik doen als ik tijdsnauwkeurigheid tot milliseconden moet behouden?

LocalTime ondersteunt precisie tot nanoseconden (9 decimalen). Als precisie tot milliseconden nodig is, gebruik dan truncateTo(ChronoUnit.MILLIS) vóór het opslaan. Dit voorkomt afrondingsproblemen bij serialisatie.

Waarom retourneert LocalDate.now() verschillende data op verschillende apparaten?

De methode now() gebruikt de systeemklok van het apparaat en de standaard tijdzone. Als apparaten zich in verschillende tijdzones bevinden, kan de datum verschillen. Gebruik voor een uniforme tijdstempel Instant.now(), die altijd de tijd in UTC retourneert.

Samenvatting

  • LocalDate — immutable klasse voor datum zonder tijd en tijdzone. Gebruikt voor het opslaan van geboortedata, deadlines, evenementdata.
  • LocalTime — immutable klasse voor tijd van de dag met precisie tot nanoseconde. Geschikt voor het opslaan van openingstijden, procesduren.
  • LocalDateTime — combinatie van datum en tijd zonder zonekoppeling. Meest flexibele lokale type, maar niet geschikt voor gedistribueerde systemen.
  • Alle drie klassen ondersteunen rekenkunde, vergelijking, formattering en parsing via een uniforme API gebaseerd op DateTimeFormatter.
  • Op Android is java.time beschikbaar via ingebouwde ondersteuning vanaf API 26 of via desugaring voor oudere versies.
  • Gebruik voor wereldwijde tijdstempels en gegevens met tijdzones ZonedDateTime of Instant in plaats van lokale typen.
  • Geef bij het parsen van strings altijd DateTimeFormatter mee voor niet-standaard formaten en handel DateTimeParseException af.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook