kotlinx-datetime
kotlinx-datetime copied to clipboard
Parsing ISO8601 DateTime with time zones formatted as "+0000"
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.
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.
@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)
Hi. This line of code documentation states that Instant can parse such kind of format Instant.kt
@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
@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 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 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 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.
I have same issue. fixOffsetRepresentation works for +09, not works for +0900.
@tateisu sure, but why do you expect it to work for +0900? Who supports this format?
After all, I stopped using kotlinx.datetime. It cannot support Android 7-.
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 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.
Good point. So, what is requested here is actually some mix of basic and extended formats.
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.
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())
Thank you @dkhalanskyjb for the snippet.