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.

We didn't set out to wrap kotlinx-datetime. We just kept writing the same extension functions in every project until it was obvious they belonged in a library.

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