These already is an API on Calendar for this: date(byAdding:value:to:wrappingComponents:) | Apple Developer Documentation
that’s a Foundation API, but yes, i think it is an excellent model for a standard library calendar API.
The presence of .days() etc was brought up in the original Clock pitch and was deliberately removed from the pitch. It could certainly be re-added (and there are some decent ideas suggested in this thread), but it would need to address the concerns brought up in the original thread around their inclusion. For example:
-
The name
.days()is bad. A "day" is technically a duration, but it's a duration that is derived from the calendar against which a "day" is being measured, and Foundation supports many different kinds of calendars. Therefore,.days(1)can represent different absolute durations depending on which calendar you choose to use. (As a dumb example, days on Mars are longer than days on Earth) -
Even when using a same calendar, a "day" is still a variable-length duration. Sometimes days are 23 hours, usually they're 24, and sometimes they're 25. Depending on what you're defining a "day" as, a "day" can technically be as long as 50 hours.
-
"Weeks" are conventionally 7 days long, but there have been historical variations, and there's no guarantee future calendars will also use 7 day weeks.
Can we work around these issues by picking new names? Sure, we could probably use .siDays() and .siWeeks() to mean durations of 86,400 SI seconds and 604,800 SI seconds respectively, but remember that these are all different kinds of clocks. Does it make sense to advance a ContinuousClock by 3 SI days? Is that the same expected duration as advancing a SuspendingClock? What if I define a custom Clock that performs Calendar-based operations; should .siDays(1) still mean 86,400 seconds, or should it mean "one calendrical day of variable length"?
We quickly run in to issues around expectations when we introduce calendrical concepts onto a type that does not natively support calendars. Recall that the original pitch around Clock deliberately avoided calendars as part of the base proposal, but instead left that up to be determined by the conforming type.
Date() + 1 day (which is 24x3600 sec, unless we all move to Mars) has its legitimate uses, e.g. if I run an experiment that lasts exactly 7 days I want to have an alarm that goes off in 7x24x3600 seconds and I won't appreciate OS doing leap second adjustments or daylight saving adjustments, or time zone adjustments if I happen to carry that experiment while on a transiberian train moving across different time zones. Or if I run 10 meter per second it is 36 km/hour even if I do this around 1AM last Sunday of March in the country that observes DL saving switch, and if I manage to keep travelling at that exact speed for a day I'd cover 864 km.
I think the discussion around days/hours/minutes has clear ambiguity and treads into the mire of political and religious impact. Given two date-components we might be able to derive an accurate duration (since those components could carry calendrical information). Likewise we could derive another set of date-components given a date-components and a given duration.
It has been listed so far with responses here (and to the original pitch/proposal) there is ambiguity around what it means past a given resolution of time. Day could mean 24 hours (defined as 86400 seconds) or day could mean until the next sunrise, or to the next sunset, or until the calendrical identification of which day it is changes.
None of those ambiguities in my opinion should detract from the original pitch. It is a clear improvement.
Localization and internationalization are clearly complex subjects and should definitely be part of a more correct and comprehensive system. Doing so needs to have clear outlines of what is safe and what is both culturally and mathematically accurate.
The original boundary of seconds was chosen because that is relatively un-ambiguous. Any duration that is used for measurement as listed is useful to measure the accurate time in terms of seconds. Once a program has a lifetime as long as one would measure days then it is reasonable to consider more complex systems to get that right.
In short; it seems reasonable to accept the pitch as stated and develop minutes/hours/days/weeks as something on its own.
Foundation's format style already has:
-
weeks
The unit for weeks. One week is always 604800 seconds. -
days
The unit for days. One day is always 86400 seconds.
import Foundation
Duration.seconds(31 * 24 * 60 * 60).formatted(
.units(
allowed: [
.weeks,
.days,
.hours,
.minutes,
.seconds,
.milliseconds,
.microseconds,
.nanoseconds,
],
width: .wide
)
) //-> "4 weeks, 3 days"
The default uses an hour-minute-second pattern, which doesn't include fractional seconds.
import Foundation
Duration.milliseconds(500).formatted() //-> "0:00:00"