A multiplatform Kotlin library for working with date and time, specifically the iOS x64 target variant
—
Time zone handling with support for named IANA time zones, fixed UTC offsets, and time zone conversions. The library distinguishes between time zones (which have rules for daylight saving time) and fixed offsets (which are constant).
Base class for time zone representations, supporting both named time zones with DST rules and fixed offset time zones.
/**
* Time zone with rules for UTC offset calculations
* Base class for all time zone implementations
*/
expect open class TimeZone {
/** Time zone identifier (e.g., "America/New_York", "+05:00") */
val id: String
companion object {
/**
* Create a time zone from IANA identifier
* @param zoneId IANA time zone ID (e.g., "America/New_York", "Europe/London")
* @returns TimeZone for the specified identifier
* @throws IllegalTimeZoneException if the zone ID is invalid
*/
fun of(zoneId: String): TimeZone
/**
* Get the current system default time zone
* @returns TimeZone representing the system's default time zone
*/
fun currentSystemDefault(): TimeZone
/** UTC time zone (equivalent to FixedOffsetTimeZone(UtcOffset.ZERO)) */
val UTC: FixedOffsetTimeZone
/** Set of all available time zone IDs */
val availableZoneIds: Set<String>
}
}Usage Examples:
import kotlinx.datetime.*
// Create named time zones
val newYork = TimeZone.of("America/New_York")
val london = TimeZone.of("Europe/London")
val tokyo = TimeZone.of("Asia/Tokyo")
// System time zone
val systemTz = TimeZone.currentSystemDefault()
// UTC time zone
val utc = TimeZone.UTC
// List available time zones
val allZones = TimeZone.availableZoneIds
println("Available zones: ${allZones.size}")
allZones.filter { it.startsWith("America/") }.forEach { println(it) }Time zone implementation with a constant UTC offset, useful for representing fixed offsets that don't change with daylight saving time.
/**
* Time zone with constant UTC offset
* Extends TimeZone with a fixed, unchanging offset from UTC
*/
expect class FixedOffsetTimeZone : TimeZone {
/** Create a fixed offset time zone */
constructor(offset: UtcOffset)
/** The constant offset from UTC */
val offset: UtcOffset
}Usage Examples:
import kotlinx.datetime.*
// Create fixed offset time zones
val plus5 = FixedOffsetTimeZone(UtcOffset(hours = 5))
val minus8 = FixedOffsetTimeZone(UtcOffset(hours = -8))
val plus530 = FixedOffsetTimeZone(UtcOffset(hours = 5, minutes = 30))
println("ID: ${plus5.id}") // "+05:00"
println("Offset: ${plus5.offset}") // +05:00Represents a fixed offset from UTC, measured in hours, minutes, and seconds.
/**
* Fixed offset from UTC
* Represents a constant time difference from Coordinated Universal Time
*/
expect class UtcOffset : Comparable<UtcOffset> {
/** Total offset in seconds (can be negative for westward offsets) */
val totalSeconds: Int
companion object {
/** Zero offset (UTC) */
val ZERO: UtcOffset
/**
* Parse UtcOffset from string using specified format
* @param input String to parse (e.g., "+05:30", "Z", "-08:00")
* @param format Format to use for parsing
* @returns Parsed UtcOffset
*/
fun parse(input: CharSequence, format: DateTimeFormat<UtcOffset>): UtcOffset
}
}Factory Functions:
/**
* Create UtcOffset from components
* @param hours Hours component (can be null)
* @param minutes Minutes component (can be null)
* @param seconds Seconds component (can be null)
* @returns UtcOffset representing the total offset
*/
fun UtcOffset(hours: Int? = null, minutes: Int? = null, seconds: Int? = null): UtcOffset
/**
* Create zero offset (equivalent to UtcOffset.ZERO)
* @returns UtcOffset representing UTC (zero offset)
*/
fun UtcOffset(): UtcOffsetConversions:
/**
* Convert this offset to a FixedOffsetTimeZone
* @returns FixedOffsetTimeZone with this offset
*/
fun UtcOffset.asTimeZone(): FixedOffsetTimeZoneFormatting:
/**
* Format this offset using the specified format
* @param format Format to use
* @returns Formatted string representation
*/
fun UtcOffset.format(format: DateTimeFormat<UtcOffset>): String
/**
* Convert to ISO 8601 string representation
* @returns ISO format string (e.g., "+05:30", "Z", "-08:00")
*/
override fun UtcOffset.toString(): StringUsage Examples:
import kotlinx.datetime.*
// Create offsets
val utc = UtcOffset.ZERO // +00:00 (or "Z")
val plus5 = UtcOffset(hours = 5) // +05:00
val minus8 = UtcOffset(hours = -8) // -08:00
val plus530 = UtcOffset(hours = 5, minutes = 30) // +05:30
val complex = UtcOffset(hours = -4, minutes = -30, seconds = -15) // -04:30:15
// Properties
println("Total seconds: ${plus530.totalSeconds}") // 19800
// Convert to time zone
val fixedTz = plus530.asTimeZone()
// Formatting
println("Default: $plus530") // +05:30
println("ISO: ${plus530.format(UtcOffset.Formats.ISO)}")Extension functions for working with time zones and instants.
/**
* Get the UTC offset for the specified instant in this time zone
* @param instant The instant to check the offset for
* @returns UtcOffset that applies at the given instant in this time zone
*/
fun TimeZone.offsetAt(instant: Instant): UtcOffsetUsage Examples:
import kotlinx.datetime.*
import kotlin.time.Clock
val now = Clock.System.now()
val newYork = TimeZone.of("America/New_York")
// Get current offset (accounts for DST)
val currentOffset = newYork.offsetAt(now)
println("NY offset now: $currentOffset") // Could be -05:00 or -04:00 depending on DST
// Check offset at different times of year
val winter = LocalDate(2023, 1, 15).atStartOfDayIn(TimeZone.UTC)
val summer = LocalDate(2023, 7, 15).atStartOfDayIn(TimeZone.UTC)
val winterOffset = newYork.offsetAt(winter) // -05:00 (EST)
val summerOffset = newYork.offsetAt(summer) // -04:00 (EDT)Advanced time zone conversion functions using context receivers for cleaner API usage.
/**
* Context receiver functions for Instant conversions
* These allow calling conversion functions directly on Instant with TimeZone context
*/
context(TimeZone)
fun Instant.toLocalDateTime(): LocalDateTime
context(TimeZone)
fun Instant.offsetIn(): UtcOffset
context(TimeZone)
fun Instant.plus(period: DateTimePeriod): Instant
context(TimeZone)
fun Instant.minus(period: DateTimePeriod): InstantUsage Examples:
import kotlinx.datetime.*
import kotlin.time.Clock
val now = Clock.System.now()
val timeZone = TimeZone.of("Europe/Paris")
// Using context receivers (if supported)
with(timeZone) {
val localTime = now.toLocalDateTime()
val offset = now.offsetIn()
val tomorrow = now.plus(DatePeriod(days = 1))
}UtcOffset provides several predefined formatting options:
object UtcOffset.Formats {
/** ISO 8601 extended format: +HH:MM, +HH:MM:SS, or Z */
val ISO: DateTimeFormat<UtcOffset>
/** ISO 8601 basic format: +HHMM, +HHMMSS, or Z */
val ISO_BASIC: DateTimeFormat<UtcOffset>
/** Four digits format: always ±HHMM, never Z */
val FOUR_DIGITS: DateTimeFormat<UtcOffset>
}Usage Examples:
import kotlinx.datetime.*
val offset = UtcOffset(hours = 5, minutes = 30)
println("ISO: ${offset.format(UtcOffset.Formats.ISO)}") // +05:30
println("Basic: ${offset.format(UtcOffset.Formats.ISO_BASIC)}") // +0530
println("Four digits: ${offset.format(UtcOffset.Formats.FOUR_DIGITS)}") // +0530
val utc = UtcOffset.ZERO
println("UTC ISO: ${utc.format(UtcOffset.Formats.ISO)}") // Z
println("UTC Four digits: ${utc.format(UtcOffset.Formats.FOUR_DIGITS)}") // +0000import kotlinx.datetime.*
val newYork = TimeZone.of("America/New_York")
// Spring forward transition (2023-03-12 02:00 -> 03:00)
val beforeSpring = LocalDateTime(2023, 3, 12, 1, 30).toInstant(newYork)
val afterSpring = LocalDateTime(2023, 3, 12, 3, 30).toInstant(newYork)
println("Before spring DST: ${newYork.offsetAt(beforeSpring)}") // -05:00
println("After spring DST: ${newYork.offsetAt(afterSpring)}") // -04:00
// Fall back transition (2023-11-05 02:00 -> 01:00)
val beforeFall = LocalDateTime(2023, 11, 5, 0, 30).toInstant(newYork)
val afterFall = LocalDateTime(2023, 11, 5, 3, 30).toInstant(newYork)
println("Before fall DST: ${newYork.offsetAt(beforeFall)}") // -04:00
println("After fall DST: ${newYork.offsetAt(afterFall)}") // -05:00import kotlinx.datetime.*
import kotlin.time.Clock
val now = Clock.System.now()
// Convert the same instant to different time zones
val newYork = TimeZone.of("America/New_York")
val london = TimeZone.of("Europe/London")
val tokyo = TimeZone.of("Asia/Tokyo")
val nyTime = now.toLocalDateTime(newYork)
val londonTime = now.toLocalDateTime(london)
val tokyoTime = now.toLocalDateTime(tokyo)
println("Same moment in different zones:")
println("New York: $nyTime")
println("London: $londonTime")
println("Tokyo: $tokyoTime")import kotlinx.datetime.*
// Create a meeting time in a specific offset
val meetingTime = LocalDateTime(2023, 12, 25, 15, 30)
val offset = UtcOffset(hours = -5) // EST
val meetingInstant = meetingTime.toInstant(offset)
// Convert to different time zones
val londonTime = meetingInstant.toLocalDateTime(TimeZone.of("Europe/London"))
val tokyoTime = meetingInstant.toLocalDateTime(TimeZone.of("Asia/Tokyo"))
println("Meeting at $meetingTime (UTC$offset):")
println("London: $londonTime")
println("Tokyo: $tokyoTime")import kotlinx.datetime.*
try {
val invalidZone = TimeZone.of("Invalid/Zone")
} catch (e: IllegalTimeZoneException) {
println("Invalid time zone: ${e.message}")
}
// Check if a zone ID is valid before using
val zoneId = "America/New_York"
if (zoneId in TimeZone.availableZoneIds) {
val timeZone = TimeZone.of(zoneId)
// Use the time zone safely
} else {
println("Time zone $zoneId is not available")
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-datetime-iosx64