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

Itai Ferber iferber at apple.com
Fri Sep 8 16:11:06 CDT 2017

Hi Pitiphong,

Thanks for taking the time and energy to pitch this, too! If we can find 
a good solution for matching this up with ISO 8601, and we have high 
demand for this feature, I think it will be worth reconsidering again in 
the future.
Thanks for the input!

— Itai

On 8 Sep 2017, at 13:00, Pitiphong Phongpattranont wrote:

> 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/20170908/705d4715/attachment.html>

More information about the swift-evolution mailing list