A multiplatform Kotlin library for working with date and time with JVM target support.
—
This document covers period representations and arithmetic units for flexible date and time calculations: DateTimePeriod, DatePeriod, and the DateTimeUnit hierarchy.
A period with both date and time components, representing a difference between two instants decomposed into years, months, days, hours, minutes, seconds, and nanoseconds.
// Constructor
data class DateTimePeriod(
val years: Int = 0,
val months: Int = 0,
val days: Int = 0,
val hours: Int = 0,
val minutes: Int = 0,
val seconds: Int = 0,
val nanoseconds: Long = 0
)
// Properties
val DateTimePeriod.years: Int
val DateTimePeriod.months: Int
val DateTimePeriod.days: Int
val DateTimePeriod.hours: Int
val DateTimePeriod.minutes: Int
val DateTimePeriod.seconds: Int
val DateTimePeriod.nanoseconds: Long
// Arithmetic operations
operator fun DateTimePeriod.plus(other: DateTimePeriod): DateTimePeriod
operator fun DateTimePeriod.minus(other: DateTimePeriod): DateTimePeriod
operator fun DateTimePeriod.unaryMinus(): DateTimePeriod
// Companion object functions
fun DateTimePeriod.Companion.parse(input: CharSequence): DateTimePeriod
// String representation (ISO 8601 format)
fun DateTimePeriod.toString(): String// Create periods
val oneYear = DateTimePeriod(years = 1)
val twoWeeks = DateTimePeriod(days = 14)
val fullPeriod = DateTimePeriod(
years = 1,
months = 2,
days = 15,
hours = 3,
minutes = 30,
seconds = 45
)
// Arithmetic on periods
val combined = oneYear + twoWeeks
val difference = fullPeriod - oneYear
val negated = -fullPeriod
// Parse ISO 8601 period strings
val parsed = DateTimePeriod.parse("P1Y2M15DT3H30M45S")
// Use with Instant arithmetic (requires timezone)
val instant = Clock.System.now()
val timeZone = TimeZone.currentSystemDefault()
val futureInstant = instant.plus(fullPeriod, timeZone)A period with only date components (years, months, days), representing calendar-based time differences. This is a subtype of DateTimePeriod with all time components set to zero.
// Constructor
data class DatePeriod(
val years: Int = 0,
val months: Int = 0,
val days: Int = 0
) : DateTimePeriod
// Properties (inherited from DateTimePeriod)
val DatePeriod.years: Int
val DatePeriod.months: Int
val DatePeriod.days: Int
// Arithmetic operations (inherited from DateTimePeriod)
operator fun DatePeriod.plus(other: DatePeriod): DatePeriod
operator fun DatePeriod.minus(other: DatePeriod): DatePeriod
operator fun DatePeriod.unaryMinus(): DatePeriod
// Companion object functions
fun DatePeriod.Companion.parse(input: CharSequence): DatePeriod
// String representation (ISO 8601 format)
fun DatePeriod.toString(): String// Create date periods
val oneYear = DatePeriod(years = 1)
val sixMonths = DatePeriod(months = 6)
val twoWeeks = DatePeriod(days = 14)
// Combine periods
val quarterAndTwoWeeks = DatePeriod(months = 3, days = 14)
val combined = sixMonths + twoWeeks
// Parse ISO 8601 date period strings
val parsed = DatePeriod.parse("P1Y6M14D")
// Use with LocalDate arithmetic
val date = LocalDate(2023, 1, 1)
val futureDate = date.plus(quarterAndTwoWeeks)
val periodBetween = date.periodUntil(futureDate)Sealed class hierarchy representing units for measuring time, providing both duration-based and calendar-based units for datetime arithmetic.
// Base sealed class
sealed class DateTimeUnit {
// Duration-based units (independent of calendar/timezone)
sealed class TimeBased : DateTimeUnit() {
val duration: Duration
}
// Calendar-based units (depend on calendar/timezone)
sealed class DateBased : DateTimeUnit()
// Day-based units (subtype of DateBased)
sealed class DayBased : DateBased() {
val days: Int
}
// Month-based units (subtype of DateBased)
sealed class MonthBased : DateBased() {
val months: Int
}
}// Time-based units (duration-based)
val DateTimeUnit.NANOSECOND: DateTimeUnit.TimeBased
val DateTimeUnit.MICROSECOND: DateTimeUnit.TimeBased
val DateTimeUnit.MILLISECOND: DateTimeUnit.TimeBased
val DateTimeUnit.SECOND: DateTimeUnit.TimeBased
val DateTimeUnit.MINUTE: DateTimeUnit.TimeBased
val DateTimeUnit.HOUR: DateTimeUnit.TimeBased
// Date-based units (calendar-based)
val DateTimeUnit.DAY: DateTimeUnit.DayBased
val DateTimeUnit.WEEK: DateTimeUnit.DayBased
val DateTimeUnit.MONTH: DateTimeUnit.MonthBased
val DateTimeUnit.QUARTER: DateTimeUnit.MonthBased
val DateTimeUnit.YEAR: DateTimeUnit.MonthBased// Create custom time-based units
fun DateTimeUnit.TimeBased(duration: Duration): DateTimeUnit.TimeBased
// Create custom day-based units
fun DateTimeUnit.DayBased(days: Int): DateTimeUnit.DayBased
// Create custom month-based units
fun DateTimeUnit.MonthBased(months: Int): DateTimeUnit.MonthBased
// Multiplication operators for creating multiples
operator fun DateTimeUnit.times(scalar: Int): DateTimeUnit
operator fun Int.times(unit: DateTimeUnit): DateTimeUnit// TimeBased units - no timezone required
fun Instant.plus(value: Int, unit: DateTimeUnit.TimeBased): Instant
fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant
fun Instant.minus(value: Int, unit: DateTimeUnit.TimeBased): Instant
fun Instant.minus(value: Long, unit: DateTimeUnit.TimeBased): Instant
fun Instant.until(other: Instant, unit: DateTimeUnit.TimeBased): Long
// DateBased units - timezone required
fun Instant.plus(value: Int, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Instant
fun Instant.plus(value: Long, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Instant
fun Instant.minus(value: Int, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Instant
fun Instant.minus(value: Long, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Instant
fun Instant.until(other: Instant, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Longfun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): LocalDate
fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): LocalDate
fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate
fun LocalDate.minus(value: Long, unit: DateTimeUnit.DateBased): LocalDate
fun LocalDate.until(other: LocalDate, unit: DateTimeUnit.DateBased): Long// Using predefined units
val instant = Clock.System.now()
val timeZone = TimeZone.currentSystemDefault()
// Time-based units (no timezone needed)
val oneHourLater = instant.plus(1, DateTimeUnit.HOUR)
val thirtySecondsEarlier = instant.minus(30, DateTimeUnit.SECOND)
// Date-based units (timezone required for Instant)
val oneDayLater = instant.plus(1, DateTimeUnit.DAY, timeZone)
val threeMonthsLater = instant.plus(3, DateTimeUnit.MONTH, timeZone)
// Custom units
val tenDays = DateTimeUnit.DayBased(10)
val sixMonths = DateTimeUnit.MonthBased(6)
val fiveMinutes = DateTimeUnit.TimeBased(5.minutes)
// Unit multiplication
val twoWeeks = DateTimeUnit.WEEK * 2
val threeQuarters = 3 * DateTimeUnit.QUARTER
// With LocalDate (only date-based units)
val date = LocalDate(2023, 6, 15)
val futureDate = date.plus(2, DateTimeUnit.MONTH)
val monthsBetween = date.until(futureDate, DateTimeUnit.MONTH)
// Calculate differences
val daysBetween = date.until(futureDate, DateTimeUnit.DAY)
val weeksBetween = date.until(futureDate, DateTimeUnit.WEEK)// Period example - combination of components
val complexPeriod = DateTimePeriod(years = 2, months = 3, days = 5, hours = 4)
val instant = Clock.System.now()
val futureInstant = instant.plus(complexPeriod, TimeZone.currentSystemDefault())// Unit example - single unit arithmetic
val date = LocalDate(2023, 1, 1)
val futureDate = date.plus(30, DateTimeUnit.DAY)
val daysBetween = date.until(futureDate, DateTimeUnit.DAY) // returns 30
// Custom intervals
val biweekly = DateTimeUnit.WEEK * 2
val quarterly = DateTimeUnit.MONTH * 3Date-based arithmetic accounts for daylight saving time and timezone rule changes:
// DST transition example
val timeZone = TimeZone.of("Europe/London")
val springForward = LocalDateTime(2023, 3, 26, 1, 30) // Before DST
val instant = springForward.toInstant(timeZone)
// Adding 1 hour might skip over the "missing" hour
val oneHourLater = instant.plus(1, DateTimeUnit.HOUR)
val localTime = oneHourLater.toLocalDateTime(timeZone) // 03:30, not 02:30Month arithmetic handles varying month lengths appropriately:
val january31 = LocalDate(2023, 1, 31)
val february = january31.plus(1, DateTimeUnit.MONTH) // 2023-02-28 (not 2023-02-31)
val leap = LocalDate(2024, 2, 29) // Leap year
val nextYear = leap.plus(1, DateTimeUnit.YEAR) // 2025-02-28 (leap day doesn't exist)Periods are automatically normalized:
val period1 = DateTimePeriod(months = 24, hours = 2, minutes = 63)
val period2 = DateTimePeriod(years = 2, hours = 3, minutes = 3)
// period1 and period2 are equivalent after normalizationInstall with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-datetime-jvm