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

Parsing ISO8601 DateTime with time zones formatted as "+0000"

Open nathanscb opened this issue 4 years ago • 17 comments

Parsing a string as an Instant like 2021-08-22T03:32:10+0000 fails due to fixOffsetRepresentation adding :00 to the end of the string.

The underlying Java Time library is able to handle this fine.

nathanscb avatar Aug 25 '21 09:08 nathanscb

In JDK 8, which this library targets to on JVM, java.time does not support parsing such format by default: neither Instant, nor OffsetDateTime can do it. Perhaps it was supported in more recent versions of JDK, in that case we could think how to retrofit this capability to JDK 8 implementation.

ilya-g avatar Aug 27 '21 03:08 ilya-g

@nathanscb, could you please provide a complete snippet where 2021-08-22T03:32:10+0000 is successfully parsed, along with the JVM version? I just checked this on JRE 11 and JRE 16 (OpenJDK), and OffsetDateTime.parse("2021-08-22T03:32:10+0000") fails with Text '2021-08-22T03:32:10+0000' could not be parsed, unparsed text found at index 22, while Instant.parse("2021-08-22T03:32:10+0000") fails with Text '2021-08-22T03:32:10+0000' could not be parsed at index 19. The documentation seems to agree with these results: https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/time/OffsetDateTime.html#parse(java.lang.CharSequence)

dkhalanskyjb avatar Sep 02 '21 13:09 dkhalanskyjb

Hi. This line of code documentation states that Instant can parse such kind of format Instant.kt

merlin-zaraza avatar Oct 11 '21 13:10 merlin-zaraza

@merlin-zaraza , but the link you provided is not to our repository but to something else. In our repository, there's no such line: https://github.com/Kotlin/kotlinx-datetime/blob/master/core/common/src/Instant.kt#L138

dkhalanskyjb avatar Oct 11 '21 13:10 dkhalanskyjb

@merlin-zaraza , but the link you provided is not to our repository but to something else. In our repository, there's no such line: https://github.com/Kotlin/kotlinx-datetime/blob/master/core/common/src/Instant.kt#L138

Oh. My bad. You are right. Seems like this fork contains such format. Can you add this here?

merlin-zaraza avatar Oct 11 '21 14:10 merlin-zaraza

@merlin-zaraza We expect a string in the extended ISO format of complete representation for calendar date and time of day (ISO 8601-1:2019, clause 5.4.2.1) as an input of the Instant.parse function. According to that format, +0000 is not a valid representation of a UTC offset.

ilya-g avatar Oct 13 '21 02:10 ilya-g

@ilya-g That's understandable and it's ok. The question is how to parse +0000 with this library? Java has DateTimeFormatter and DateTimeFormatterBuilder where you can construct custom format. Are there any plans to add it?

merlin-zaraza avatar Oct 13 '21 06:10 merlin-zaraza

@merlin-zaraza Currently there's no way to parse such values because Instant.parse doesn't support this format. Later, when we implement custom formatting and parsing (see #58, #39), you'll be able to parse such values with a custom format pattern.

You can work around it currently by transforming an input value to the format Instant.parse supports, i.e. by inserting a : between hours and minutes in the offset designator part.

ilya-g avatar Oct 14 '21 16:10 ilya-g

I have same issue. fixOffsetRepresentation works for +09, not works for +0900.

tateisu avatar Nov 14 '21 12:11 tateisu

@tateisu sure, but why do you expect it to work for +0900? Who supports this format?

dkhalanskyjb avatar Nov 15 '21 08:11 dkhalanskyjb

After all, I stopped using kotlinx.datetime. It cannot support Android 7-.

tateisu avatar Nov 15 '21 10:11 tateisu

Having looked through the ISO, I see that +HHMM is the basic format, whereas we (like Java) only support the extended ISO format everywhere. We could potentially also support the basic format, but this would probably need to be a part of the overall effort of providing formatting/parsing.

dkhalanskyjb avatar Nov 22 '21 09:11 dkhalanskyjb

@dkhalanskyjb In the basic format, date and time parts are also written without delimiters, e.g. 20210120T231501+0400. I suppose it's not what is needed here.

ilya-g avatar Nov 22 '21 09:11 ilya-g

Good point. So, what is requested here is actually some mix of basic and extended formats.

dkhalanskyjb avatar Nov 22 '21 09:11 dkhalanskyjb

In my case, the api I work with at my company returns date with this format : "2021-01-01T22:00:00.000+0000". I currently need to implement ax expect/actual strategy with kmm to let platform specific libraries parse the date.

jeantuffier avatar Nov 23 '21 11:11 jeantuffier

Having to write expect/actual won't do at all.

        fun Instant.Companion.parseWithBasicOffset(string: String): Instant {
            var lastDigit = string.length
            while (lastDigit > 0 && string[lastDigit - 1].isDigit()) { --lastDigit }
            val digits = string.length - lastDigit // how many digits are there at the end of the string
            if (digits <= 2)
                return parse(string) // no issue in any case
            var newString = string.substring(0, lastDigit + 2)
            lastDigit += 2
            while (lastDigit < string.length) {
                newString += ":" + string.substring(lastDigit, lastDigit + 2)
                lastDigit += 2
            }
            return parse(newString)
        }

As a workaround, define this function in common code. For (somewhat) valid instants, it adds : between every two digits in the offset. For invalid instants, the errors won't look pretty though.

Example:

assertEquals("2019-12-31T06:02:01.010203040Z",
            Instant.parseWithBasicOffset("2020-01-01T00:01:01.010203040+1759").toString())
assertEquals("2019-12-31T06:02:01.010203040Z",
            Instant.parseWithBasicOffset("2020-01-01T00:01:01.010203040+17:59").toString())

dkhalanskyjb avatar Nov 23 '21 12:11 dkhalanskyjb

Thank you @dkhalanskyjb for the snippet.

micbakos avatar Jan 12 '22 14:01 micbakos