LocalDate, LocalTime a LocalDateTime: co to je, práce s datem

Autor: IT Sectr Publikováno: 2026-07-13 Doba čtení: 12 min

LocalDate, LocalTime a LocalDateTime — hlavní třídy balíčku java.time, které poskytují práci s datem a časem bez vazby na časové pásmo. Podle dokumentace Oracle (Java 17, 2024) jsou tyto typy navrženy jako immutable a thread-safe, což je činí bezpečnými pro vícevláknové aplikace. Na Androidu se staly dostupnými prostřednictvím desugaring od API 26 a pro starší verze prostřednictvím knihovny ThreeTenABP.

Hlavní body

  • LocalDate — immutable třída pro reprezentaci data (rok, měsíc, den) bez času a časového pásma.
  • LocalTime — immutable třída pro reprezentaci času (hodina, minuta, sekunda, nanosekunda) bez data a časového pásma.
  • LocalDateTime — kombinace LocalDate a LocalTime, ukládající datum i čas bez vazby na zónu.
  • Všechny tři třídy podporují aritmetické operace — přidávání a odebírání dnů, měsíců, hodin pomocí metod plus a minus.
  • Na Androidu jsou tyto typy dostupné prostřednictvím desugaring (API 26+) nebo knihovny ThreeTenABP (API < 26).

Co jsou LocalDate, LocalTime a LocalDateTime?

LocalDate — třída představující datum ve formátu rok-měsíc-den bez informací o čase a časovém pásmu. Používá se pro ukládání údajů jako datum narození, datum události nebo datum vypršení.

LocalDate ukládá rok v rozsahu od -999999999 do +999999999, měsíc od 1 do 12 a den v měsíci s přihlédnutím k přestupným rokům. Třída je zcela immutable — každá operace vrací nový objekt.

LocalTime představuje denní dobu: hodiny, minuty, sekundy a nanosekundy. Maximální přesnost — až do nanosekundy. LocalTime neobsahuje informace o datu a časovém pásmu, což jej činí vhodným pro ukládání otevírací doby obchodu nebo doby trvání procesu.

LocalDateTime kombinuje LocalDate a LocalTime do jednoho objektu. Je to nejčastěji používaný typ, když potřebujeme ukládat datum i čas, ale vazba na časové pásmo není vyžadována. Například datum a čas koncertu v místním formátu.

Podle Oracle Java Documentation (2024) jsou všechny tři třídy navrženy na základě nápadů z knihovny Joda-Time, ale s vylepšenou architekturou a úplnou integrací do standardní knihovny.

Jak je balíček java.time strukturován?

Balíček java.time se objevil v Java 8 jako náhrada za zastaralé třídy Date, Calendar a SimpleDateFormat. Jeho architektura je postavena na principech immutable objektů a fluent rozhraní.

Klíčová vlastnost — všechny hlavní třídy jsou value-based. To znamená, že jejich instance jsou porovnávány podle hodnoty, nikoli podle reference, a nelze je dědit. Pro porovnání dvou objektů se používá metoda equals, nikoli operátor ==.

Balíček je rozdělen do několika kategorií. Typy bez časového pásma — LocalDate, LocalTime, LocalDateTime — se používají pro místní data a časy. Typy s časovým pásmem — ZonedDateTime, OffsetDateTime, OffsetTime — přidávají informace o odchylce nebo zóně. Okamžité typy — Instant — představují bod na časové ose v UTC.

Toto rozdělení řeší problém charakteristický pro staré API: programátor nikdy nevěděl, zda objekt Date obsahuje informace o časovém pásmu nebo ne. V java.time každý typ explicitně deklaruje svou sémantiku.

LocalDate: práce s datem

Třída LocalDate poskytuje mnoho metod pro vytváření, čtení a úpravu data. Aktuální datum lze získat pomocí statické metody now(). Konkrétní datum — pomocí metody of(int year, int month, int dayOfMonth).

Pro čtení komponent data se používají gettery: getYear(), getMonthValue(), getDayOfMonth(), getDayOfWeek(), getDayOfYear(). Metoda getMonth() vrací enum Month, getDayOfWeek() — enum DayOfWeek.

LocalDate podporuje kontrolu dat. Metody isBefore(), isAfter() a isEqual() umožňují porovnávat data. Metoda isLeapYear() kontroluje, zda je rok přestupný. Metoda lengthOfMonth() vrací počet dní v měsíci a lengthOfYear() — v roce.

Pro úpravu se používají metody withYear(), withMonth(), withDayOfMonth(), které vracejí nový objekt se změněnou komponentou. Metody plusDays(), minusMonths() a podobné provádějí aritmetiku data.

LocalTime: práce s časem

LocalTime představuje denní dobu s přesností až na nanosekundu. Standardní formát — ISO-8601 (HH:mm:ss.nnnnnnnnn). Minimální hodnota — 00:00, maximální — 23:59:59.999999999.

Objekt LocalTime lze vytvořit pomocí now() pro aktuální čas, of(int hour, int minute), of(int hour, int minute, int second) nebo of(int hour, int minute, int second, int nanoOfSecond). Metoda parse(CharSequence text) parsuje řetězec ve formátu ISO-8601.

Gettery zahrnují getHour(), getMinute(), getSecond(), getNano(). Metoda toSecondOfDay() vrací počet sekund od začátku dne a toNanoOfDay() — nanosekund. To je vhodné pro výpočet trvání během jednoho dne.

LocalTime podporuje stejné operace porovnání a úpravy jako LocalDate: plusHours(), minusMinutes(), withHour(), withMinute(). Metody isBefore() a isAfter() pracují s přihlédnutím k cykličnosti času během dne.

LocalDateTime: kombinace data a času

LocalDateTime kombinuje možnosti LocalDate a LocalTime v jedné třídě. Ukládá datum i čas, ale bez časového pásma. Je to nejflexibilnější lokální typ, ale vyžaduje opatrnost při použití v distribuovaných systémech.

LocalDateTime lze vytvořit pomocí statických metod now(), of(LocalDate date, LocalTime time), of(int year, Month month, int dayOfMonth, int hour, int minute) a jejich přetížení. Lze také kombinovat LocalDate a LocalTime pomocí metody atTime().

LocalDateTime poskytuje přístup ke všem polím data a času prostřednictvím odpovídajících getterů: toLocalDate() a toLocalTime() vracejí jednotlivé komponenty. Metoda truncatedTo(TemporalUnit unit) umožňuje zaokrouhlení času na danou přesnost — například na minuty.

Pro převod do časového pásma se používá metoda atZone(ZoneId zone), která vrací ZonedDateTime. To je jediný způsob, jak přidat časové pásmo k LocalDateTime.

Jak vytvářet objekty data a času?

Všechny tři třídy používají jednotný vzor vytváření pomocí statických továrních metod. Konstruktory tříd jsou deklarovány jako private — objekt nelze vytvořit přímo pomocí new.

Hlavní způsoby vytváření:

  • now() — aktuální datum/čas ze systémových hodin
  • of(...) — z komponent (rok, měsíc, den atd.)
  • parse(String) — z řetězce ve formátu ISO-8601
  • from(TemporalAccessor) — z jiného temporálního objektu

Metoda of má mnoho přetížení. Pro LocalDate jsou potřebné rok, měsíc a den. Pro LocalTime — hodiny a minuty (volitelně sekundy a nanosekundy). Pro LocalDateTime — rok, měsíc, den, hodiny, minuty. Měsíc lze předat jako int (1-12) nebo jako 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)

Převod mezi typy

Třídy java.time jsou navrženy pro snadný převod mezi sebou. LocalDate lze převést na LocalDateTime pomocí metody atTime(LocalTime) nebo atStartOfDay(). LocalTime — pomocí atDate(LocalDate).

LocalDateTime lze převést zpět na LocalDate pomocí toLocalDate() a na LocalTime pomocí toLocalTime(). Pro převod na ZonedDateTime se používá metoda atZone(ZoneId).

Převod na java.util.Date (pro kompatibilitu se starým kódem) vyžaduje mezikrok přes Instant a časové pásmo. Podle Baeldung (2024) se tato operace provádí pomocí 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"))

Formátování a parsování

Pro formátování a parsování se používá třída DateTimeFormatter. Poskytuje předdefinované formáty pomocí konstant (ISO_LOCAL_DATE, ISO_LOCAL_TIME, ISO_LOCAL_DATE_TIME) a možnost vytvářet vlastní formáty pomocí vzorů.

Vzory formátování používají symboly: yyyy — rok, MM — měsíc (dvoumístný), dd — den, HH — hodina (0-23), mm — minuta, ss — sekunda. Metoda format() se volá na objektu datum-čas nebo pomocí DateTimeFormatter.

DateTimeFormatter také podporuje lokalizaci pomocí statických metod ofLocalizedDate(FormatStyle), ofLocalizedTime(FormatStyle) a ofLocalizedDateTime(FormatStyle). K dispozici jsou styly SHORT, MEDIUM, LONG a 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")
)

Porovnávání objektů datum-čas

Všechny tři třídy implementují rozhraní Comparable, což umožňuje jejich přirozené porovnání. Metoda compareTo() vrací záporné číslo, nulu nebo kladné číslo v závislosti na pořadí. Metody isBefore(), isAfter() a isEqual() vracejí boolean.

Pro LocalDate se porovnává chronologicky — dřívější datum je menší. Pro LocalTime — podle denní doby. Pro LocalDateTime — nejprve podle data, poté podle času. Všechna porovnání správně zohledňují přestupné roky a počet dní v měsících.

Důležitý rozdíl oproti starému API: equals() pro LocalDate, LocalTime a LocalDateTime porovnává hodnoty, nikoli reference. To znamená, že dva objekty se stejými poli budou stejné, i když se jedná o různé instance.

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

if (d1.isBefore(d2)) {
    Log.d("Datum", "d1 je před d2")
}

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

Aritmetika data a času

Všechny tři třídy podporují aritmetické operace pomocí metod plus a minus. Pro LocalDate jsou k dispozici plusDays(), plusWeeks(), plusMonths(), plusYears() a podobné minus-metody. LocalTime podporuje plusHours(), plusMinutes(), plusSeconds(), plusNanos().

LocalDateTime dědí všechny aritmetické operace obou typů. Vlastnost LocalDate: při přidávání měsíce výsledky správně řeší různou délku měsíců. Například 31. ledna + 1 měsíc = 28. (29. v přestupném roce) února.

Pro složitější operace existují třídy Period (pro data) a Duration (pro čas). Metody plus(TemporalAmount) a minus(TemporalAmount) přijímají tyto objekty.

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)

Příklady kódu v Kotlinu

Podívejme se na praktický příklad: aplikace pro evidenci pracovních směn. Je třeba vypočítat dobu trvání směny a určit, zda spadá do noční doby. Používáme LocalTime pro čas začátku a konce, LocalDate pro datum a LocalDateTime pro výpočty směn přecházejících půlnocí.

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

Druhý příklad — výpočet věku uživatele. Používáme LocalDate pro datum narození a porovnáváme jej s aktuálním datem, s přihlédnutím ke dni a měsíci narození.

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

Třetí příklad — práce s oznámeními. LocalDateTime se používá pro plánování připomínek. Kontrolujeme, zda nastal plánovaný čas.

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

Podpora v Androidu: úroveň API a desugaring

Vestavěná podpora pro java.time se na Androidu objevila od API 26 (Android 8.0 Oreo). Pro zařízení se staršími verzemi Androidu je třeba použít desugaring — mechanismus, který přidává podporu pro nová Java API do starších verzí.

Desugaring v Android Gradle Plugin se konfiguruje pomocí compileOptions v build.gradle. Stačí nastavit isCoreLibraryDesugaringEnabled = true a přidat knihovnu desugar_jdk_libs. Poté bude java.time dostupný pro všechny úrovně API od 14.

Pro projekty, které nemohou použít desugaring (např. legacy projekty na AGP nižším než 4.0), existuje knihovna ThreeTenABP — backport java.time. Poskytuje stejné třídy (LocalDate, LocalTime, LocalDateTime), ale v balíčku org.threeten.bp.

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

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

Typické chyby a jak se jim vyhnout

První častá chyba — použití LocalDateTime v distribuovaných systémech bez zohlednění časového pásma. Pokud je server v Europe/Moscow a klient v Asia/Tokyo, LocalDateTime bude interpretován odlišně. Řešení: použijte Instant nebo ZonedDateTime pro globální data.

Druhá chyba — nesprávné parsování řetězců. Ve výchozím nastavení očekává LocalDate.parse() formát ISO-8601 (yyyy-MM-dd). Pokud je řetězec v jiném formátu, je třeba explicitně předat DateTimeFormatter. Také je třeba ošetřit DateTimeParseException, aby aplikace nespadla při neplatném vstupu.

Třetí chyba — ignorování null-bezpečnosti. LocalDate, LocalTime a LocalDateTime jsou objekty, které mohou být null. V Kotlinu se doporučuje používání nullable typů s explicitní kontrolou nebo Elvis operátorem. V Javě — kontrolujte null před voláním metod.

Čtvrtá chyba — záměna mezi LocalDateTime a ZonedDateTime. LocalDateTime neobsahuje žádné informace o časovém pásmu. Pokud potřebujete předat absolutní časový okamžik — použijte zonální typy. Pokud stačí místní čas — lokální typy.

Často kladené otázky

Jaký je rozdíl mezi LocalDate a Date v Javě?

Date ukládá počet milisekund od 1970-01-01 UTC, zatímco LocalDate ukládá rok, měsíc a den bez vazby na časové pásmo. Date je mutabilní a není thread-safe, LocalDate — immutable a thread-safe. Date je zastaralý od Java 8.

Lze použít LocalDateTime v databázi?

Ano, LocalDateTime se dobře mapuje na SQL typ TIMESTAMP WITHOUT TIME ZONE. JPA a Room jej podporují prostřednictvím TypeConverter. Pro TIMESTAMP WITH TIME ZONE použijte ZonedDateTime nebo OffsetDateTime.

Jak získat počet dní mezi dvěma daty?

Použijte ChronoUnit.DAYS.between(startDate, endDate). Tato metoda vrací long — rozdíl ve dnech. Pro podrobnější výpočet použijte Period.between(), který vrací Period s roky, měsíci a dny.

Co dělat, pokud potřebuji zachovat přesnost času až na milisekundy?

LocalTime podporuje přesnost až na nanosekundy (9 desetinných míst). Pokud je potřeba přesnost až na milisekundy, použijte truncateTo(ChronoUnit.MILLIS) před uložením. To zabrání problémům se zaokrouhlováním při serializaci.

Proč LocalDate.now() vrací různé datum na různých zařízeních?

Metoda now() používá systémové hodiny zařízení a výchozí časové pásmo. Pokud se zařízení nacházejí v různých časových pásmech, datum se může lišit. Pro jednotné časové razítko použijte Instant.now(), který vždy vrací čas v UTC.

Shrnutí

  • LocalDate — immutable třída pro datum bez času a časového pásma. Používá se pro ukládání dat narození, termínů, dat událostí.
  • LocalTime — immutable třída pro denní dobu s přesností až na nanosekundu. Vhodná pro ukládání otevírací doby, dob trvání procesů.
  • LocalDateTime — kombinace data a času bez vazby na zónu. Nejflexibilnější lokální typ, ale nevhodný pro distribuované systémy.
  • Všechny tři třídy podporují aritmetiku, porovnávání, formátování a parsování prostřednictvím jednotného API založeného na DateTimeFormatter.
  • Na Androidu je java.time dostupný prostřednictvím vestavěné podpory od API 26 nebo prostřednictvím desugaring pro starší verze.
  • Pro globální časová razítka a data s časovými pásmy použijte ZonedDateTime nebo Instant místo lokálních typů.
  • Při parsování řetězců vždy předávejte DateTimeFormatter pro nestandardní formáty a ošetřete DateTimeParseException.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také