Datetime
Date, time, timezone, and range types for Kotlin Multiplatform. Built on kotlinx-datetime with rich arithmetic, formatting, and a testable clock.
Why another date/time library?
kotlinx-datetime gives you the foundation: Instant, LocalDate, TimeZone.
But real-world code needs more — date ranges, time slots, formatting, DST-aware arithmetic, anchoring to
start-of-month
or start-of-week, and a clock you can freeze in tests.
The core idea: rich types, multiplatform
Datetime provides a set of Mp* types — MpInstant, MpLocalDate,
MpLocalDateTime, MpLocalTime, MpZonedDateTime, MpTimezone —
that wrap kotlinx-datetime internally but expose a much richer API. They work identically on JVM and JS.
val now = kronos.instantNow()
val nextWeek = now.plusDays(7)
val startOfMonth = now.atStartOfMonth()
val formatted = now.atZone(MpTimezone.of("Europe/Berlin"))
.format("dd MMM yyyy HH:mm")
val range = MpLocalDateRange(
from = now.toLocalDate(),
to = nextWeek.toLocalDate(),
)
println(range.numberOfDays) // 7 What you get
Rich Date/Time Types
Instant, LocalDate, LocalDateTime, LocalTime, ZonedDateTime, Timezone — all multiplatform.
Date Arithmetic
Add days, months, years. DST-aware. Anchor to start of month, week, quarter, year.
Ranges & Time Slots
Date ranges, instant ranges, zoned datetime ranges, and time-of-day slots with set operations.
Formatting & Parsing
Pattern-based formatting (dd MMM yyyy HH:mm) and ISO string
parsing.
Kronos Clock
An injectable, freezable time source. System clock in production, fixed clock in tests.
JVM Interop
Seamless conversion to and from java.time types via
.jvm and .mp properties.
Quick taste
import io.peekandpoke.ultra.datetime.MpInstant
import io.peekandpoke.ultra.datetime.MpLocalDate
import io.peekandpoke.ultra.datetime.MpTimezone
import io.peekandpoke.ultra.datetime.Kronos
// Parse and manipulate
val date = MpLocalDate.parse("2025-03-15")
val nextMonth = date.plusMonths(1) // 2025-04-15
val startOfWeek = date.atStartOfPrevious(DayOfWeek.MONDAY)
// Timezone-aware
val instant = MpInstant.parse("2025-03-15T10:30:00Z")
val berlin = instant.atZone(MpTimezone.of("Europe/Berlin"))
val tokyo = instant.atZone(MpTimezone.of("Asia/Tokyo"))
// Testable clock
val kronos = Kronos.fixed(MpInstant.parse("2025-01-01T00:00:00Z"))
println(kronos.instantNow()) // always 2025-01-01T00:00:00Z