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:
1. 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
2. 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 `Date`s. 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 <[email protected]> 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
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution