kotlinx-datetime icon indicating copy to clipboard operation
kotlinx-datetime copied to clipboard

DateTimeComponents.Format parse throws an exception when timeZoneId() is UTC

Open LotuxPunk opened this issue 1 year ago • 1 comments

I have this piece of code to handle a Long date (20241016153100) to an ISO8106 date, it works for most of the cases, but if tz is UTC, the id will be Z, and throw an exception.

DateTimeComponents.Format {
    year()
    monthNumber()
    dayOfMonth()
    hour()
    minute()
    second()
    char('|')
    timeZoneId()
}.parse("$date|${(tz.id)}").let {
    buildString {
        append(it.year.toString().padStart(2, '0'))
        append('-')
        append(it.monthNumber.toString().padStart(2, '0'))
        append('-')
        append(it.dayOfMonth.toString().padStart(2, '0'))
        append('T')
        append(it.hour.toString().padStart(2, '0'))
        append(':')
        append(it.minute.toString().padStart(2, '0'))
        append(':')
        append(it.second.toString().padStart(2, '0'))
        append(tz.offsetAt(it.toInstantUsingOffset()).asTimeZone().offset.toString())
    }
}
Errors: position 15: 'Expected timeZoneId but got ', position 0: 'Expected - but got 2', position 0: 'Expected + but got 2'kotlinx.datetime.DateTimeFormatException: Failed to parse value from '20231013000000|Z'

The workaround I have now is to change Z to Europe/London and it seems to works, but it would seems fair that timeZoneId could recognise Z as a valid timeZoneId.

LotuxPunk avatar Oct 16 '24 13:10 LotuxPunk

Would this work instead?

import kotlinx.datetime.*
import kotlinx.datetime.format.*

fun main() {
    val tz = TimeZone.UTC
    val date = "20231013000000"
    val ldt = LocalDateTime.Format {
        year()
        monthNumber()
        dayOfMonth()
        hour()
        minute()
        second()
    }.parse(date)
    val instant = ldt.toInstant(tz)
    println(instant.format(DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET, tz.offsetAt(instant)))
}

Runnable version: https://pl.kotl.in/wJEhu6YbS

  • Why put the timezone into a string just to immediately parse it back?
  • .asTimeZone().offset shouldn't do anything.
  • append(x.toString()) is the same as append(x): see append(value: Any?) in https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/-string-builder/append.html
  • Formatting in ISO 8601 by hand is not needed, we have such formatters already. For Instant, that's DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET.
  • There is no reason to parse DateTimeComponents when all you have is a LocalDateTime.
  • The year is padded to two digits, you probably meant 4. Also, wrong results for negative years.

dkhalanskyjb avatar Oct 16 '24 14:10 dkhalanskyjb