[swift-evolution] [Pitch] DateComponents{Encoding/Decoding}Strategy in JSON{Encoder/Decoder}

Pitiphong Phongpattranont pitiphong.p at me.com
Fri Sep 8 15:00:40 CDT 2017


Hi Itai,

As I told you in my last email that I’m thinking about the ISO 8601 case. After thinking about that, having a discussion in the Swift Evolution and reading your emails, I think it may not worth to add this into Swift Standard Library. I think the use case is not that much so it’s not worth the cost of maintenance alone not to mention or think about how to implement it properly (if we choose to do and support the `iso8601` strategy.

I think I will close this pitch and would like to thank you for reviewing and discussing on this.


— Pitiphong P.

> On 7 Sep BE 2560, at 01:03, Itai Ferber <iferber at apple.com> wrote:
> 
> Hi Pitiphong,
> 
> Don’t worry — your original email was clear, and we are on the same page about Date{En,De}codingStrategy and DateComponents{En,De}codingStrategy being separate things.
> To clarify my points, though, there are two main things I want to say:
> 
> I think there is a mismatch here between your goal of representing the components of a date (and what DateComponents can specifically hold) and the goal of ISO 8601
> I think that there is an inherent problem in parsing DateComponents due to ambiguity
> I think both of these issues can be solved by reading and writing a Date (formatted however you need it to be) instead of DateComponents.
> 
> To elaborate:
> 
> DateComponents is meant to be a container for an arbitrary subset of information about a Date. A Date represents a specific instant in time, but DateComponents are effectively meaningless without additional context. In the examples that you give, it’s possible to represent the concepts at hand with DateComponents, but in order to make those components actionable and meaningful, you still need to convert them to Dates. Note also that:
> 
> It’s entirely possible to create a DateComponents which represents a date which does not exist, or a time which does not exist
> Any of these concepts can also be represented by a Date instead of just components; e.g., an all-day event can be represented by a Date that represents the beginning of the day (00:00:00) and a flag that indicates that the time of the event can be ignored, or by a start Date that represents the start of the day and and end Date that represents the end of the day
> Unlike DateComponents, ISO 8601 strings have some structure to them. They cannot represent just a time zone, for instance, or some singular components of a date/time (e.g. a month without a year, a day without a month and year, a minute without an hour, a second without a minute and hour, etc.). I think this is a relatively large conceptual mismatch that is worth considering deeply. There are a lot of DateComponents instances which simply cannot be represented by an ISO 8601 string
> 
> There is also the issue of decoding arbitrary ISO 8601 strings into DateComponents. DateComponents, having no structure at all, have no specified format they can expect to decode from, and ISO 8601 does not always provide that structure. Consider the following example:
> 
> ISO 8601 allows for date representations by year, month, and day (YYYY-MM-DD), among other forms. But it also allows days to be left unspecified (YYYY-MM), and even months (YYYY)
> Similarly, it allows for a time representations by hour, minute, and second (hh:mm:ss), but also just hour and minute (hh:mm), and just hour (hh). Importantly, it allows time separators to be omitted (hhmmss, hhmm, hh)
> Consider then, attempting to parse the string "2017" without any context — what DateComponents should be read out? Intuitively, 2017 looks like a year (YYYY), but it is equally valid to parse as the time 20:17 (hhmm). Without knowing the expected format, parsing is ambiguous
> We cannot promise to parse DateComponents in all cases because there are many combinations of strings that are just completely ambiguous.
> 
> So, to get at the core of this — if there is a specific format that you would like to encode to and from, why not do so with a Date and a DateFormatter (or if you need ISO 8601 specifically, ISO8601DateFormatter)? With a formatter, the format is unambiguous because you explicitly provide it, and there is nothing the date can’t represent that DateComponents can. You can always parse the date and pull out only those components that you care about. You also mention interoperability with an external JSON source — how is that source producing a string/parsing one back? [What I’m getting at here is: what is the value of adding a new, potentially risky strategy over existing methods that might work just as well, or better?]
> 
> And lastly, if .iso8601 is not necessarily a good fit for this strategy, what separates .custom from just overriding encode(to:) and init(from:) and writing the components out in the format that you need?
> 
> I think answers to these questions can help us push this forward. :)
> 
> — Itai
> 
> On 5 Sep 2017, at 10:41, Pitiphong Phongpattranont wrote:
> 
> Hi Itai,
> 
> I think my first pitch email was not clear enough and want to sorry for that. I have been working on a calendar app for awhile and understand the concept of calendar or date and time programming in some level. I didn’t pitch the idea of encoding and decoding `Date` value with this `DateComponents{Encoding/Decoding}Strategy`. I still agree that `Date` value should be encoded/decoded with the `Date{Encoding/Decoding}Strategy`. The DateComponents{Encoding/Decoding}Strategy I pitched only apply for `DateComponents` value only.
> 
> About the use case, I think there are some application which store an information of a `Date` value that is not include a time value (A date of September 6th, 2017) for example a calendar app which want to store the Start and End date of an `All Day Event` with a value of DateComponents type or an alarm app which want to store just a time of the day that user want to set an recurring alarm (10:30am.)
> 
> The problem I found with the current implementation is that I have no control on how the DateComponents implement the conformance methods of the Encodable and Decodable protocol. This means that if I have a service that serialize those properties with a difference notation (ISO 8601 in my case) then I cannot rely on the auto synthesized implementation from the compiler and need to do a manual encoding/decoding by manually implement the Encodable and Decodable
> 
> Lastly, on the issue that `ISO8601` standard does not support every components in DateComponents, I still haven’t thought this though and still thinking about it. I want to pitch the idea first and would like to have a discussion/brainstorm on should we do this and how we can do it. My backup plan is doesn’t include the `iso8601` strategy but still have the `custom` strategy for those who need to apply a custom encoding/decoding strategy which will be apply to all values in a payload. Since we encode/decode a JSON from one source at a time and the encoding/decoding strategy of DateComponents of that source should be consistency throughout its types (which may be the types that I own or the types from a 3rd party service), I think this still is a valid use case for providing a custom strategy.
> 
> Thank you
> — Pitiphong P.
> 
> 
>> On 6 Sep BE 2560, at 00:15, Itai Ferber <iferber at apple.com <mailto:iferber at apple.com>> wrote:
>> 
>> Hi Pitiphong,
>> 
>> Thanks for pitching this! My main question here is about the use case. Since encoding/decoding strategies apply to all values in a payload (whether or not those belong to types that you own), they inherently come with some risk.
>> What is the use case in mind for needing to encode and decode DateComponents directly, as opposed to encoding and decoding a Date instance and pulling the components you need from that?
>> 
>> From a correctness standpoint, I also want to point out that DateComponents is really just a "bag of stuff" that doesn’t necessarily mean much until converted into a Date through a Calendar and a TimeZone. There is somewhat of a mismatch between this "bag of stuff" and what ISO 8601 intends to represent — an actual date and time. It’s possible to represent things in a DateComponents that don’t really make sense for (or are not supported by) ISO-8601-formatted dates. For instance, you can have a DateComponents which just has a TimeZone, but ISO 8601 does not allow representing a time zone without a corresponding time. DateComponents also, for instance, has a quarter component (among others) which I’m almost certain ISO 8601 has no equivalent for.
>> 
>> Given that conceptual mismatch, I think we’d need a very compelling use case to support this over simply using Date.
>> 
>> — Itai
>> 
>> On 3 Sep 2017, at 0:55, Pitiphong Phongpattranont via swift-evolution wrote:
>> 
>> Hi folks, I have an idea on improving the JSON{Encoder/Decoder} to pitch.
>> 
>> Since JSON doesn’t have a native representation for `DateComponents` like it doesn’t have for `Date` too so that there’re many ways to represent it in JSON, for example ISO 8601, UNIX timestamp, etc. for Date. There are also a few ways to represent `DateComponents` too, for example ISO 8601 (https://en.wikipedia.org/wiki/ISO_8601 <https://en.wikipedia.org/wiki/ISO_8601>) also describes how to represent some of the valid date components (e.g. "2017-09-03”). Unlike what JSON{Encoder/Decoder} does to represent `Date` value with several strategy but there is no support like that for `DateComponents`.
>> 
>> The current implementation DateComponents is to encode/decode with KeyedContainer and cannot provide a custom or ISO 8601 compatible implementation. So I think JSON{Encoder/Decoder} should have a strategy for encoding/decoding `DateComponents` just like for Date
>> 
>> Here’s an initial `DateComponentsStrategy` strategy that I want JSON{Encoder/Decoder} I can think of now, any suggestion is welcomed.
>> 
>> ```swift
>> /// The strategy to use for encoding `DateComponents` values.
>> public enum DateComponentsStrategy {
>> /// Defer to `Date` for choosing an encoding. This is the default strategy.
>> case deferredToDateComponents
>> 
>> /// Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
>> case iso8601
>> 
>> /// Encode the `Date` as a custom value encoded by the given closure.
>> ///
>> /// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
>> case custom((DateComponents, Encoder) throws -> Void)
>> }
>> ```
>> 
>> What do you guys think about this pitch?
>> 
>> 
>> Pitiphong Phongpattranont
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170909/c1715021/attachment.html>


More information about the swift-evolution mailing list