# Handling Date and Time Formatting in Kotlin Time: A Complete Guide to Cross-Device Consistency

> Master Kotlin time formatting for cross-device consistency. Learn to store dates as ISO-8601 and format at the UI layer for identical behavior everywhere.

- Repository: [JetBrains/kotlin](https://github.com/jetbrains/kotlin)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Store all date-time values as ISO-8601 strings using `kotlin.time.Instant.toString()` and apply locale-aware formatting only at the UI boundary to guarantee identical behavior across JVM, JavaScript, Native, and WASM targets.**

Handling date and time formatting in Kotlin time requires a clear separation between your domain model and presentation layer. The JetBrains/kotlin repository provides a robust multiplatform time API in `kotlin.time` that ensures consistency by using ISO-8601 as the canonical representation. By adhering to the patterns implemented in [`libraries/stdlib/src/kotlin/time/Instant.kt`](https://github.com/JetBrains/kotlin/blob/main/libraries/stdlib/src/kotlin/time/Instant.kt), you can prevent locale-related bugs when sharing temporal data between different devices and operating systems.

## Use ISO-8601 as the Canonical Representation for Kotlin Time Formatting

The foundation of consistent date handling lies in the `Instant` class, which represents a moment in time independent of time zones or locales. According to the source code in [`libraries/stdlib/src/kotlin/time/Instant.kt`](https://github.com/JetBrains/kotlin/blob/main/libraries/stdlib/src/kotlin/time/Instant.kt), the `toString()` method delegates to an internal `formatIso` function that always emits a UTC-based ISO-8601 string, such as `2023-01-02T23:40:57.120Z`.

This implementation spans lines 31-74 in [`Instant.kt`](https://github.com/JetBrains/kotlin/blob/main/Instant.kt) and guarantees that the output format remains **fixed and locale-agnostic**. Because the string representation never changes based on device settings, you can safely store these values in databases, transmit them via JSON APIs, or compare them across different platforms without parsing ambiguities.

The reverse operation, `Instant.parse()`, uses the same ISO-8601 logic located in lines 59-86 of [`Instant.kt`](https://github.com/JetBrains/kotlin/blob/main/Instant.kt). This symmetry ensures that any string produced by `toString()` can be reliably reconstructed on any Kotlin-supported target.

## Store and Transmit Raw Instants to Ensure Device Consistency

When persisting temporal data, always serialize the `Instant` directly rather than any formatted representation. The `Clock.System.now()` function, defined in [`libraries/stdlib/src/kotlin/time/Clock.kt`](https://github.com/JetBrains/kotlin/blob/main/libraries/stdlib/src/kotlin/time/Clock.kt), provides the primary source of the current moment and works uniformly across JVM, JavaScript, Native, and WASM.

```kotlin
import kotlin.time.Clock
import kotlin.time.Instant

// Serialize for storage or transmission
fun storeInstant(): String {
    val now: Instant = Clock.System.now()
    return now.toString()  // ISO-8601: 2023-01-02T23:40:57.120Z
}

// Deserialize from external source
fun loadInstant(encoded: String): Instant? = 
    runCatching { Instant.parse(encoded) }.getOrNull()

```

By storing only the ISO-8601 string, you eliminate the risk of locale-specific parsing errors when the data is read on a device with different language settings. This approach is particularly critical for distributed systems where Android, iOS, and server-side JVM components exchange temporal data.

## Apply Locale-Aware Formatting Only at the UI Boundary

The recommended architecture for handling date and time formatting in Kotlin time separates domain logic from presentation. Keep `Instant` objects in your business logic, and convert them to human-readable strings only when rendering the UI.

### JVM Platform Formatting with DateTimeFormatter

On the JVM, convert `kotlin.time.Instant` to `java.time.Instant` using the `toJavaInstant()` extension defined in [`libraries/stdlib/jvm/src/kotlin/time/InstantJvm.kt`](https://github.com/JetBrains/kotlin/blob/main/libraries/stdlib/jvm/src/kotlin/time/InstantJvm.kt) (lines 63-68). Then apply `DateTimeFormatter` with the user's locale.

```kotlin
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Locale
import kotlin.time.Clock
import kotlin.time.toJavaInstant

fun formatForUser(): String {
    val instant = Clock.System.now().toJavaInstant()
    val formatter = DateTimeFormatter.ofPattern("EEEE, d MMMM yyyy HH:mm", Locale.getDefault())
        .withZone(ZoneId.systemDefault())
    return formatter.format(instant)  // e.g., "Monday, 2 January 2023 15:40"
}

```

This approach leverages the full ICU locale data available on the JVM while maintaining a clean separation from the domain model.

### Multiplatform Formatting with kotlinx.datetime

For common code shared across platforms, use the `kotlinx.datetime` library. Convert `Instant` to `LocalDateTime` using the current system time zone, then format manually or delegate to platform-specific APIs.

```kotlin
import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime

fun formattedCommon(): String {
    val instant = Clock.System.now()
    val local = instant.toLocalDateTime(TimeZone.currentSystemDefault())
    // Manual formatting for common code; replace with platform APIs for production
    return "${local.date} ${local.time.hour.toString().padStart(2, '0')}:${local.time.minute.toString().padStart(2, '0')}"
}

```

This strategy ensures that your domain logic remains in `kotlin.time` while presentation concerns are handled appropriately for each target platform.

### JavaScript and Native Platform Handling

On JavaScript, delegate to `Intl.DateTimeFormat` for locale-aware formatting. On Native platforms (iOS), use `NSDateFormatter` or equivalent. Always convert from the ISO-8601 string or `Instant` object at the boundary rather than storing formatted strings.

## Summary

- **Store canonical ISO-8601**: Use `Instant.toString()` and `Instant.parse()` from [`libraries/stdlib/src/kotlin/time/Instant.kt`](https://github.com/JetBrains/kotlin/blob/main/libraries/stdlib/src/kotlin/time/Instant.kt) for all persistence and transmission.
- **Separate domain from presentation**: Keep `Instant` objects in business logic; apply formatting only when rendering UI.
- **Leverage platform formatters**: Use `DateTimeFormatter` on JVM, `Intl.DateTimeFormat` on JS, and native APIs on iOS/Android, converting via `toJavaInstant()` or `kotlinx.datetime` extensions.
- **Avoid locale-dependent storage**: Never persist human-readable date strings to databases or APIs; always use the fixed ISO format implemented in `formatIso`.

## Frequently Asked Questions

### Why should I use ISO-8601 for storing dates in Kotlin?

ISO-8601 provides a **fixed, unambiguous text representation** that does not change based on device locale or time zone settings. The `formatIso` function in [`libraries/stdlib/src/kotlin/time/Instant.kt`](https://github.com/JetBrains/kotlin/blob/main/libraries/stdlib/src/kotlin/time/Instant.kt) guarantees that `Instant.toString()` always outputs UTC-based ISO-8601 (e.g., `2023-01-02T23:40:57.120Z`), ensuring that data serialized on an Android device in Japan can be correctly parsed on a JVM server in Brazil without formatting errors.

### How do I convert kotlin.time.Instant to Java's Instant?

Use the `toJavaInstant()` extension function defined in [`libraries/stdlib/jvm/src/kotlin/time/InstantJvm.kt`](https://github.com/JetBrains/kotlin/blob/main/libraries/stdlib/jvm/src/kotlin/time/InstantJvm.kt) (lines 63-68). This zero-cost conversion allows you to pass Kotlin `Instant` objects to Java `DateTimeFormatter` or other legacy APIs while maintaining the same underlying epoch timestamp. Remember to import `kotlin.time.toJavaInstant` to access the extension.

### What is the difference between kotlin.time and kotlinx.datetime?

**kotlin.time** (in the standard library) provides the fundamental `Instant`, `Duration`, and `Clock` types for measuring moments and elapsed time. **kotlinx.datetime** is a separate multiplatform library that builds on `kotlin.time` to add `LocalDateTime`, `LocalDate`, `TimeZone`, and conversion utilities like `Instant.toLocalDateTime()`. For formatting, you typically use `kotlin.time` for domain logic and `kotlinx.datetime` (or platform-specific APIs) for presentation logic.

### How do I handle custom date patterns in common Kotlin multiplatform code?

Since `kotlinx.datetime` does not yet provide a pattern-based formatter for common code, you should convert the `Instant` to a `LocalDateTime` using `toLocalDateTime(TimeZone.currentSystemDefault())`, then manually construct the string or use **expect/actual** declarations. The **actual** implementations delegate to `DateTimeFormatter` on JVM, `Intl.DateTimeFormat` on JavaScript, and `NSDateFormatter` on Native. This approach keeps your domain model clean while allowing rich, locale-aware formatting at the platform boundary.

```