A multiplatform Kotlin library for working with date and time with JVM target support.
—
This document covers the comprehensive parsing and formatting system for all datetime types, including the builder DSL, predefined formats, and DateTimeComponents for complex parsing scenarios.
The base interface for all datetime formatting operations.
sealed interface DateTimeFormat<T> {
// Format value to string
fun format(value: T): String
// Format value to appendable
fun <A : Appendable> formatTo(appendable: A, value: T): A
// Parse string to value
fun parse(input: CharSequence): T
// Parse string to value or return null if invalid
fun parseOrNull(input: CharSequence): T?
}
// Companion object for utility functions
object DateTimeFormat.Companion {
fun formatAsKotlinBuilderDsl(format: DateTimeFormat<*>): String
}Each datetime type provides a format builder function for creating custom formats using a DSL.
fun LocalDate.Companion.Format(
block: DateTimeFormatBuilder.WithDate.() -> Unit
): DateTimeFormat<LocalDate>
// Builder interface for date formatting
interface DateTimeFormatBuilder.WithDate {
// Year formatting
fun year(padding: Padding = Padding.ZERO)
fun yearTwoDigits(baseYear: Int)
// Month formatting
fun monthNumber(padding: Padding = Padding.ZERO)
fun monthName(names: MonthNames)
fun monthAbbreviated(names: MonthNames)
// Day formatting
fun dayOfMonth(padding: Padding = Padding.ZERO)
fun dayOfYear(padding: Padding = Padding.ZERO)
fun dayOfWeek(names: DayOfWeekNames)
fun dayOfWeekAbbreviated(names: DayOfWeekNames)
// Literal text
fun chars(value: String)
fun char(value: Char)
// Alternative formats
fun alternativeParsing(block: DateTimeFormatBuilder.WithDate.() -> Unit)
}fun LocalTime.Companion.Format(
block: DateTimeFormatBuilder.WithTime.() -> Unit
): DateTimeFormat<LocalTime>
// Builder interface for time formatting
interface DateTimeFormatBuilder.WithTime {
// Hour formatting
fun hour(padding: Padding = Padding.ZERO)
fun hourOfAmPm(padding: Padding = Padding.ZERO)
fun amPmHour(padding: Padding = Padding.ZERO)
fun amPmMarker(am: String, pm: String)
// Minute and second formatting
fun minute(padding: Padding = Padding.ZERO)
fun second(padding: Padding = Padding.ZERO)
// Fractional second formatting
fun secondFraction(fixedLength: Int)
fun secondFraction(minLength: Int, maxLength: Int)
// Literal text
fun chars(value: String)
fun char(value: Char)
// Alternative formats
fun alternativeParsing(block: DateTimeFormatBuilder.WithTime.() -> Unit)
}fun LocalDateTime.Companion.Format(
block: DateTimeFormatBuilder.WithDateTime.() -> Unit
): DateTimeFormat<LocalDateTime>
// Builder interface combining date and time formatting
interface DateTimeFormatBuilder.WithDateTime :
DateTimeFormatBuilder.WithDate,
DateTimeFormatBuilder.WithTime {
// Date component
fun date(format: DateTimeFormat<LocalDate>)
// Time component
fun time(format: DateTimeFormat<LocalTime>)
// Combined date and time with separator
fun dateTime(
dateFormat: DateTimeFormat<LocalDate>,
timeFormat: DateTimeFormat<LocalTime>,
separator: String = "T"
)
}fun YearMonth.Companion.Format(
block: DateTimeFormatBuilder.WithYearMonth.() -> Unit
): DateTimeFormat<YearMonth>
// Builder interface for year-month formatting
interface DateTimeFormatBuilder.WithYearMonth {
// Year formatting
fun year(padding: Padding = Padding.ZERO)
fun yearTwoDigits(baseYear: Int)
// Month formatting
fun monthNumber(padding: Padding = Padding.ZERO)
fun monthName(names: MonthNames)
fun monthAbbreviated(names: MonthNames)
// Literal text
fun chars(value: String)
fun char(value: Char)
// Alternative formats
fun alternativeParsing(block: DateTimeFormatBuilder.WithYearMonth.() -> Unit)
}fun UtcOffset.Companion.Format(
block: DateTimeFormatBuilder.WithUtcOffset.() -> Unit
): DateTimeFormat<UtcOffset>
// Builder interface for UTC offset formatting
interface DateTimeFormatBuilder.WithUtcOffset {
// Offset formatting
fun offset(format: UtcOffsetFormat)
fun offsetHours(padding: Padding = Padding.ZERO)
fun offsetMinutesOfHour(padding: Padding = Padding.ZERO)
fun offsetSecondsOfMinute(padding: Padding = Padding.ZERO)
// Literal text
fun chars(value: String)
fun char(value: Char)
// Alternative formats
fun alternativeParsing(block: DateTimeFormatBuilder.WithUtcOffset.() -> Unit)
}Each datetime type provides commonly used predefined formats through its Formats object.
object LocalDate.Formats {
// ISO 8601 format: "2023-12-25"
val ISO: DateTimeFormat<LocalDate>
// ISO basic format: "20231225"
val ISO_BASIC: DateTimeFormat<LocalDate>
}object LocalTime.Formats {
// ISO 8601 format: "14:30:00" or "14:30:00.123456789"
val ISO: DateTimeFormat<LocalTime>
}object LocalDateTime.Formats {
// ISO 8601 format: "2023-12-25T14:30:00" or "2023-12-25T14:30:00.123456789"
val ISO: DateTimeFormat<LocalDateTime>
}object YearMonth.Formats {
// ISO 8601 format: "2023-12"
val ISO: DateTimeFormat<YearMonth>
}object UtcOffset.Formats {
// ISO 8601 format: "+01:00", "-05:30", "Z"
val ISO: DateTimeFormat<UtcOffset>
// Four digits format: "+0100", "-0530", "+0000"
val FOUR_DIGITS: DateTimeFormat<UtcOffset>
}Intermediate representation for parsing and formatting complex datetime values that may include timezone information and partial data.
class DateTimeComponents {
// Date components
var year: Int?
var monthNumber: Int?
var dayOfMonth: Int?
var dayOfYear: Int?
var dayOfWeek: DayOfWeek?
// Time components
var hour: Int?
var hourOfAmPm: Int?
var isAmPm: Boolean?
var minute: Int?
var second: Int?
var nanosecond: Int?
// Offset components
var offsetIsNegative: Boolean?
var offsetHours: Int?
var offsetMinutesOfHour: Int?
var offsetSecondsOfMinute: Int?
// Timezone component
var timeZoneId: String?
// Conversion methods
fun toLocalDate(): LocalDate
fun toLocalTime(): LocalTime
fun toLocalDateTime(): LocalDateTime
fun toUtcOffset(): UtcOffset
fun toInstantUsingOffset(): Instant
}fun DateTimeComponents.Companion.Format(
block: DateTimeFormatBuilder.WithDateTimeComponents.() -> Unit
): DateTimeFormat<DateTimeComponents>
// Builder interface for complex datetime formatting
interface DateTimeFormatBuilder.WithDateTimeComponents :
DateTimeFormatBuilder.WithDate,
DateTimeFormatBuilder.WithTime,
DateTimeFormatBuilder.WithUtcOffset {
// Timezone formatting
fun timeZoneId()
// Optional sections
fun optional(vararg format: String, block: DateTimeFormatBuilder.WithDateTimeComponents.() -> Unit)
}object DateTimeComponents.Formats {
// ISO 8601 with offset: "2023-12-25T14:30:00+01:00"
val ISO_DATE_TIME_OFFSET: DateTimeFormat<DateTimeComponents>
// RFC 1123: "Mon, 25 Dec 2023 14:30:00 GMT"
val RFC_1123: DateTimeFormat<DateTimeComponents>
}enum class Padding {
// No padding during formatting, optional during parsing
NONE,
// Pad with zeros during formatting, required during parsing
ZERO,
// Pad with spaces during formatting, required during parsing
SPACE
}// Custom date format: "25/12/2023"
val customDateFormat = LocalDate.Format {
dayOfMonth(Padding.ZERO)
char('/')
monthNumber(Padding.ZERO)
char('/')
year()
}
val date = LocalDate(2023, 12, 25)
val formatted = date.format(customDateFormat) // "25/12/2023"
val parsed = LocalDate.parse("01/01/2024", customDateFormat)// 12-hour format with AM/PM: "2:30 PM"
val twelveHourFormat = LocalTime.Format {
amPmHour(Padding.NONE)
char(':')
minute(Padding.ZERO)
char(' ')
amPmMarker("AM", "PM")
}
val time = LocalTime(14, 30)
val formatted = time.format(twelveHourFormat) // "2:30 PM"// European style with timezone: "25. December 2023, 14:30 CET"
val europeanFormat = DateTimeComponents.Format {
dayOfMonth(Padding.NONE)
chars(". ")
monthName(MonthNames.ENGLISH_FULL)
char(' ')
year()
chars(", ")
hour(Padding.ZERO)
char(':')
minute(Padding.ZERO)
char(' ')
timeZoneId()
}// Using ISO formats
val date = LocalDate(2023, 12, 25)
val isoDate = date.format(LocalDate.Formats.ISO) // "2023-12-25"
val dateTime = LocalDateTime(2023, 12, 25, 14, 30, 45, 123_456_789)
val isoDateTime = dateTime.format(LocalDateTime.Formats.ISO) // "2023-12-25T14:30:45.123456789"
// Parse using predefined formats
val parsedDate = LocalDate.parse("2023-12-25") // Uses ISO format by default
val parsedDateTime = LocalDateTime.parse("2023-12-25T14:30:45", LocalDateTime.Formats.ISO)// Parse complex datetime string with timezone
val complexFormat = DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET
val components = DateTimeComponents.parse("2023-12-25T14:30:00+01:00", complexFormat)
// Extract different representations
val instant = components.toInstantUsingOffset()
val localDateTime = components.toLocalDateTime()
val offset = components.toUtcOffset()
// Access individual components
val year = components.year // 2023
val timeZoneId = components.timeZoneId // null (offset-based, not zone-based)// Accept multiple date formats during parsing
val flexibleDateFormat = LocalDate.Format {
year()
alternativeParsing {
char('-')
monthNumber(Padding.ZERO)
char('-')
dayOfMonth(Padding.ZERO)
}
alternativeParsing {
char('/')
monthNumber(Padding.ZERO)
char('/')
dayOfMonth(Padding.ZERO)
}
}
// Can parse both "2023-12-25" and "2023/12/25"
val date1 = LocalDate.parse("2023-12-25", flexibleDateFormat)
val date2 = LocalDate.parse("2023/12/25", flexibleDateFormat)// Safe parsing with null return
val maybeDate = LocalDate.parseOrNull("invalid-date") // returns null
// Exception-based parsing
try {
val date = LocalDate.parse("invalid-date") // throws DateTimeFormatException
} catch (e: DateTimeFormatException) {
println("Failed to parse date: ${e.message}")
}// Parse Instant with custom DateTimeComponents format
val instantFormat = DateTimeComponents.Format {
year()
char('-')
monthNumber(Padding.ZERO)
char('-')
dayOfMonth(Padding.ZERO)
char(' ')
hour(Padding.ZERO)
char(':')
minute(Padding.ZERO)
char(':')
second(Padding.ZERO)
char(' ')
offset(UtcOffsetFormat.FOUR_DIGITS)
}
val instant = Instant.parse("2023-12-25 14:30:00 +0100", instantFormat)Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-datetime-jvm