-
Notifications
You must be signed in to change notification settings - Fork 542
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
Add CalendarDuration
type
#1282
Comments
I would like to start small, to keep things reviewable:
@djc does this sound reasonable? Of would you like to start with something smaller or larger? |
The natural order to add a What to do if a result does not exist? That can be the case of the date is for example 2023-08-31 and you try to add one month. The result, 2023-09-31, does not exist. Returning 2023-10-01 does not seem right; adding one month to a date in August should not return a date in October. Also returning 2023-09-30 does not seem right, the period in between is less than a month. In those cases we should return The tricky part is: what to do if an intermidiate value does not exist, but there can be a reasonable end result? For example adding one month and one day to 2023-08-31. In that case I find it reasonable to return 2023-10-01, just like how adding two months and one day would return 2023-11-01. Why go through the trouble of supporting non-existing intermediate values? Because otherwise working with a Another tricky part is that we add the |
About parsing ISO 8601 durations with a fractional component:
The terms 'if necessary' and 'may' do not read like we have to support fractions. A couple of fractions make sense to me to support, because there is a common understanding of what they should mean:
Not really sensible:
|
What about signedness? You proposed a type with |
They define it to be unsigned: https://www.iso.org/obp/ui/en/#iso_std_iso_8601-1_ed-1_v1_en_term_3.1.1.8
|
Okay! Do you want to make the fields public of keep them private? It feels like once we have this, we can maybe deprecate the |
I prefer to make them private, like in
Yes, maybe? I won't miss the
I'll do my best. A first PR needs ca. 2 more days to polish. |
Currently I am reading up a bit more on our leap seconds issues. It seems like it really shouldn't be much work to implement a TAI timezone and do correct calculations with leap seconds (but definitely not in the first PR). As I see it now we can prepare the type to support working with them. It should encode whether the duration is expressed with an accuracy in hours and/or minutes, or in seconds. With an accuracy in minutes the expectation is probably that we paper over leap seconds. With an accuracy in seconds the expectation is probably that passing seconds are accurately counted, including leap seconds. In any case it is a property of the duration how to deal with leap seconds. Of course the type should have a method to override this. |
Continuing the discussion about durations from #954 (comment)
First of all, I appreciate the effort of adding My goal would be for Here are two scenarios I described in the linked issue. There was a leap second on December 31st 2016. The current time is 22:00 UTC:
I expect the answer to the first scenario to be:
And the answer to the second scenario to be:
For calendar durations, I expect them to be applied as follow:
This feels to me like a natural interpretation of calendar durations; but it brings its fair share of edge cases.
I do not have access to ISO 8601 (I think it's not freely available?) but based on the simple scenario described above, I think that you can't convert hours into seconds but have to keep an explicit The main concerns I have are discontinuities around leap seconds and DST, situations such as "January 30th + 1 month", or if the order of application is important or not (is adding calendar durations commutative?). I also know that chrono durations are way easier to handle than std durations because of their signedness. I'm not sure about calendar duration, but I would still consider having a sign. EDIT The |
Your comments made me realize the situation with leap seconds is different from days that are not always 24h, months that differ in length, and leap years. For those three it is just a question of: in what unit do you want to count in? For leap seconds we have a second question: does a leap second exist for you? For many devices and thus many people leap seconds don't exist. Watches, analogue clocks and most digital clocks don't have the concept. They just don't have enough accuracy to be synced to within a fraction of a second with UTC, and they don't need to. Historical seconds with your definition. And the same is true for most computers. And if you do want to acknowledge leap seconds, for most software that means pretending because they don't have a clock source that can return a leap second. We should strive for correctness, but also not forget this reality exists. So we have three cases:
I am not sure where to go with this. It just corrected my thinking. Combinations of hours, minutes and secondsIt seems to me it is best to carve out two reasonable modes to specify the accurate components of a duration:
This is kind of allowed by ISO 8601. The choice for maximum number of digits for a component, and by extension the maximum value, is left up to mutual agreement (4.4.3.2):
Instead of this proposal of having two modes we could allow any arbitrary amount of minutes and seconds. That may even be a little easier to do. A second advantage is that it keeps the size of the type smaller. In one |
Beyond the definitions that are freely available, it does not do much more than specify how to format the values as a string.
You are right. We should at least have separate
Discontinuities are the tricky part. #1282 (comment) describes most of how I plan to work with them. That does not describe leap seconds yet. As long as we know the type is capable, I think it is best to implement the basic operations without support first and slowly grow the complexity.
It is important. If you add a duration with the highest order components first, the inverse would be to subtract with the lowest order components first. And even then I am pretty sure that we can't guarantee it will always round-trip. I hope we don't need to implement
This stuff is turning out somewhat tricky with all the edge cases. Throwing signedness into the mix is currently too much for me 🤣. And we would loose the ability to always format a duration with the ISO 8601 format.
The idea is to make For consistency we may have to do the same for |
Creating a duration from the difference between two datesWe should have methods to create a impl NaiveDate {
fn diff(&self, other: NaiveDate, date_components: DateComponents);
}
impl NaiveDateTime {
fn diff(&self, other: NaiveDateTime, date_components: DateComponents, time_components: TimeComponents);
}
impl<Tz: TimeZone> DateTime<Tz> {
fn diff(&self, other: DateTime<Tz>, date_components: DateComponents, time_components: TimeComponents);
}
enum DateComponents {
YearsDays,
MonthsDays,
Days,
None, // defaults to `TimeComponents::Minutes` if used with `NaiveDate`
}
enum TimeComponents {
Minutes,
Seconds,
} Again the order of operations is important. To quote from #1247
I have not thought much about calculating the difference between two |
I'm open in principle to accomodate for the use cases of those who care very much about leap seconds, but one rule that I think makes sense is that the API for those who don't care (who are in the majority by far) probably shouldn't be allowed to suffer. |
ISO 8601 specifies two ways to serialize a duration: a default format using designators, and a more familiar format similar to dates and times. Examples:
I don't think the second syntax is great. But the designator format is quite alien until you get to know it compared to the regular time formats. In some cases, especially the time-only ones, there is something to say for the second format. Worth noting that the alternate format is not as flexible als the designator format. It can't express hours ≥ 24, days (within a month) ≥ 31, and similar values beyond their carry-over point to the next unit. If we have ISO 8061 date and time parsers the alternate format is easy to add.
|
The goal is to add a type that can describe durations with the flexibility of an ISO 8601 duration. Because we already have enough confusion with types named
Duration
, I proposeCalendarDuration
as name.It will have components that can describe a 'nominal duration' (to use the terms of ISO 8601), and an 'accurate duration'.
ISO 8601 describes more possible components: years, weeks, hours and minutes. But these can all be expressed as a multiple of the components proposed above: years as 12 months, weeks as 7 days, hours as 3600 seconds, and minutes as 60 seconds.
ISO 8601 does not have a nanosecond component, but allows the last smallest component to have fractions. So we can map a fractions of a second to
nanos
.In theory a minute may not be always equal to 60 seconds, in the presence of a leap second. We currently can't do calculations with leap seconds. But if/when we do, this type may need a little revising.
Checklist for implementation:
CalendarDuration
typeFrom
Day
,Month
,std::time::Duration
CalendarDuration
Display
implementationdiff_*
methods onNaiveDate
,NaiveDateTime
andDateTime
to create aCalendarDuration
add_calendar_duration
methods onNaiveDate
,NaiveDateTime
andDateTime
sub_calendar_duration
methods onNaiveDate
,NaiveDateTime
andDateTime
The text was updated successfully, but these errors were encountered: