kotlinx-datetime

repository·master·Indexed 25 days ago

https://github.com/kotlin/kotlinx-datetime

A multiplatform Kotlin library for date and time manipulation providing a pragmatic API based on ISO 8601. It maintains a strict boundary between physical time (Instant) and local civil time (LocalDateTime, LocalDate, LocalTime, YearMonth). The library includes support for time zone conversions, calendar-aware arithmetic, and custom formatting via a Format DSL.

Tokens
4.8K
Snippets
18
Records
28
Agent score
76%

What's inside kotlinx-datetime

  1. Overview of kotlinx-datetime

    master
    kotlinx-datetime is a multiplatform Kotlin library designed for pragmatic date and time manipulation. It focuses on common developer use cases by providing a minimal API surface based on the ISO 8601 standard. The library maintains a strict boundary between physical time (Instant) and local, time-zone-dependent civil time (e.g., LocalDateTime).
  2. Use DateTimeComponents for partial or out-of-bounds data

    master

    When dealing with partial data (e.g., just month and day) or invalid/out-of-bounds values (e.g., 23:59:60), use DateTimeComponents. This allows you to parse the data, mutate it to a valid state, and then convert it to a standard type.

    // Parsing partial data
    val monthDay = DateTimeComponents.Format { monthNumber(); char('/'); day() }
        .parse("12/25")
    
    // Handling out-of-bounds seconds
    val time = DateTimeComponents.Format { time(LocalTime.Formats.ISO) }
        .parse("23:59:60").apply {
            if (second == 60) second = 59
        }.toLocalTime()
    
    // Formatting complex data
    val rfcString = DateTimeComponents.Formats.RFC_1123.format {
        setDate(LocalDate(2023, 1, 7))
        hour = 23
        minute = 59
        second = 60
        setOffset(UtcOffset(hours = 2))
    }
  3. Migrate from kotlinx-datetime 0.6.x to 0.7.x+

    master

    Starting with version 0.7.0, kotlinx.datetime.Instant and kotlinx.datetime.Clock have been deprecated in favor of the Kotlin standard library's kotlin.time.Instant and kotlin.time.Clock.

    Migration Steps

    1. Try upgrading to 0.7.1 directly. If you don't have transitive dependencies on older versions, your code should compile.
    2. Check transitive dependencies. If other libraries use older kotlinx-datetime versions, you may encounter ClassNotFoundException. Update those libraries if possible.
    3. Update kotlinx-serialization. Ensure you are using version 1.9.0 or newer.
    4. Use the compatibility release. If you cannot update third-party libraries, use version 0.7.1-0.6.x-compat instead of 0.7.1.

    Resolving Ambiguity and Type Mismatches

    If you have both import kotlin.time.* and import kotlinx.datetime.*, you may face resolution errors. Explicitly import the specific type you need (e.g., import kotlin.time.Instant).

    If you must convert between the two types, use these compatibility functions:

    • kotlin.time.Instant.toDeprecatedInstant(): kotlinx.datetime.Instant
    • kotlin.time.Clock.toDeprecatedClock(): kotlinx.datetime.Clock
    • kotlinx.datetime.Instant.toStdlibInstant(): kotlin.time.Instant
    • kotlinx.datetime.Clock.toStdlibClock(): kotlin.time.Clock
  4. Update Windows/IANA timezone name mappings

    master

    The library maintains a mapping between IANA timezone names and Windows-specific registry names. If the mapping is outdated, you can update it using a Gradle task.

    Steps to update:

    1. Update your local master branch.
    2. Run the download task: ./gradlew downloadWindowsZonesMapping.
    3. If the task succeeds, the mappings are already up-to-date.
    4. If the task fails with The new mappings were written to the filesystem., verify the changes with git diff.
    5. Commit the changes to a new branch and follow the standard release and CI verification process.
    # Update the master branch
    git checkout master
    git pull
    
    # Run the mapping update task
    ./gradlew downloadWindowsZonesMapping
    
    # If changes were written, commit them
    git checkout -b update-windows-tz-names
    git commit -a -m "Update the Windows/IANA timezone name mappings"
    git push -u origin update-windows-tz-names
  5. Install kotlinx-datetime in Gradle

    master

    To use kotlinx-datetime in your project, ensure mavenCentral() is in your repositories block.

    For Multiplatform projects, add the dependency to the commonMain source set.

    For Single-platform projects, add it to the standard dependencies block.

    // Multiplatform project
    kotlin {
        sourceSets {
            commonMain {
                 dependencies {
                     implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0")
                 }
            }
        }
    }
    
    // Single-platform project
    dependencies {
        implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0")
    }
  6. Choosing the right date and time type

    master

    Use the following guidance to select the appropriate type for your use case:

    TypeRecommended Use Case
    kotlin.time.InstantTimestamps of past events (e.g., log entries) or well-defined future events (e.g., a deadline in 1 hour).
    LocalDateTimeScheduled events in the far future (e.g., a meeting in months) where you must track the TimeZone separately, or for decoding an Instant for UI display.
    LocalDateEvents without a specific time (e.g., a birth date).
    YearMonthEvents without a specific day (e.g., credit card expiration).
    LocalTimeEvents without a specific date.
  7. Customize Dokka HTML output templates

    master
    To use custom templates for Dokka HTML output, you must configure the templatesDir property within the Dokka plugin configuration. This property defines the source directory for your custom templates. For detailed instructions on customizing HTML pages, refer to the official Dokka documentation regarding custom HTML pages.
  8. Define custom date/time formats

    master

    For non-ISO 8601 formats, use the Format DSL. You can define custom patterns or use Unicode-style patterns.

    Note: Using non-constant Unicode patterns requires the @OptIn(FormatStringsInDatetimeFormats::class) annotation.

    // Custom DSL format
    val dateFormat = LocalDate.Format {
        monthNumber(padding = Padding.SPACE)
        char('/')
        day()
        char(' ')
        year()
    }
    val date = dateFormat.parse("12/24 2023")
    
    // Unicode pattern with OptIn
    @OptIn(FormatStringsInDatetimeFormats::class)
    val formatPattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS]"
    val dateTimeFormat = LocalDateTime.Format {
        byUnicodePattern(formatPattern)
    }
    val parsed = dateTimeFormat.parse("2023-12-24T23:59:59")
  9. Enable full time zone support in Wasm/WASI

    master

    By default, Kotlin/Wasm WASI only provides the UTC time zone. To enable all time zones, add the kotlinx-datetime-zoneinfo dependency.

    kotlin {
        sourceSets {
            val wasmWasiMain by getting {
                dependencies {
                    implementation("kotlinx-datetime-zoneinfo", "2026c-spi.0.8.0")
                }
            }
        }
    }
  10. Enable full time zone support in Wasm/JS

    master

    Wasm/JS follows the same pattern as Kotlin/JS. Add the @js-joda/timezone npm dependency and initialize the module.

    // 1. Add npm dependency in Gradle
    kotlin {
        sourceSets {
            val wasmJsMain by getting {
                dependencies {
                    implementation(npm("@js-joda/timezone", "X.X.X"))
                }
            }
        }
    }
    
    // 2. Initialize in your code
    @JsModule("@js-joda/timezone")
    external object JsJodaTimeZoneModule
    
    private val jsJodaTz = JsJodaTimeZoneModule
  11. Avoid LocalDateTime arithmetic

    master

    Arithmetic on LocalDateTime is intentionally omitted to prevent errors caused by Daylight Saving Time (DST) transitions (e.g., 'spring forward' gaps).

    Best Practice: Perform all arithmetic on Instant values using a TimeZone, then convert the resulting Instant back to LocalDateTime for display.

    val timeZone = TimeZone.of("Europe/Berlin")
    val localDateTime = LocalDateTime.parse("2021-03-27T02:16:20")
    val instant = localDateTime.toInstant(timeZone)
    
    // Perform arithmetic on the Instant
    val instantOneDayLater = instant.plus(1, DateTimeUnit.DAY, timeZone)
    
    // Convert back to LocalDateTime
    val localDateTimeOneDayLater = instantOneDayLater.toLocalDateTime(timeZone)