CtrlK
BlogDocsLog inGet started
Tessl Logo

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

A multiplatform Kotlin library for working with date and time, specifically the iOS x64 target variant

Pending
Overview
Eval results
Files

arithmetic.mddocs/

Date/Time Arithmetic

Date and time arithmetic operations using units, periods, and duration-based calculations. The library provides flexible arithmetic capabilities for both precise time-based operations and calendar-aware date operations.

Capabilities

DateTimeUnit

Sealed class hierarchy representing units for measuring time intervals, supporting both precise time-based units and calendar-based units.

/**
 * Units for measuring time intervals
 * Base sealed class for all time measurement units
 */
sealed class DateTimeUnit {
    /**
     * Multiply this unit by a scalar value
     * @param scalar Multiplier for the unit
     * @returns New DateTimeUnit representing the scaled unit
     */
    abstract fun times(scalar: Int): DateTimeUnit
}

Time-Based Units:

/**
 * Time-based unit with precise nanosecond duration
 * Represents units that have a fixed duration in nanoseconds
 */
class TimeBased(val nanoseconds: Long) : DateTimeUnit() {
    override fun times(scalar: Int): TimeBased
}

// Predefined time-based constants
object DateTimeUnit {
    val NANOSECOND: TimeBased   // 1 nanosecond
    val MICROSECOND: TimeBased  // 1,000 nanoseconds
    val MILLISECOND: TimeBased  // 1,000,000 nanoseconds
    val SECOND: TimeBased       // 1,000,000,000 nanoseconds
    val MINUTE: TimeBased       // 60 seconds
    val HOUR: TimeBased         // 60 minutes
}

Date-Based Units:

/**
 * Date-based unit (abstract base for calendar units)
 * Represents units that vary in duration based on calendar context
 */
sealed class DateBased : DateTimeUnit()

/**
 * Day-based unit representing a number of calendar days
 * Duration varies based on DST transitions and leap seconds
 */
class DayBased(val days: Int) : DateBased() {
    override fun times(scalar: Int): DayBased
}

/**
 * Month-based unit representing a number of months
 * Duration varies significantly based on month length and year
 */
class MonthBased(val months: Int) : DateBased() {
    override fun times(scalar: Int): MonthBased
}

// Predefined date-based constants
object DateTimeUnit {
    val DAY: DayBased          // 1 calendar day
    val WEEK: DayBased         // 7 calendar days
    val MONTH: MonthBased      // 1 calendar month
    val QUARTER: MonthBased    // 3 calendar months
    val YEAR: MonthBased       // 12 calendar months
    val CENTURY: MonthBased    // 1200 calendar months
}

Usage Examples:

import kotlinx.datetime.*
import kotlin.time.Clock

val now = Clock.System.now()

// Time-based arithmetic (precise)
val oneHourLater = now.plus(1, DateTimeUnit.HOUR, TimeZone.UTC)
val thirtyMinutes = now.plus(30, DateTimeUnit.MINUTE, TimeZone.UTC)
val fiveSeconds = now.plus(5, DateTimeUnit.SECOND, TimeZone.UTC)

// Custom time units
val twoHours = DateTimeUnit.HOUR.times(2)
val halfHour = DateTimeUnit.MINUTE.times(30)

// Date-based arithmetic (calendar-aware)
val tomorrow = now.plus(1, DateTimeUnit.DAY, TimeZone.of("America/New_York"))
val nextWeek = now.plus(1, DateTimeUnit.WEEK, TimeZone.UTC)
val nextMonth = now.plus(1, DateTimeUnit.MONTH, TimeZone.currentSystemDefault())

DateTimePeriod

Represents a combination of date and time components for complex arithmetic operations.

/**
 * Combination of date and time components
 * Represents a period with separate year/month/day/hour/minute/second/nanosecond parts
 */
sealed class DateTimePeriod {
    /** Whole years */
    abstract val years: Int
    
    /** Months not forming whole years (-11..11) */
    abstract val months: Int
    
    /** Calendar days (can exceed month boundaries) */
    abstract val days: Int
    
    /** Whole hours (can exceed day boundaries) */
    abstract val hours: Int
    
    /** Minutes not forming whole hours (-59..59) */
    abstract val minutes: Int
    
    /** Seconds not forming whole minutes (-59..59) */
    abstract val seconds: Int
    
    /** Nanoseconds not forming whole seconds */
    abstract val nanoseconds: Int
}

Factory Functions:

/**
 * Create a DateTimePeriod with specified components
 * @param years Number of years (default 0)
 * @param months Number of months (default 0)
 * @param days Number of days (default 0)
 * @param hours Number of hours (default 0)
 * @param minutes Number of minutes (default 0)
 * @param seconds Number of seconds (default 0)
 * @param nanoseconds Number of nanoseconds (default 0)
 * @returns DateTimePeriod with the specified components
 */
fun DateTimePeriod(
    years: Int = 0,
    months: Int = 0,
    days: Int = 0,
    hours: Int = 0,
    minutes: Int = 0,
    seconds: Int = 0,
    nanoseconds: Long = 0
): DateTimePeriod

/**
 * Parse DateTimePeriod from ISO 8601 duration string
 * @param text ISO 8601 duration string (e.g., "P1Y2M3DT4H5M6.7S")
 * @returns Parsed DateTimePeriod
 */
fun DateTimePeriod.Companion.parse(text: String): DateTimePeriod

/**
 * Convert kotlin.time.Duration to DateTimePeriod
 * @returns DateTimePeriod representing the duration
 */
fun Duration.toDateTimePeriod(): DateTimePeriod

Formatting:

/**
 * Convert to ISO 8601 duration format
 * @returns ISO 8601 duration string (e.g., "P1Y2M3DT4H5M6.789S")
 */
override fun DateTimePeriod.toString(): String

Usage Examples:

import kotlinx.datetime.*
import kotlin.time.*

// Create periods
val complexPeriod = DateTimePeriod(
    years = 1,
    months = 2, 
    days = 15,
    hours = 8,
    minutes = 30,
    seconds = 45
)

val shortPeriod = DateTimePeriod(hours = 2, minutes = 30)

// From Duration
val duration = 2.hours + 30.minutes
val period = duration.toDateTimePeriod()

// Parse from ISO 8601
val parsed = DateTimePeriod.parse("P1Y2M15DT8H30M45S")

// Format as ISO 8601
println(complexPeriod.toString()) // "P1Y2M15DT8H30M45S"

// Use in arithmetic
val now = Clock.System.now()
val timeZone = TimeZone.of("Europe/Paris")
val future = now.plus(complexPeriod, timeZone)

DatePeriod

Specialized period class for date-only arithmetic operations.

/**
 * Date-only period (time components are zero)
 * Extends DateTimePeriod with only date components
 */
class DatePeriod : DateTimePeriod {
    /**
     * Create a date period with specified components
     * @param years Number of years (default 0)
     * @param months Number of months (default 0) 
     * @param days Number of days (default 0)
     */
    constructor(years: Int = 0, months: Int = 0, days: Int = 0)
    
    companion object {
        /**
         * Parse DatePeriod from ISO 8601 date duration string
         * @param text ISO 8601 date duration (e.g., "P1Y2M15D")
         * @returns Parsed DatePeriod
         */
        fun parse(text: String): DatePeriod
    }
}

Usage Examples:

import kotlinx.datetime.*

// Create date periods
val oneYear = DatePeriod(years = 1)
val twoMonths = DatePeriod(months = 2)
val fifteenDays = DatePeriod(days = 15)
val combined = DatePeriod(years = 1, months = 6, days = 10)

// Parse from ISO 8601
val parsed = DatePeriod.parse("P1Y6M10D")

// Use with LocalDate
val today = LocalDate(2023, 12, 25)
val nextYear = today.plus(oneYear)
val futureDate = today.plus(combined)

// Use with Instant (requires time zone for calendar arithmetic)
val now = Clock.System.now()
val timeZone = TimeZone.of("America/New_York")
val futureInstant = now.plus(combined, timeZone)

Instant Arithmetic Operations

Extension functions for performing arithmetic on Instant values.

Basic Arithmetic

/**
 * Add a DateTimePeriod to an Instant in the specified time zone
 * @param period Period to add
 * @param timeZone Time zone for calendar calculations
 * @returns New Instant representing the result
 */
fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant

/**
 * Subtract a DateTimePeriod from an Instant in the specified time zone
 * @param period Period to subtract
 * @param timeZone Time zone for calendar calculations
 * @returns New Instant representing the result
 */
fun Instant.minus(period: DateTimePeriod, timeZone: TimeZone): Instant

/**
 * Add a value in the specified unit to an Instant
 * @param value Amount to add
 * @param unit Unit for the arithmetic
 * @param timeZone Time zone for calendar-based units
 * @returns New Instant representing the result
 */
fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant
fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant

/**
 * Subtract a value in the specified unit from an Instant
 * @param value Amount to subtract
 * @param unit Unit for the arithmetic
 * @param timeZone Time zone for calendar-based units
 * @returns New Instant representing the result
 */
fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant
fun Instant.minus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant

Difference Calculations

/**
 * Calculate the difference between two instants in the specified unit
 * @param other The other instant
 * @param unit Unit for the result
 * @param timeZone Time zone for calendar-based calculations
 * @returns Difference in the specified unit
 */
fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long

/**
 * Calculate the number of whole days between two instants
 * @param other The other instant
 * @param timeZone Time zone for day calculations
 * @returns Number of days
 */
fun Instant.daysUntil(other: Instant, timeZone: TimeZone): Int

/**
 * Calculate the number of whole months between two instants
 * @param other The other instant
 * @param timeZone Time zone for month calculations
 * @returns Number of months
 */
fun Instant.monthsUntil(other: Instant, timeZone: TimeZone): Int

/**
 * Calculate the number of whole years between two instants
 * @param other The other instant
 * @param timeZone Time zone for year calculations
 * @returns Number of years
 */
fun Instant.yearsUntil(other: Instant, timeZone: TimeZone): Int

/**
 * Calculate the period between two instants
 * @param other The other instant
 * @param timeZone Time zone for calculations
 * @returns DateTimePeriod representing the difference
 */
fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod

Usage Examples:

import kotlinx.datetime.*
import kotlin.time.Clock

val now = Clock.System.now()
val timeZone = TimeZone.of("Europe/London")

// Basic arithmetic with periods
val period = DateTimePeriod(years = 1, months = 6, days = 15, hours = 12)
val future = now.plus(period, timeZone)
val past = now.minus(period, timeZone)

// Arithmetic with units
val tomorrow = now.plus(1, DateTimeUnit.DAY, timeZone)
val nextHour = now.plus(1, DateTimeUnit.HOUR, timeZone)
val nextMonth = now.plus(1, DateTimeUnit.MONTH, timeZone)

// Calculate differences
val startOfYear = LocalDate(2023, 1, 1).atStartOfDayIn(timeZone)
val endOfYear = LocalDate(2023, 12, 31).atTime(23, 59, 59).toInstant(timeZone)

val daysInYear = startOfYear.daysUntil(endOfYear, timeZone)
val monthsInYear = startOfYear.monthsUntil(endOfYear, timeZone)
val fullPeriod = startOfYear.periodUntil(endOfYear, timeZone)

println("Days: $daysInYear")     // 364
println("Months: $monthsInYear") // 11
println("Period: $fullPeriod")   // P11M30D23H59M59S

LocalDate Arithmetic Operations

Arithmetic operations specifically for LocalDate values.

/**
 * Add a DatePeriod to a LocalDate
 * @param period Date period to add
 * @returns New LocalDate representing the result
 */
fun LocalDate.plus(period: DatePeriod): LocalDate

/**
 * Subtract a DatePeriod from a LocalDate
 * @param period Date period to subtract
 * @returns New LocalDate representing the result
 */
fun LocalDate.minus(period: DatePeriod): LocalDate

/**
 * Add a value in the specified date-based unit
 * @param value Amount to add
 * @param unit Date-based unit (DayBased or MonthBased)
 * @returns New LocalDate representing the result
 */
fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): LocalDate
fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): LocalDate

/**
 * Subtract a value in the specified date-based unit
 * @param value Amount to subtract
 * @param unit Date-based unit (DayBased or MonthBased)
 * @returns New LocalDate representing the result
 */
fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate
fun LocalDate.minus(value: Long, unit: DateTimeUnit.DateBased): LocalDate

/**
 * Calculate difference between dates in the specified unit
 * @param other The other date
 * @param unit Date-based unit for the result
 * @returns Difference in the specified unit
 */
fun LocalDate.until(other: LocalDate, unit: DateTimeUnit.DateBased): Long

/**
 * Calculate whole days between dates
 * @param other The other date
 * @returns Number of days
 */
fun LocalDate.daysUntil(other: LocalDate): Int

/**
 * Calculate whole months between dates
 * @param other The other date
 * @returns Number of months
 */
fun LocalDate.monthsUntil(other: LocalDate): Int

/**
 * Calculate whole years between dates
 * @param other The other date
 * @returns Number of years
 */
fun LocalDate.yearsUntil(other: LocalDate): Int

/**
 * Calculate the date period between two dates
 * @param other The other date
 * @returns DatePeriod representing the difference
 */
fun LocalDate.periodUntil(other: LocalDate): DatePeriod

Usage Examples:

import kotlinx.datetime.*

val startDate = LocalDate(2023, 1, 15)

// Add/subtract periods
val period = DatePeriod(years = 1, months = 6, days = 10)
val futureDate = startDate.plus(period)    // 2024-07-25
val pastDate = startDate.minus(period)     // 2021-07-05

// Add/subtract units
val tomorrow = startDate.plus(1, DateTimeUnit.DAY)     // 2023-01-16
val nextWeek = startDate.plus(1, DateTimeUnit.WEEK)    // 2023-01-22
val nextMonth = startDate.plus(1, DateTimeUnit.MONTH)  // 2023-02-15
val nextYear = startDate.plus(1, DateTimeUnit.YEAR)    // 2024-01-15

// Calculate differences
val endDate = LocalDate(2024, 7, 25)
val daysDiff = startDate.daysUntil(endDate)      // 556
val monthsDiff = startDate.monthsUntil(endDate)  // 18
val yearsDiff = startDate.yearsUntil(endDate)    // 1
val fullPeriod = startDate.periodUntil(endDate)  // P1Y6M10D

YearMonth Arithmetic Operations

Specialized arithmetic for YearMonth values.

/**
 * Add months to a YearMonth
 * @param value Number of months to add
 * @param unit Must be MonthBased unit
 * @returns New YearMonth representing the result
 */
fun YearMonth.plus(value: Int, unit: DateTimeUnit.MonthBased): YearMonth
fun YearMonth.plus(value: Long, unit: DateTimeUnit.MonthBased): YearMonth

/**
 * Subtract months from a YearMonth
 * @param value Number of months to subtract
 * @param unit Must be MonthBased unit
 * @returns New YearMonth representing the result
 */
fun YearMonth.minus(value: Int, unit: DateTimeUnit.MonthBased): YearMonth
fun YearMonth.minus(value: Long, unit: DateTimeUnit.MonthBased): YearMonth

/**
 * Calculate difference in months
 * @param other The other YearMonth
 * @param unit MonthBased unit for the result
 * @returns Difference in the specified unit
 */
fun YearMonth.until(other: YearMonth, unit: DateTimeUnit.MonthBased): Long

/**
 * Calculate whole months between YearMonth values
 * @param other The other YearMonth
 * @returns Number of months
 */
fun YearMonth.monthsUntil(other: YearMonth): Int

/**
 * Calculate whole years between YearMonth values
 * @param other The other YearMonth
 * @returns Number of years
 */
fun YearMonth.yearsUntil(other: YearMonth): Int

// Convenience functions
fun YearMonth.plusYear(): YearMonth
fun YearMonth.minusYear(): YearMonth
fun YearMonth.plusMonth(): YearMonth
fun YearMonth.minusMonth(): YearMonth

Usage Examples:

import kotlinx.datetime.*

val startMonth = YearMonth(2023, 6)  // June 2023

// Add/subtract using units
val nextYear = startMonth.plus(1, DateTimeUnit.YEAR)        // 2024-06
val nextQuarter = startMonth.plus(1, DateTimeUnit.QUARTER)  // 2023-09
val nextMonth = startMonth.plus(1, DateTimeUnit.MONTH)      // 2023-07

// Convenience functions
val plusYear = startMonth.plusYear()    // 2024-06
val plusMonth = startMonth.plusMonth()  // 2023-07
val minusYear = startMonth.minusYear()  // 2022-06

// Calculate differences
val endMonth = YearMonth(2025, 12)  // December 2025
val monthsDiff = startMonth.monthsUntil(endMonth)  // 30
val yearsDiff = startMonth.yearsUntil(endMonth)    // 2

Error Handling

Arithmetic operations can throw DateTimeArithmeticException in certain scenarios.

/**
 * Thrown when datetime arithmetic operations fail
 * Typically occurs with invalid date calculations
 */
class DateTimeArithmeticException : RuntimeException {
    constructor(message: String)
    constructor(message: String, cause: Throwable?)
}

Common Error Scenarios:

import kotlinx.datetime.*

try {
    // This can throw if the result would be invalid
    val leap = LocalDate(2020, 2, 29)  // Feb 29 in leap year
    val nextYear = leap.plus(1, DateTimeUnit.YEAR)  // Would be Feb 29, 2021 (invalid)
} catch (e: DateTimeArithmeticException) {
    println("Arithmetic failed: ${e.message}")
}

// Safe alternatives - check before arithmetic
val date = LocalDate(2020, 2, 29)
val targetYear = 2021

// Check if the target date would be valid
val wouldBeValid = try {
    LocalDate(targetYear, date.month, date.day)
    true
} catch (e: Exception) {
    false
}

if (wouldBeValid) {
    val result = date.plus(1, DateTimeUnit.YEAR)
} else {
    // Handle invalid date case (e.g., use last day of month)
    val lastDayOfMonth = YearMonth(targetYear, date.month).lastDay
    println("Using last day of month instead: $lastDayOfMonth")
}

DST-Aware Arithmetic

When performing arithmetic with Instant values, the choice of time zone affects calendar-based calculations during DST transitions.

import kotlinx.datetime.*

val timeZone = TimeZone.of("America/New_York")

// Spring forward example (2023-03-12 02:00 -> 03:00)
val beforeSpring = LocalDateTime(2023, 3, 12, 1, 0).toInstant(timeZone)

// Adding 24 hours vs adding 1 day
val plus24Hours = beforeSpring.plus(24, DateTimeUnit.HOUR, timeZone)
val plus1Day = beforeSpring.plus(1, DateTimeUnit.DAY, timeZone)

val local24Hours = plus24Hours.toLocalDateTime(timeZone)  // 2:00 AM (due to DST)
val local1Day = plus1Day.toLocalDateTime(timeZone)        // 1:00 AM (same time next day)

println("24 hours later: $local24Hours")  // 2023-03-13T02:00
println("1 day later: $local1Day")        // 2023-03-13T01:00

// Fall back example (2023-11-05 02:00 -> 01:00)
val beforeFall = LocalDateTime(2023, 11, 5, 1, 0).toInstant(timeZone)

val fallPlus24Hours = beforeFall.plus(24, DateTimeUnit.HOUR, timeZone)
val fallPlus1Day = beforeFall.plus(1, DateTimeUnit.DAY, timeZone)

val fallLocal24Hours = fallPlus24Hours.toLocalDateTime(timeZone)  // 12:00 AM (due to DST)
val fallLocal1Day = fallPlus1Day.toLocalDateTime(timeZone)        // 1:00 AM (same time next day)

Install with Tessl CLI

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

docs

arithmetic.md

formatting.md

index.md

instant.md

local-types.md

platform.md

ranges.md

serialization.md

timezones.md

tile.json