swift-foundation icon indicating copy to clipboard operation
swift-foundation copied to clipboard

[SUGGESTION] Support decoding ISO8601 dates with fractional seconds

Open BrentMifsud opened this issue 2 years ago • 12 comments

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.

BrentMifsud avatar Nov 18 '23 16:11 BrentMifsud

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?

itingliu avatar Nov 27 '23 17:11 itingliu

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.

Craz1k0ek avatar Feb 08 '24 08:02 Craz1k0ek

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).

hassila avatar Feb 08 '24 10:02 hassila

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)

Playrom avatar Mar 11 '24 12:03 Playrom

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

itingliu avatar Mar 11 '24 21:03 itingliu

Is a pitch really needed to adopt correct iso8601 behavior? (I can see the need to discuss what is a good implementation though :-)

hassila avatar Mar 11 '24 21:03 hassila

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 avatar Mar 11 '24 21:03 itingliu

@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!

Playrom avatar Mar 11 '24 21:03 Playrom

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.

BrentMifsud avatar Mar 12 '24 15:03 BrentMifsud

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.

image

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 :)

alobaili avatar Jul 14 '24 12:07 alobaili

I’ll take a look at this. Our app has a bunch of legacy date handling code that uses the old formatters.

BrentMifsud avatar Jul 14 '24 18:07 BrentMifsud

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

BrentMifsud avatar Jul 26 '24 09:07 BrentMifsud