Skip to content

Update the proposal for UTCClock to reflect the discussion surrounding it #1445

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 21 additions & 20 deletions Proposals/0027-UTCClock.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,37 @@ All three of the aforementioned clocks all have a concept of a starting point of

In short, a new clock will be added: `UTCClock`. This clock will have its `Instant` type defined as `Date`. There will also be affordances added for calculations that account for the edge cases of [leap seconds](https://en.wikipedia.org/wiki/Leap_second) (which currently `Date` on its own does not currently offer any sort of mechanism either on itself or via `Calendar`). `Date` has facilities for expressing a starting point from an epoch, however that mechanism is not shared to `ContinuousClock.Instant` or `SuspendingClock.Instant`. All three types will have an added new static property for fetching the `epoch` - and it is suggested that any adopters of `InstantProtocol` should add a new property to their types to match this convention where it fits.

Usage of this `UTCClock` can be illustrated by awaiting to perform a task at a given time of day. This has a number of interesting wrinkles that the `SuspendingClock` and `ContinousClock` wouldn't be able to handle. Example cases include where the deadline might be beyond a daylight savings time. Since `Date` directly interacts with `Calendar` it then ensures appropriate handling of these edges and is able to respect the user's settings and localization.
Usage of this `UTCClock` can be illustrated by awaiting to perform a task at a given coordinated time. This has a number of interesting wrinkles that the `SuspendingClock` and `ContinousClock` wouldn't be able to handle. Example usages include server side coordination between machines or processes to execute work at a given time. In the following example a server workload is scheduled to synchronize at 18:00 every Sunday.

```swift
let calendar = Calendar.current
var when = calendar.dateComponents([.day, .month, .year], from: .now)
when.day = when.day.map { $0 + 1 }
when.hour = 8

if let tomorrowMorning8AM = calendar.date(from: when) {
try await UTCClock().sleep(until: tomorrowMorning8AM)
playAlarmSound()
func synchronization() async throws {
while true {
let nextSunday = DateComponents(hour: 18, minute: 0, weekday: 1)
if let when = Calendar(identifier: .iso8601).nextDate(after: Date(), matching: nextSunday, matchingPolicy: .nextTime) {
try await UTCClock().sleep(until: when)
try await synchronize()
}
}
}
```

This can be used not only for alarms, but also scheduled maintenance routines or other such chronological tasks. The applications for which span from mobile to desktop to server environments and have a wide but specific set of use cases. It is worth noting that this new type should not be viewed as a replacement since those others have key functionality for representing behavior where the concept of time would be inappropriate to be non-monotonic.
The applications for which span from mobile to desktop to server environments and have a wide but specific set of use cases. It is worth noting that this new type should not be viewed as a replacement to the SuspendingClock or ContinuousClock, since those others have key functionality for representing behavior where the concept of time would be inappropriate to be non-monotonic. Further construction around the Date and the behavior after the suspension can accommodate for other uses and it can be a part of a composition to offer a civil/human alarm system - however that concept is well beyond the scope of the UTCClock itself (and this proposal).


## Detailed design

These additions can be broken down into three categories; the `UTCClock` definition, the conformance of `Date` to `InstantProtocol`, and the extensions for vending epochs.
These additions can be broken down into three categories; the `UTCClock` definition, the conformance of `Date` to `InstantProtocol`, and the extensions for vending epochs. All of these will live along side Date in the Foundation essentials module.

The structure of the `UTCClock` is trivially sendable since it houses no specific state and has the defined typealias of its `Instant` as `Date`. The minimum feasible resolution of `Date` is 1 nanosecond (however that may vary from platform to platform where Foundation is implemented).
The structure of the `UTCClock` is trivially sendable since it houses no specific state and has the defined typealias of its `Instant` as `Date`. The minimum feasible resolution of `Date` is 1 nanosecond. The sleep method may respond to host system time adjustments; e.g. if a system has it's time changed manually or if the system adjusts the time drift via ntp updates so the sleep method, unlike ContinuousClock, is not strictly monotonic. However, since UTC itself is the root definition of timezones[^timezones] there is no daylight savings time adjustment; any calculation for accounting to DST must be done before hand by interacting with the Calendar and deriving a Date accordingly.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The structure of the `UTCClock` is trivially sendable since it houses no specific state and has the defined typealias of its `Instant` as `Date`. The minimum feasible resolution of `Date` is 1 nanosecond. The sleep method may respond to host system time adjustments; e.g. if a system has it's time changed manually or if the system adjusts the time drift via ntp updates so the sleep method, unlike ContinuousClock, is not strictly monotonic. However, since UTC itself is the root definition of timezones[^timezones] there is no daylight savings time adjustment; any calculation for accounting to DST must be done before hand by interacting with the Calendar and deriving a Date accordingly.
The structure of the `UTCClock` is trivially sendable since it houses no specific state and has the defined typealias of its `Instant` as `Date`. The minimum feasible resolution of `Date` is 1 nanosecond. The sleep method may respond to host system time adjustments; e.g. if a system has its time changed manually or if the system adjusts the time drift via ntp updates so the sleep method, unlike ContinuousClock, is not strictly monotonic. However, since UTC itself is the root definition of timezones[^timezones] there is no daylight savings time adjustment; any calculation for accounting to DST must be done before hand by interacting with the Calendar and deriving a Date accordingly.


```swift
@available(FoundationPreview 6.2, *)
@available(FoundationPreview 6.3, *)
public struct UTCClock: Sendable {
public typealias Instant = Date
public init()
}

@available(FoundationPreview 6.2, *)
@available(FoundationPreview 6.3, *)
extension UTCClock: Clock {
public func sleep(until deadline: Date, tolerance: Duration? = nil) async throws
public var now: Date { get }
Expand All @@ -64,13 +64,13 @@ extension UTCClock: Clock {
The extension of `Date` conforms it to `InstantProtocol` and adds one addition "near miss" of the protocol as an additional function that in practice feels like a default parameter. This `duration(to:includingLeapSeconds:)` function provides the calculation of the duration from one point in time to another and calculates if the span between the two points includes a leap second or not. This calculation can be used for historical astronomical data since the irregularity of the rotation causes variation in the observed solar time. Those points are historically fixed and are a known series of events at specific dates (in UTC)[^utclist].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this part an outdated version of the previous? I don't see a duration(to:includingLeapSeconds:). Should it just be func duration(to other: Date)?

And the functionality to get the leap seconds between two would be covered by public static func leapSeconds(from start: Date, to end: Date) -> Duration below


```swift
@available(FoundationPreview 6.2, *)
@available(FoundationPreview 6.3, *)
extension Date: InstantProtocol {
public func advanced(by duration: Duration) -> Date
public func duration(to other: Date) -> Duration
}

@available(FoundationPreview 6.2, *)
@available(FoundationPreview 6.3, *)
extension Date {
public static func leapSeconds(from start: Date, to end: Date) -> Duration
}
Expand All @@ -88,11 +88,11 @@ print(start.duration(to: end) + leaps) // prints 1451692827.0 seconds

It is worth noting that the usages of leap seconds for a given range is not a common use in most every-day computing; this is intended for special cases where data-sets or historical leap second including durations are strictly needed. The general usages should only require the normal `duration(to:)` api without adding any additional values. Documentation of this function will reflect that more "niche" use case.

An extension to `UTCClock` will be made in `Foundation` for exposing an `systemEpoch` similarly to the properties proposed for the `SuspendingClock` and `ContinousClock`. This epoch will be defined as the `Date(timeIntervalSinceReferenceDate: 0)` which is Jan 1 2001.
An extension to `UTCClock` will be made in `Foundation` for exposing an `systemEpoch` similarly to the properties proposed for the `SuspendingClock` and `ContinousClock`. This epoch will be defined as the `Date(timeIntervalSinceReferenceDate: 0)` which is Jan 1 2001. The naming is set forth by the proposal [SE-0473](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0473-clock-epochs.md) and the system in the name `systemEpoch` refers to Foundation as the system and not the host system since Foundation's premise is to provide a common abstraction to any host platform.

```swift

@available(FoundationPreview 6.2, *)
@available(FoundationPreview 6.3, *)
extension UTCClock {
public static var systemEpoch: Date { get }
}
Expand All @@ -104,10 +104,11 @@ This is a purely additive set of changes.

## Alternatives considered

It was considered to add a protocol joining the epochs as a "EpochInstant" but that offers no real algorithmic advantage worth introducing a new protocol. Specialization of functions should likely just use where clauses to the specific instant or clock types.
It was considered to add a protocol joining the epochs as a "EpochClock" but that offers no real algorithmic advantage worth introducing a new protocol. Specialization of functions should likely just use where clauses to the specific instant or clock types.

It was considered to add a new `Instant` type instead of depending on `Date` however this was rejected since it would mean re-implementing a vast swath of the supporting APIs for `Calendar` and such. The advantage of this is minimal and does not counteract the additional API complexity.
It was considered to add a new `Instant` type instead of depending on `Date` however this was rejected since it would mean re-implementing a vast swath of the supporting APIs for `Calendar` and such. The advantage of this is minimal and does not counteract the additional API complexity. This consideration did delve into the idea of moving Date to a lower scope and also changing it's storage, however due to the complexity of that; it is considered a non-goal of this proposal and squarely in the out of scope territory. The beneifit of scheduling and appropriate calculations with regards to leap seconds are valueable on their own and distinctly seperable tasks to any sort of re-orgnaization of Date et al.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo:

Suggested change
It was considered to add a new `Instant` type instead of depending on `Date` however this was rejected since it would mean re-implementing a vast swath of the supporting APIs for `Calendar` and such. The advantage of this is minimal and does not counteract the additional API complexity. This consideration did delve into the idea of moving Date to a lower scope and also changing it's storage, however due to the complexity of that; it is considered a non-goal of this proposal and squarely in the out of scope territory. The beneifit of scheduling and appropriate calculations with regards to leap seconds are valueable on their own and distinctly seperable tasks to any sort of re-orgnaization of Date et al.
It was considered to add a new `Instant` type instead of depending on `Date` however this was rejected since it would mean re-implementing a vast swath of the supporting APIs for `Calendar` and such. The advantage of this is minimal and does not counteract the additional API complexity. This consideration did delve into the idea of moving Date to a lower scope and also changing its storage, however due to the complexity of that; it is considered a non-goal of this proposal and squarely in the out of scope territory. The benefit of scheduling and appropriate calculations with regards to leap seconds are valuable on their own and distinctly separable tasks to any sort of re-organization of Date et al.


It was considered to add a near-miss overload for `duration(to:)` that had an additional parameter of `includingLeapSeconds` this was dismissed because it was considered to be too confusing and may lead to bugs where that data was not intended to be used.

[^utclist] If there are any updates to the list that will be considered a data table update and require a change to Foundation.
[^timezones] Time zones and daylight savings times are defined as a reference from UTC offsets. For example Pacific Standard Time is defined as UTC -8, and Pacific Daylight Time is defined as UTC -7.