SE-0329: Clock, Instant, Date, and Duration

Yes, we could fix the definition of .hours to correspond exactly to SI hours, but there are other definitions of "hour" (such as the UTC hour) which do not have a fixed number of seconds.

It's not immediately apparent from the name .hours which definition is being used. I believe someone in the pitch thread mentioned the possibility of calling these siHours and siMinutes or similar.

1 Like

A great demonstration of how writing out 3 * 3600 seconds unambiguously shows what you mean, as compared to explaining which definition of the hour you’re using by reference to a table that tells you to multiply by 3600!

4 Likes

I'm sorry to insist, but I still don't understand why astronomical hours are relevant in the conversation.

If I'm using a WallTime clock to get a time wt1, and a monotonic clock to get a time mt1' where wt1 == mt1 (there are taken simultaneously).

If I ask for a time wt2 = wt1 + .hours(3) and a time mt2 = mt1 + .hours(3), they both will represent a time that will be reached after 10800 seconds (especially if we put aside the fact that wall time clock may hide leap seconds).

As we are talking about an API to manipulate duration, I still don't see why leap second should be taken into consideration. For instance, the fact that wt1 may correspond to 23:00:00 and wt2 to 01:59:59 is irrelevant to the Clock API. The elapse time is still 3 hours.

Manipulating astronomical time (leap second, day, month, years, …) is the goal of a Calendar API and as long as you don't make assumption about what astronomical time represent a timestamp, you shouldn't care about leap second, or any other astronomical time oddity.

What is a concrete use case of the Clock API where leap seconds are relevant ?

3 Likes

Three hours after 9pm is midnight, by any definition of “three hours” suitable for general use. If it is 9pm and I ask for clock.now + .hours(3) and it’s not then midnight, then I have been misled and it’s the API’s fault for misleading me. It’s as simple as that.

Put another way, I contend that the only definition of “hours” widely understood enough to be vended by a general-use library are calendrical hours. Since those cannot be vended by the standard library, none should be. The non-SI unit approved for use with SI units that is equivalent to 3600 SI seconds can be spelled out unambiguously as a multiple of 3600.

2 Likes

I understand your point, but 9pm is not a timestamp, this is a calendar time and so is not cover by the Clock API.

And hopefully, it will not be possible to add a Duration to a calendar time once we got such API.

2 Likes

And I’m saying that “hours” is a calendar duration that should not be covered by these clock/duration APIs.

Because while “9pm” is not expressible with these APIs, “now” is, and there are many use cases where it will be natural to want 3 calendar hours or 7 calendar days from “now” (for example, a timer app). If the standard library vends an API that calls something “hours” or “days,” it will be used incorrectly for this purpose.

2 Likes

I expect most people would answer the question "how many seconds does a minute have?" with "sixty" right away — and if someone hesitates, it's probably because they suspect that there is a hidden trap, as the answer seems to be too obvious.

However, don't think it is worth arguing much about hours and minutes:
There's no huge problem with them, but not having those shortcuts wouldn't be terrible either.

As even a second can be interpreted in different ways, my preference would actually be leaving out all or most units for duration and time at this level.
This would not only emphasize that durations might be clock-specific, but also make it less awkward to define "clocks" which don't count seconds at all (which might be exotic, but do we already know all ideas people will come up with in the future?).

2 Likes

Possibly worth noting that .Net’s TimeStamp and Java’s Duration types both offer hours and days (as 60 minutes and 24 hours, respectively). I think there would be less confusion adding .hours(3) to an Instant than there would be to Date, so maybe they should only be added if that name is used when the type is lowered? In any case, if hours and days aren’t added to the API, it’s a straightforward thing to add as an extension.

The fundamental problem is that there are 2 things meant by hours/days: calendrical/NSDateComponents (where they represent a variable number of SI seconds) and Duration where they represent a fixed number of seconds. It’s the difference between ‘a time 3 hours later’ and ‘in 3 hours’.

I mentioned this over in the pitch thread, but all of this is lending credence to the idea that Duration should be an associated type on Clock, so that each clock can decide what kind of durations are appropriate for it. Some clocks can only realistically support seconds. Some can support minutes and hours. Others might measure things completely differently (runloop turns? app activations? something else?).

10 Likes

I started out agreeing with you, but the more you argue your position the less convinced I get, and now I don’t agree at all.

If I say that something will happen “in three hours” or “three hours from now”, I mean 10800 seconds from now (with some contextual margin of error). This applies even if there is DST change during those hours, or if I travel across a time zone boundary.

If I set an alarm on my oven for three hours, and a DST change happens, I certainly expect it to go off after 10800 seconds. The same applies if I set a timer on my phone – I would file a grumpy bug report if it somehow adjusted my cooking time for DST.

Similarly, in an ideal world I would expect a three-hour wall-time clock timer across a leap second to fire after 10800 SI seconds. In reality I know this may be hard to achieve, since the underlying system clock may double-count the leap second or use clock skewing to smear it out over a day or two, but that’s my baseline expectation based on my uses of clocks and timers, both in software and in meatspace.

In short, I don’t believe that minutes or hours cause any additional confusion here, beyond the confusion of what seconds really means to various clocks. I would be wary of including days, though. At that point I do think the calenderical interpretation dominates.

7 Likes

But the edge cases are what we need to worry about. Let’s say your long-running service runs at 3 hour intervals, and schedules its next task for 3 hours from now. If today happens to contain a leap second, the next task could check the current calendar date, see that it’s still “yesterday”, and skip its work.

The larger the granularity offered in the API, the more enticing it is to write bugs like this. But I think the real fix is @davedelong’s suggestion of leveraging the type system to discourage mixing elapsed time from calendrical time.

1 Like

That’s no “but”; that’s a completely separate argument from the one I’m questioning.

I don't buy this scenario as presenting a good argument against including .hours(_:). If someone wants to schedule a task every 3 hours, they're going to run into this highly-speculative same-day case no matter how they form that 3 hour duration. The only way to avoid such a bug is the same no matter what API is on the Date/Instant type: namely, by asking a calendrical API.

In short, excluding .minutes(_:) and .hours(_:) does nothing to prevent your hypothetical bug, nor does the omission steer the developer towards the correct solution.

Alright, folks, I'm not sure this review thread is the most appropriate place for this argument. Your feedback on this point will be considered; if you'd like to continue this detailed discussion, it should go in a dedicated thread.

3 Likes

Hello John,

You may be 102.4% correct and it is in your power to say this, but could you please elaborate on why you feel this is not the appropriate place for this argument. I think it will be helpful to see the line next time in that case or knowing on what grounds people may disagree but accept.

All of this stuff with leap seconds and the differences between the Mac/Linux/Windows clocks is IMO a stronger argument for calling this SystemClock.

It may be somewhat unsatisfying that we can't define the clock's behaviour beyond "what the system clock happens to do", but it doesn't seem that there is a way for us to define our own wall clock, with whichever behaviour we think is best for Swift, and which we can implement consistently on all platforms.

It seems that governments have been putting forward proposals to abolish the leap second for decades, but people lose interest and nothing gets done. When the ITU actually held a survey about abolishing leap seconds at the end of 2011, they "received 16 responses from the 192 Member States with 13 being in favor of change, 3 being contrary." The low response rate should tell you something, as should the clear balance in favour of abolishing the leap second. But rather than make a decision, it was postponed to 2015, and then again to 2023.

It's unfortunately not uncommon for that kind of thing to happen when dealing with very technical details like this, and major corporations that would otherwise be pushing for these matters to be resolved end up inventing their own workarounds because it's easier to get things done.

Maybe we can just ignore it, and in another couple of decades it will just sort itself out? Or maybe I'm just starting to sound like the ITU :face_with_hand_over_mouth:

3 Likes

Why should we limit us to a single definition for hour. Wouldn't it be possible to make .hour behaves as it should depending the type of computation you are doing.

When using Duration, hour will be a fixed interval of 10800 seconds.

When using Calendar API, it will represent a date component and behaves as expected by users.

var t: Timestamp = Clock.now + .hours(3) // add 10800 seconds 
var c: CalendarTime = CalendarTime(year: 2015, month: .june, day: 30, hour: 21) + .hours(3) // returns "2015-07-01 00:00:00"

// If we encourage users to works with seconds instead:
var t: Timestamp = Clock.now + .seconds(10800) // add 10800 seconds 
var c: CalendarTime = CalendarTime(year: 2015, month: .june, day: 30, hour: 21) + .seconds(10800) // returns "2015-06-30 23:59:60"
3 Likes

Broadly, review threads are not discussion threads. It’s fine to have short discussions that help clarify feedback, but long debates should go in threads dedicated to those issues.

9 Likes

SE-0329 has been returned for revision. We'll be restarting discussion of the proposal after the U.S. holiday of Thanksgiving.

5 Likes
Terms of Service

Privacy Policy

Cookie Policy