LocalDate, LocalTime och LocalDateTime — huvudklasserna i paketet java.time som tillhandahåller arbete med datum och tid utan koppling till tidszon. Enligt Oracles dokumentation (Java 17, 2024) är dessa typer utformade som immutable och thread-safe, vilket gör dem säkra för flertrådade applikationer. De blev tillgängliga på Android via desugaring från API 26 och för äldre versioner via biblioteket ThreeTenABP.
Huvudpunkter
LocalDate — klass som representerar datum i formatet år-månad-dag utan information om tid och tidszon. Den används för att lagra data som födelsedatum, händelsedatum eller utgångsdatum.
LocalDate lagrar år i intervallet -999999999 till +999999999, månad från 1 till 12 och dag i månaden med hänsyn till skottår. Klassen är helt immutable — varje operation returnerar ett nytt objekt.
LocalTime representerar tiden på dagen: timmar, minuter, sekunder och nanosekunder. Maximal precision — upp till nanosekund. LocalTime innehåller ingen information om datum och tidszon, vilket gör den bekväm för att lagra öppettider eller processtid.
LocalDateTime kombinerar LocalDate och LocalTime i ett objekt. Det är den mest använda typen när man behöver lagra både datum och tid, men koppling till tidszon inte krävs. Till exempel datum och tid för en konsert i lokalt format.
Enligt Oracle Java Documentation (2024) är alla tre klasserna utformade baserat på idéer från biblioteket Joda-Time, men med förbättrad arkitektur och full integration i standardbiblioteket.
Paketet java.time dök upp i Java 8 som en ersättning för de föråldrade klasserna Date, Calendar och SimpleDateFormat. Dess arkitektur är baserad på principerna om immutable objekt och fluent-gränssnitt.
Nyckelegenskap — alla huvudklasser är value-based. Detta innebär att deras instanser jämförs efter värde, inte efter referens, och de kan inte ärvas. För att jämföra två objekt används metoden equals, inte operatorn ==.
Paketet är uppdelat i flera kategorier. Typer utan tidszon — LocalDate, LocalTime, LocalDateTime — används för lokala datum och tider. Typer med tidszon — ZonedDateTime, OffsetDateTime, OffsetTime — lägger till information om förskjutning eller zon. Oögonblickliga typer — Instant — representerar en punkt på tidslinjen i UTC.
Denna uppdelning löser problemet som var typiskt för det gamla API:et: programmeraren visste aldrig om Date-objektet innehöll information om tidszon eller inte. I java.time deklarerar varje typ explicit sin semantik.
Klassen LocalDate tillhandahåller många metoder för att skapa, läsa och ändra datum. Aktuellt datum kan erhållas via den statiska metoden now(). Ett specifikt datum — via metoden of(int year, int month, int dayOfMonth).
För att läsa datumkomponenter används getters: getYear(), getMonthValue(), getDayOfMonth(), getDayOfWeek(), getDayOfYear(). Metoden getMonth() returnerar enum Month, och getDayOfWeek() — enum DayOfWeek.
LocalDate stödjer kontroll av datum. Metoderna isBefore(), isAfter() och isEqual() gör det möjligt att jämföra datum. Metoden isLeapYear() kontrollerar om året är skottår. Metoden lengthOfMonth() returnerar antalet dagar i månaden och lengthOfYear() — på året.
För ändring används metoderna withYear(), withMonth(), withDayOfMonth(), som returnerar ett nytt objekt med ändrad komponent. Metoderna plusDays(), minusMonths() och liknande utför datum aritmetik.
LocalTime representerar tiden på dagen med precision upp till nanosekund. Standardformat — ISO-8601 (HH:mm:ss.nnnnnnnnn). Minimivärde — 00:00, maxvärde — 23:59:59.999999999.
Ett LocalTime-objekt kan skapas via now() för aktuell tid, of(int hour, int minute), of(int hour, int minute, int second) eller of(int hour, int minute, int second, int nanoOfSecond). Metoden parse(CharSequence text) tolkar en sträng i ISO-8601-format.
Getters inkluderar getHour(), getMinute(), getSecond(), getNano(). Metoden toSecondOfDay() returnerar antalet sekunder sedan dagens början och toNanoOfDay() — nanosekunder. Detta är praktiskt för varaktighetsberäkningar inom en dag.
LocalTime stödjer samma jämförelse- och ändringsoperationer som LocalDate: plusHours(), minusMinutes(), withHour(), withMinute(). Metoderna isBefore() och isAfter() fungerar med hänsyn till tidens cykliska natur inom en dag.
LocalDateTime kombinerar LocalDates och LocalTimes kapacitet i en klass. Den lagrar både datum och tid, men utan tidszon. Det är den mest flexibla lokala typen, men kräver försiktighet vid användning i distribuerade system.
LocalDateTime kan skapas via de statiska metoderna now(), of(LocalDate date, LocalTime time), of(int year, Month month, int dayOfMonth, int hour, int minute) och deras överlagringar. Man kan också kombinera LocalDate och LocalTime via metoden atTime().
LocalDateTime ger tillgång till alla datum- och tidsfält via motsvarande getters: toLocalDate() och toLocalTime() returnerar separata komponenter. Metoden truncatedTo(TemporalUnit unit) gör det möjligt att avrunda tiden till given precision — till exempel till minuter.
För konvertering till tidszon används metoden atZone(ZoneId zone), som returnerar ZonedDateTime. Detta är det enda sättet att lägga till en tidszon till LocalDateTime.
Alla tre klasserna använder ett enhetligt skapandemönster via statiska fabriksmetoder. Klassernas konstruktorer är deklarerade som private — ett objekt kan inte skapas direkt via new.
Huvudsätt att skapa:
Metoden of har många överlagringar. För LocalDate behövs år, månad och dag. För LocalTime — timmar och minuter (valfritt sekunder och nanosekunder). För LocalDateTime — år, månad, dag, timmar, minuter. Månad kan skickas som int (1-12) eller som enum Month.
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)
Java.time-klasserna är utformade för enkel konvertering mellan varandra. LocalDate kan konverteras till LocalDateTime via metoden atTime(LocalTime) eller atStartOfDay(). LocalTime — via atDate(LocalDate).
LocalDateTime kan konverteras tillbaka till LocalDate via toLocalDate() och till LocalTime via toLocalTime(). För konvertering till ZonedDateTime används metoden atZone(ZoneId).
Konvertering till java.util.Date (för kompatibilitet med gammal kod) kräver ett mellansteg via Instant och tidszon. Enligt Baeldung (2024) utförs denna operation via Date.from(instant).
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"))
För formatering och parsning används klassen DateTimeFormatter. Den erbjuder fördefinierade format via konstanter (ISO_LOCAL_DATE, ISO_LOCAL_TIME, ISO_LOCAL_DATE_TIME) och möjlighet att skapa egna format via mönster.
Formateringsmönster använder symboler: yyyy — år, MM — månad (två siffror), dd — dag, HH — timme (0-23), mm — minut, ss — sekund. Metoden format() anropas på datum-tid-objektet eller via DateTimeFormatter.
DateTimeFormatter stödjer också lokalisering via de statiska metoderna ofLocalizedDate(FormatStyle), ofLocalizedTime(FormatStyle) och ofLocalizedDateTime(FormatStyle). Stilarna SHORT, MEDIUM, LONG och FULL finns tillgängliga.
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")
)
Alla tre klasserna implementerar gränssnittet Comparable, vilket möjliggör naturlig jämförelse. Metoden compareTo() returnerar ett negativt tal, noll eller positivt beroende på ordningen. Metoderna isBefore(), isAfter() och isEqual() returnerar boolean.
För LocalDate görs jämförelsen kronologiskt — ett tidigare datum är mindre. För LocalTime — enligt tiden på dagen. För LocalDateTime — först enligt datum, sedan enligt tid. Alla jämförelser tar korrekt hänsyn till skottår och antalet dagar i månader.
En viktig skillnad från det gamla API:et: equals() för LocalDate, LocalTime och LocalDateTime jämför värden, inte referenser. Detta innebär att två objekt med samma fält kommer att vara lika, även om de är olika instanser.
val d1 = LocalDate.of(2026, 7, 21)
val d2 = LocalDate.of(2026, 12, 25)
if (d1.isBefore(d2)) {
Log.d("Datum", "d1 är före d2")
}
val sortedDates = listOf(d2, d1).sorted()
Alla tre klasserna stödjer aritmetiska operationer via metoderna plus och minus. För LocalDate finns plusDays(), plusWeeks(), plusMonths(), plusYears() och liknande minus-metoder. LocalTime stödjer plusHours(), plusMinutes(), plusSeconds(), plusNanos().
LocalDateTime ärver alla aritmetiska operationer från båda typerna. Egenskap hos LocalDate: när man lägger till en månad hanterar resultaten korrekt olika längder på månader. Till exempel, 31 januari + 1 månad = 28 (29 under skottår) februari.
För mer komplexa operationer finns klasserna Period (för datum) och Duration (för tid). Metoderna plus(TemporalAmount) och minus(TemporalAmount) accepterar dessa objekt.
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)
Låt oss betrakta ett praktiskt exempel: en applikation för registrering av arbetspass. Vi behöver beräkna passets längd och avgöra om det infaller på natten. Vi använder LocalTime för start- och sluttid, LocalDate för datum och LocalDateTime för beräkningar av pass som går över midnatt.
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()
}
}
Andra exemplet — beräkning av en användares ålder. Vi använder LocalDate för födelsedatum och jämför det med aktuellt datum, med hänsyn till födelsedagens dag och månad.
fun calculateAge(birthDate: LocalDate): Int {
val today = LocalDate.now()
val period = Period.between(birthDate, today)
return period.years
}
Tredje exemplet — arbete med notifieringar. LocalDateTime används för att schemalägga påminnelser. Vi kontrollerar om den schemalagda tiden har kommit.
data class Reminder(
val id: Long,
val scheduledAt: LocalDateTime
) {
fun isDue(): Boolean =
LocalDateTime.now().isAfter(scheduledAt)
}
Inbyggt stöd för java.time dök upp på Android från API 26 (Android 8.0 Oreo). För enheter med äldre versioner av Android måste desugaring användas — en mekanism som lägger till stöd för nya Java API:er i tidigare versioner.
Desugaring i Android Gradle Plugin konfigureras via compileOptions i build.gradle. Det räcker att ställa in isCoreLibraryDesugaringEnabled = true och lägga till biblioteket desugar_jdk_libs. Därefter blir java.time tillgängligt för alla API-nivåer från 14.
För projekt som inte kan använda desugaring (t.ex. legacy-projekt på AGP under 4.0) finns biblioteket ThreeTenABP — en backport av java.time. Den tillhandahåller samma klasser (LocalDate, LocalTime, LocalDateTime), men i paketet org.threeten.bp.
@Suppress("UnstableApiUsage")
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
"coreLibraryDesugaring"("com.android.tools:desugar_jdk_libs:2.1.4")
}
Första vanliga misstaget — att använda LocalDateTime i distribuerade system utan att ta hänsyn till tidszonen. Om servern finns i Europe/Moscow och klienten i Asia/Tokyo kommer LocalDateTime att tolkas olika. Lösning: använd Instant eller ZonedDateTime för global data.
Andra misstaget — felaktig parsning av strängar. Som standard förväntar sig LocalDate.parse() formatet ISO-8601 (yyyy-MM-dd). Om strängen har ett annat format måste DateTimeFormatter skickas explicit. Man bör också hantera DateTimeParseException för att applikationen inte ska krascha vid ogiltig inmatning.
Tredje misstaget — att ignorera null-säkerhet. LocalDate, LocalTime och LocalDateTime är objekt som kan vara null. I Kotlin rekommenderas användning av nullable-typer med explicit kontroll eller Elvis-operatorn. I Java — kontrollera null innan metoder anropas.
Fjärde misstaget — förväxling mellan LocalDateTime och ZonedDateTime. LocalDateTime innehåller ingen information om tidszon. Om du behöver skicka ett absolut tidsögonblick — använd zonala typer. Om lokal tid räcker — lokala typer.
Vanliga frågor
Date lagrar antalet millisekunder sedan 1970-01-01 UTC, medan LocalDate lagrar år, månad och dag utan koppling till tidszon. Date är mutabel och inte thread-safe, LocalDate — immutable och thread-safe. Date är föråldrad sedan Java 8.
Ja, LocalDateTime mappar väl till SQL-typen TIMESTAMP WITHOUT TIME ZONE. JPA och Room stödjer det via TypeConverter. För TIMESTAMP WITH TIME ZONE använd ZonedDateTime eller OffsetDateTime.
Använd ChronoUnit.DAYS.between(startDate, endDate). Denna metod returnerar long — skillnaden i dagar. För mer detaljerad beräkning använd Period.between(), som returnerar en Period med år, månader och dagar.
LocalTime stödjer precision upp till nanosekunder (9 decimaler). Om precision till millisekunder behövs, använd truncateTo(ChronoUnit.MILLIS) före lagring. Detta förhindrar avrundningsproblem vid serialisering.
Metoden now() använder enhetens systemklocka och standard tidszon. Om enheter befinner sig i olika tidszoner kan datumet skilja sig. För en enhetlig tidsstämpel använd Instant.now(), som alltid returnerar tiden i UTC.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också