[Pitch] Clock, Instant, Date, and Duration

Wait, are your comments re storing a leap second flag directed at the underlying storage of Date as opposed to its semantics and user-facing API?

It would certainly make sense where systems do not offer easy access to TAI time to store Unix time + something extra for leap seconds (for instance, as has been proposed elsewhere, using 1000000000...1999999999 in the nanoseconds field). But this would not have any effect on the semantics of the type such as its comparability.

The point is that it must unambiguously identify time elapsed since a reference point, which it would regardless of the underlying storage. I am trusting that the implementors are fully capable of choosing the underlying representation of elapsed time that makes the most sense while delivering on the stated design.

That kind of discontinuity is common. Leave your device offline for some time so its clock drifts a bit, then let it sync its time again when online and the time will jump, either repeating a couple of instants if in advance or jumping to some point in the future if late.

What I'm not sure here is whether Date is supposed to be the number of seconds since its reference time excluding leap seconds (leap seconds would appear like a time adjustment in that system) or including leap seconds (in which case you need a table of leap seconds to convert to calendar dates, and the result of converting Date to calendar dates in the future could change when leap seconds are added). In a calendar application, you'd probably prefer the former definition of time because it is calendar-stable, but if you're trying to precisely measure time you'll want the later.

In either case, calling this type Date is quite confusing. There probably should be two different clocks for these two concepts, two "date" types, and a way to convert between the two. That would make things conceptually clear.

3 Likes

Let me be very clear, I have no intent on changing to this. It would break existing software changing Date to no longer be Equatable. It would not only be source breaking, but it would also be ABI breaking. But also it is incumbent upon the implementation that instant types must be comparable. This is a hard requirement of the use case for execution (which is the primary goal of these types). There may be more complex model types that are useful, and again reiterating what has been said before, the existence of execution types does not preclude additional model types.

If the cost is that some folks that have a specialized need for tracking leap seconds have to pay the price of a wrapper type; that seems like a fair exchange. It seems quite small in comparison to the number of developers that would gain benefit from the use of comparing wall clock instants.

As numerous folks have chimed in: a representation derived from clock_gettime(CLOCK_REALTIME, ...) is expected as a source of time; which the proposed implementation is, and the existing implementation of Date has an isomorphic source. It also has been suggested numerous times that if you need a clock from CLOCK_TAI that is not hard to do. Furthermore it has been suggested that if you need calendrical based clocks that also is achievable (perhaps a little more complicated).

10 Likes

It's unfortunate Swift can't make a clean break here, as the Date name is rather confusing, especially to users from other languages. Virtually everyone would consider it a human-centric version of time from a calendar, not simply an interval from a reference point, which I believe many other languages refer to as a Timestamp. Many languages separate that representation from what they show to users as a Date (day, month, year), a Time (time within a day), or a DateTime (combination of the two). This leads to a certain impedance mismatch between Swift and other languages, especially those used for backend services. I'd hoped that, as Swift starts adopting native representations of these values, that it would evolve beyond Foundation's historical naming to something more inline with the rest of the industry.

Even if we can't get rid of the unfortunate naming, it would still be great to see some sort of representation for time within a day, as it's entirely missing at the moment. I'm not sure whether it would fit at the level of this proposal though.

25 Likes

DateComponents does exactly that along side Calendar.

That is correct; any calendrical stuff is not in the scope being proposed.

2 Likes

Eh, not really, at least not for the use cases most people have in mind. If I get some backend data that has a "time": "16:42" field, all I want is a parsed value I can format for my users or perform calculations on. I don't need knowledge of a particular day, just that it's within a general 24 hour period, with the ability to see if time ranges overlap and things like that. There really shouldn't be any calendar involved here since these aren't real times. I'll think more about whether this representation needs to interact with the other APIs presented here.

And not just other programming languages, it’s confusing to users of the English language. I would go so far as to say that naming a type Date when it definitely isn’t a date isn’t just confusing – it is actively hindering understanding.

19 Likes

That does need a calendar and timezone to figure out what storable offset that is. 16:42 may not exist in the timezone you are in for a specific day because of a daylight savings transition etc.

2 Likes

It may not exist on a specific day, but I take @Jon_Shier's point that it exists as an abstract concept of a time on any arbitrary day (indeed, we need this abstract concept to even make sense of the statement "16:42 may not exist in the timezone you are in for a specific day" or to say something like "this meeting occurs every day at 10 AM"). Yes, when you try to use this abstract time to fix a specific instant on a particular day you may run into edge cases (and like you note, the particular day may not have such a time), but it doesn't mean that the time is meaningless independent of a day.

Anyway, if such APIs are out-of-scope for this particular proposal we should probably move further discussion about calendrical APIs elsewhere.

4 Likes

This brings up an interesting question...

@Philippe_Hausler can you explain clarify why this proposal involves moving the Date symbol out of Foundation and into the Standard Library?

From what I can see, the main point is to allow WallClock to use it as its associated Instant type, but given that both MonotonicClock and UptimeClock are defining their own instant types, perhaps WallClock could as well, and then Foundation could separately have convenience initializers on Date to accept MonotonicClock.Instant, UptimeClock.Instant, or WallClock.Instant.

10 Likes

Certainly! I will try to be as specific as I can, obviously there are parts of this that are not yet ready to be cited as a specific reason. But hopefully you can get the general ideas here.

There are a number of reasons to consider this. One major reason is that the desire to thin down the requirements for Foundation as a server side framework. Some of the key types that are commonly used (not in the standard library) are URL, UUID, Data and particularly here Date. None of these types require any sort of localization or internationalization on their own (albeit that some ... like Date) do interplay with that system (in this case Calendar). Lowering a type that has little to now dependency graphs means that we have the opportunity to make swift as a full ecosystem on linux less intense when needing to load libraries. Particularly this can provide a way for us to potentially with other types use a basis of refinement and transition; with regards to Date we can remove a long standing granularity loss bug in the process and make the type live at a lower layer.

To what end? Well, the proposal aims to allow a type to represent a wall clock instant. Common usage with existing Darwin frameworks is Date. It is widely used by applications today as a representation of an instant calculated by an offset from a arbitrary point in time. Particularly lowering it allows for the usage directly in the concepts of scheduling sleeps for tasks. That scheduling is crucial for building additional facilities in asynchronous algorithms, for durations for measuring between events but also for setting deadlines for execution. The duration part becomes key for algorithms pertaining to AsyncSequence such as commonly requested items like debouncing or throttling. Deadlines are needed to express algorithms around distributed actors. In specific the issue is that transmitting a monotonic reference point or an uptime reference point at best is not a shared reference point from machine to machine and at worst becomes a security vulnerability. The distributed sense of deadlines need a common and shared reference point in time; i.e. wall clock instants. Otherwise known in the rest of the API stack as Date.

The remaining reasoning has to do with refining a type that with hind-sight might have been approached differently if we knew then (when initially making swift's importer) what we know now. The lesson is that strong types are useful for many reasons. TimeInterval is not that strong of a type - it is just an alias. This becomes an "along-the-way" type fix but allows us to re-think the problem with a type that can be extended in it's own domain instead of applying those extensions on all Double values.

So; we have an existing type that is used in many layers of APIs that are part of the Swift ecosystem. That type represents the same thing that a a wall clock instant does. Using that type in a lower scope gives us a potential start to reducing dependencies for server side swift without incurring additional baggage to the standard library (if we stay focused on what that type represents). It means that existing APIs can interplay with new functionality like distributed actors without needing convert back and forth. We can fix a long standing bug of resolution and future proof the interface such that the storage is sufficient to last. And finally the existing users of Swift APIs won't need to learn a new type and the conversions to the older ones.

Overall the approach with Date being lowered offers a number of VERY attractive qualities. In the end, the benefits from an overall ecosystem standpoint outweigh the few drawbacks that may exist.

It is perhaps a good design concept to avoid surprising experts. I would say that developers using Swift APIs on Darwin are definitely experts; and anyone who has written software targeting SDKs all the way back to the origins of Mac OS X 10.0 are experts with regards to NSDate and subsequently Date in Swift and that knowledge will transfer to the new home for Date in the standard library even if that does cause some level of pedantic naming friction with a re-envisioning of the naming of APIs.

4 Likes

These are good things, but these aren't compelling enough to me to warrant lowering Date, from an ergonomics POV.

We've got a couple of decades of evidence showing that developers continue to be confused about what "Date" means. This is further compounded by The Great Renaming of Foundation Types that dropped the NS prefix on importation, which made Date look even more tempting.

We've got a really interesting opportunity to make a break from the poorly-named calendaring types in Foundation. Can we take advantage of this once-in-a-lifetime chance and not perpetuate the naming mistake of Date, and instead introduce a "more fundamental" type of Timestamp or something?

Then Foundation's naming problems stay Foundation's problems and don't get inflicted on everyone else, even if many of those situations would end up otherwise using Foundation.

35 Likes

I suppose my real objection is to the name "Date". Whether or not the underlying type is lowered or not, I don't really care.

I would be very disappointed though if we were stuck with the name. A "date", by any human understanding, is a concept intrinsically tied to a calendar, and calendars don't exist at the Standard Library level. "An instantaneous point in time" is also pretty far removed from what humans think of as "dates".

This proposal seems like the prime opportunity to revisit this name and I really really hope there's a way we capitalize on it.

26 Likes

I understand your point, but to me the tradeoff is pretty clear.

  • Re-use the existing terminology, which is conceptually compatible with all existing source code, documentation, and the SDK as a whole

or

  • Rename the type but have it mean the same thing, which has a chance of being clearer for people completely new to Apple's SDKs but at the cost of making everyone else choose between two names and forever remember the two things are actually the same

Calendrical calculations and time in general are inherently complicated. The name for Date is just one piece of the puzzle. This proposal advances the status quo in so many other ways:

  • Completely standardizing the right type to use for all of Swift
  • Helping people understand the purpose of Date by making it the Instant of WallClock. This gives it a context in the strong type system that NSDate never had before and with a name for the associated type that helps identify its purpose
  • At the same time, clearly establish the relationship between a clock and a date

So, for me the decision is straightforward. Let's sink Date and keep the name.

3 Likes

So, for me the decision is straightforward. Let's sink Date and keep the name.

I don't write here often so please excuse me if I've missed some things in this thread (I tried to read it all :sweat_smile:), but I do think that it would be a missed opportunity to apply this philosophy. If the Swift project is creating a completely new approach to working with dates, times, clocks, etc, then I would view this as an opportunity to fix something that was semantically incorrect and unclear for a long time, rather than advance the status quo everywhere except for this one piece of the puzzle.

20 Likes

This would still happen if the standard library used Timestamp instead. There wouldn't be a need to delete all of Date's documentation or anything, and some of it could be reused after replacing Date with Timestamp. In the end, the resulting documentation would actually be simpler since users wouldn't immediately be confused by a type named Date used as a timestamp. You could separate that notion from actual calendrical calculations and get Date out of the instant business.

This makes the assumption that Swift will be used with Apple's SDKs. This isn't a great starting position for a language which is increasingly trying to position itself as a cross platform solution appropriate for server development. Many server projects actively try to avoid Foundation, being so large, so this position has no value for those users. And given the rather simpler use of Timestamp I don't think this choice would be as onerous as you think.

I agree with these points in principle but using the Date name actively works against those goals because it would continue to blur the line between an instant on a system clock and the dates developers show to users. Using Date in more places will not solve the ongoing confusion users have with it, only deepen it.

16 Likes

To me, the overriding consideration that tips the scale in the other direction is that "understanding the purpose of Date" has to begin by understanding that it does not represent a date in any conventional sense and discarding all intuitions based on that. It's not merely a name that doesn't explain the type's purpose, it's a name that actively works against a correct understanding.

The preceding dozens of messages show how none of the bullet points above, which are amply explained in the text, are sufficient for readers to discard those intuitions even after much back and forth, and needless to say this state of affairs is impossible to reconcile with the principle of progressive disclosure that the proposal cites as one of its guiding lights.

While the idea of sinking the existing type and fixing some weaknesses along the way is very attractive and has many real benefits, one has to acknowledge the empiric evidence that even among involved members of the community it has run into immediate problems of understanding.

And failure to grasp what Date really is will threaten a full understanding of the whole design: the bullet points above cut both ways. By enmeshing Date with Clock and InstantProtocol and standardizing it across all of Swift, if all of those surrounding facilities are insufficient to help users develop a correct understanding of Date, then users' incorrect understanding of Date (surely, it represents a calendrical date) will poison their understanding of all the rest of the design.

33 Likes

according to my crystal ball leap seconds are going away. alternative often considered is "leap hours", although personally i'd just assign a region to a new GMT offset when time difference compared to sundial becomes an hour or so, perhaps on some "round" e.g. century boundaries only. a hypothetical scenario would be (provided daylight saving time is still in use):

- Sunday 25/10/5598 01:00:00 - London switches to UTC+0
- Sunday 28/03/5599 02:00:00 - London switches to UTC+1
- Sunday 31/10/5599 01:00:00 - London switches to UTC+0
- Sunday 26/03/5600 02:00:00 - London stays on UTC+0  ***********
- Sunday 29/10/5600 01:00:00 - London switches to UTC-1
- Sunday 25/03/5601 02:00:00 - London switches to UTC+0
...
(yes that means that greenwich observatory will eventually move to -1, -2, -3 etc compared to UTC).

and we have ~40 centuries to improve on that.

having said that, leap seconds (or leap hours) are relevant to this discussion in the following aspect (hence this message): whether the new "Date" ("Time"? "Timestamp"?) type being discussed is more like "Unix time" (whereas one unix time value might correspond to "two UTC/TAI values" when on a positive leap second and to "no UTC/TAI value" when on a (theoretically possible) negative leap second), or whether it is more like the strictly monotonic / evenly paced TAI value itself. AFAIK different systems answer this question differently. the consequence of this design choice becomes relevant at higher levels (Calendar / DateComponents / DateFormatter). also it affects the implementation of the + operation (Unix time "+" is more involved than normal Integer plus operation, see the "Unix time across midnight into 1 January 1999 (positive leap second)" in the link above).

I hope Swift will be around for a long time and continue to grow, so the people completely new to Swift will vastly outnumber “everyone else”.

20 Likes

I personally consider the Date naming one of the biggest mistakes in Foundation, which is otherwise an excellent framework. Let's save the next generation of Swift programmers from this unnecessary confusion. This is our chance to do better.

Both JavaScript and Java are using a better naming in their modern "date" frameworks: Temporal documentation

26 Likes