[SUGGESTION] Support decoding ISO8601 dates with fractional seconds
I know that date formatters have not been implemented here just yet, but I would like to bring light to this issue that has existed in the original foundation for quite some time.
Essentially, by default, ISO8601DateFormatters fail to parse valid ISO8601 dates when there are fractional seconds present. You can fix this by adding the .withFractionalSeconds option. But in some cases, this isn't possible.
For example:
I noticed this issue recently in my own codebase where we were using the .iso8601 JSON date decoding strategy. We got around this by creating a custom date decoding strategy. But it would be nice if we didn't have to do this.
It would be nice to have this fixed in Swift Foundation.
Currently .withFractionalSeconds requires the string to contain fractional seconds, and decoding would fail if it doesn't. Just as a general survey -- Do all strings contain fractional seconds, or some of them do and some of them don't?
Just as a general survey -- Do all strings contain fractional seconds, or some of them do and some of them don't?
This shouldn't affect the implementation, as the ISO 8601 standard clearly defined a way to have reduced precision by omitting values.
For a starter it would be nice if the existing implementation would be renamed to RFC 3339, which it appears to be, and the requested behaviour would be correctly implemented as the ISO 8601 standard.
I'd also like to add that it would be fantastic if the formatter can support precision all the way down to nanoseconds, which is practically useful for some domains (e.g. financial transaction timestamps can have that).
As stated in the RFC https://www.rfc-editor.org/rfc/rfc3339#page-8 , at page 7 , "Internet Date/Time Format"
The fractional part of the second is optional (time-secfrac between square parenthesis). The foundation implementation, when setting "withFractionalSeconds", should correctly parse a string with or without the fractional part
This "forcing" behaviour is creating a lot of headaches In the current foundation library, and I think It would be helpful to have a change (which is additive, not breaking compatibilty) in the new swift-foundation!
I would propose myself a PR but checking the current source code I'm not so confident in writing a code that is fast enough to be placed in this part of the library (ICUFoundation is using pointers and direct string access)
We can't change the behavior of Date.ISO8601FormatStyle or ISO8601DateFormatter so that withFractionalSeconds parses both strings with and without the fractional seconds if the current behavior requires strict matching.
We can consider having a separate option, say, lenientFractionalSecond instead. If anyone has any good ideas about enabling this while being compatible with the existing behavior, I think we'll all be very glad to see a pitch to kick off the discussion
Is a pitch really needed to adopt correct iso8601 behavior? (I can see the need to discuss what is a good implementation though :-)
My point was that we can't simply "fix this bug" by changing the behavior. It's important to maintain compatibility for clients who are relying on the current strict behavior.
So the only option we have is to add a different configuration, which by its nature would be an API, hence the need for a pitch
@itingliu I see your point, you are right Honestly I thought that being this rewrite not "just a port" some changes on the api may be possible, but if strict compatibility is necessary your approach of a lenientFractionalSecond it appears the way to go!
I like the idea of a lenientFractionalSeconds option. I'd like to propose an additional option: .replacingMissingTimeValues
This will automatically populate time, fractional seconds, and time zone with a safe default when constructing a date from a string if those values are missing.
For example if the time zone is missing, use UTC. If fractional seconds are missing, use .000, if time is missing, use 00:00:00.
Obviously you can't really do this for missing year month or day, but it will prevent failures when your date string is missing some of the time values.
I have been dealing with decoding ISO 8601 lately and wanted to point out that the current documentation for ISO8601DateFormatter appears to be indirectly recommending using Date.ISO8601FormatStyle instead, which from my quick experiment appear to have different defaults than the old ISO8601DateFormatter. For example, the old API fails if the time zone separator is not present in the string, while the new API doesn't.
It appears that the new API is part of swift-foundation and, as stated in this Swift forum post, the internal implementation for the .iso8601 JSON decoding strategy is using the new API with its defaults. so maybe this solves the original issue? @BrentMifsud
I think the new API also has better documentation with more sample code :)
I’ll take a look at this. Our app has a bunch of legacy date handling code that uses the old formatters.
The new API seems to work as I expect.
Except oddly enough, found some inconsistencies with RelativeFormatStyle when compared with RelativeDateFormatters
made a separate issue for that here: #774