CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlinx--kotlinx-datetime-jvm

A multiplatform Kotlin library for working with date and time with JVM target support.

Pending
Overview
Eval results
Files

periods-units.mddocs/

Periods & Units

This document covers period representations and arithmetic units for flexible date and time calculations: DateTimePeriod, DatePeriod, and the DateTimeUnit hierarchy.

DateTimePeriod

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

Usage Examples

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

DatePeriod

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

Usage Examples

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

DateTimeUnit

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

Predefined Units

// 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

Factory Functions

// 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

Usage with Different Types

With Instant (requires TimeZone for DateBased units)

// 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): Long

With LocalDate (only DateBased units)

fun 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

Usage Examples

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

Working with Periods vs Units

When to Use DateTimePeriod/DatePeriod

  • Representing a combination of different time components (e.g., "2 years, 3 months, and 5 days")
  • Storing the result of period calculations between two dates/instants
  • Serializing/deserializing period information
  • When you need all components preserved separately
// 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())

When to Use DateTimeUnit

  • Performing arithmetic with a single unit type (e.g., "add 5 days")
  • Measuring differences in specific units (e.g., "how many weeks between these dates?")
  • Creating regular intervals or recurring events
  • When you need precise control over the arithmetic operation
// 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 * 3

Edge Cases and Considerations

Time Zone Transitions

Date-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:30

Month Length Variations

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

Normalization

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 normalization

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-datetime-jvm

docs

core-types.md

formatting.md

index.md

jvm-interop.md

periods-units.md

serialization.md

tile.json