-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Tracking Issue for duration_constructors #120301
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
Comments
Thanks for adding this context. I was fairly certain that this would have been discussed before, but searching the issue tracker (for open issues) didn't turn up any existing discussion. I think it's okay to have this discussion here/now? From RFC 1040:
It goes on to quote JodaTime documentation; I'll quote the current version of this:
Note that the RFC is from 2015 and the latest discussion in #52556 is from 2018 so I feel like revisiting this decision isn't all that crazy. Personally I feel this distinction is well-established in the Rust ecosystem at this point, and there's a clear interplay between IMO each of these constructors have a clear unambiguous meaning in the context of a Note: I happen to be the current chrono maintainer, so I have some background in these things. |
We may want to consider as part of stabilization whether these could "just" (or in addition should be) associated constants. e.g., |
…Simulacrum core: add Duration constructors Add more `Duration` constructors. Tracking issue: rust-lang#120301. These match similar convenience constructors available on both `chrono::Duration` and `time::Duration`. What's the best ordering for these with respect to the existing constructors?
Rollup merge of rust-lang#120307 - djc:duration-constructors, r=Mark-Simulacrum core: add Duration constructors Add more `Duration` constructors. Tracking issue: rust-lang#120301. These match similar convenience constructors available on both `chrono::Duration` and `time::Duration`. What's the best ordering for these with respect to the existing constructors?
I'm happy to add them, but I'd be disappointed to remove the new constructors in favor of these. |
Just thought about this and found this issue :) I prefer the constructors to associated constants. Have you considered making the constructors take u32 instead of u64, so they don't need to worry about overflow and hence panicking? You lose the option of making a duration longer than (PS: I think the panic message in |
Yup. That looks like a classic case of copy-paste error. Also
is rather confusing.
|
@Mark-Simulacrum @djc I have attempted to add these associated constants in an idiomatic manner: #127700 I would love your feedback! |
I notice that it isn't currently proposed to add |
We also don't have It's notable that neither |
1 minute = 60 seconds. I don't think the leap second should be involved in (if then, 1 second != 1 second, |
Is there motivation for these constructors beyond the following?
If there's no motivation except for this, I think we should rather omit them from the standard library and let users be explicit that they want to sleep for 300 seconds and not 5 minutes (the latter being ambiguous wrt. leap seconds). |
Leap second is a concept about absolute time - more specifically, about calendar. 23:59:45 + 60 seconds could be one of 24:00:44, 24:00:45 or 24:00:46 by how to design the absolute time. But the duration 60 seconds could mean a physical quantity 60 seconds. If this still can be ambiguous, this can be documented enough on Duration. |
Some prior art from other languages / libraries:
Ohter languages and libraries that I checked1 did not have a comparable "Duration" concept. If you know of any, let me know and I'll be happy to add them. Personal thoughts:
Footnotes
|
I'm fine with adding hours/minutes constructors. I think those are pretty uncontroversial in what their semantics ought to be. Only leap seconds impact the length of an hour/minute, but we should just do what everyone else does and what But I'm strongly opposed to adding a constructor based on units of days (or anything bigger). I think that's a major mistake because it is common for days to not be 24 hours and for this to actually matter to humans. The new Javascript Temporal API, for example, generally does not let you treat days as "always 24 hours" unless you're explicitly working with civil time. When you're working with zoned datetimes, then |
I don't think the ("absolute") time keeping subtleties like leap second smearing, timezone differences or daylight saving time changes would trip people. A Maybe English isn't the best language to describe this as "time" is used for both, "absolute time" and relative time, but I think it's better to admit this ambiguity that often appears on spoken language and stick to well defined I'm not aware of real-world confusion, but only some cautious pushback around "standard" minutes/hours/days/weeks. (Yeah, I guess |
They can and do. Compare adding 1 day to 2025-03-08T17:00-05 in New York and adding 24 hours.
Those might be reasonable in specific contexts where the error is deemed acceptable, but I think it is a terrible choice to make for a general purpose library. |
Doing that where? This to me seems to be an issue with defining the When trying to define the length of Maybe what we need to avoid possible footguns is to properly define |
Also, like BurntSushi suggested, since the controversial bits seem to be mostly around I'm mostly interested on not forcing people to download a new nightly build every day just because of these 2 missing functions. |
Huh? Any datetime library is likely going to support adding
I would just drop weeks and days, yes. I don't see them ever becoming stable. I would strongly object to them.
I have no idea what you mean by this. |
TAI has no leap seconds or time zones (and thus no DST). We could call it |
I don't think that's any less of a footgun. |
My 5-minute tea won't be ready any sooner when there's leap seconds, daylight saving time changes or leap years. Instructions to make it say to wait 5 minutes and that always means The gap for confusion comes from bringing in concepts from absolute time, like day, year and their subdivisions as handy multipliers, and having people not agree on how I don't think |
Okay, this is going in circles. Unless this moves forward substantively, I'm done after this. I don't know why you're talking about Caesium-133 or leap seconds. I already told you my concern: DST. This is a real world footgun. Consider this program where we start with an appointment scheduled at Here is the [package]
publish = false
name = "jiff-play"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.86"
jiff = "0.2"
[[bin]]
name = "jiff-play"
path = "main.rs"
[profile.release]
debug = true Here is use std::time::{Duration, SystemTime};
use jiff::{tz::TimeZone, Zoned};
fn main() -> anyhow::Result<()> {
let tz = TimeZone::get("America/New_York")?;
let appointment = SystemTime::UNIX_EPOCH + Duration::from_secs(1741456800);
println!(
"appointment at: {}",
Zoned::try_from(appointment)?.with_time_zone(tz.clone())
);
// same time next tomorrow! so just add 1 day.
let rescheduled = appointment + from_days(1);
println!(
"appointment rescheduled to: {}",
Zoned::try_from(rescheduled)?.with_time_zone(tz.clone())
);
Ok(())
}
fn from_days(days: u64) -> Duration {
Duration::from_secs(days * 86400)
} And here is the output:
I used Jiff just to print the datetimes so that the mistake is clear, but the mistake occurs with just using standard library APIs in addition to the proposed This mistake is very easy to hit because you don't even need to be at or near a time zone transition for this to occur. You just need your duration to cover a time zone transition and you'll end up with an unintuitive result. The bottom line here is that humans tend use the unit of "day" in a way that leads to a variable length definition. It is usually but not always 24 hours. This doesn't apply to minutes. Humans essentially never use the unit "minute" to mean anything other than 60 SI seconds. So when you keep talking about leap seconds and cesium atoms despite the fact that I've already made it clear that it's the interaction of DST (and more generally, any time zone transition) and calendar units that concerns me, it gets very frustrating because it seems like you aren't really engaging with what I'm saying. Civil time versus absolute time doesn't really have anything to do with the calculus here. This is why I wrote my example above demonstrating the footgun using only standard library APIs. Now, sometimes it is appropriate to assume all days are 24 hours. For example, if you're only doing civil time arithmetic, then that makes sense. Totally cool with that and really nothing can go wrong. But a |
@BurntSushi With all due respect, Regardless of It's where to apply this duration matters - whether to As for the DST problem - but all of the |
I don't really understand why you're telling me this. I mean, I know what a
Yup, that's fine. I'm not seeking to prevent all mistakes. I'm looking to prevent some of them, such as the misconception that a day is always 24 hours.
I gave an example. Please respond with one. If |
No, you're mistaken. It really is the concept of day that matters here. Let's modify my program above to try out your claimed footgun: use std::time::{Duration, SystemTime};
use jiff::{tz::TimeZone, Zoned};
fn main() -> anyhow::Result<()> {
let tz = TimeZone::get("America/New_York")?;
// The second before DST starts in New York.
let t1 = SystemTime::UNIX_EPOCH + Duration::from_secs(1741503599);
println!("{}", Zoned::try_from(t1)?.with_time_zone(tz.clone()));
// add 2 seconds
let t2 = t1 + Duration::from_secs(2);
println!("{}", Zoned::try_from(t2)?.with_time_zone(tz.clone()));
// subtract 1 second
let t3 = t2 - Duration::from_secs(1);
println!("{}", Zoned::try_from(t3)?.with_time_zone(tz.clone()));
Ok(())
} Its output:
Those are all perfectly in keeping with what a human would expect. Formatting it requires the datetime library to be aware of the DST, but the actual arithmetic here is just fine. There's no footgun or mistake. |
We could turn this around, how is one supposed to express "one day in elapsed actual time" rather than "same time, one day forward on the calendar"? E.g. when trying to compute the amount of GPU-days spent on a renderjob. |
I would question that "one day in elapsed actual time" has an unambiguous meaning. But I have no problem asking users to write |
Then how would you normally provide this context? Should we make duration generic over a clock source? I don't think this is possible retroactively because durations can be transplanted between Instant and SystemTime mentioned above. |
In |
@BurntSushi I am no expert in time libraries, but Chrono seems do the job: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=f1cd329588b14f9580cddc59965481a5. |
Note that on the example on 2025-03-09 NY is no longer at UTC-05:00, but at UTC-04:00. |
It does not. Case in point, with your setup, Chrono will let you generate a time that will literally never appear on the clocks in New York: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=8c06a26675f11c2f1e17c5f572e3b40b You need to be using (This is one of the many reasons I went out and built Jiff.) |
Well, they're also physical units because almost nobody uses megaseconds. This is what I was aiming for with |
Isn't the problem with days and DST the same for minutes and leap seconds? |
Sure. The problem is that "day" is an overload term. It is difficult to establish the right context while still using the word "day." Like it just doesn't seem worth it to be given how easy the footgun is personally. |
It seems to me we just need a // | |
let appointment = SystemTime::UNIX_EPOCH + Duration::from_secs(1741456800); // 2025-03-08T13:00:00-05:00[America/New_York]
let poorly_rescheduled = appointment + Duration::from_days(1); // 2025-03-09T14:00:00-04:00[America/New_York]
let civicly_rescheduled = appointment + CivicTimeOffset::days(1); // 2025-03-09T13:00:00-04:00[America/New_York] We should probably delay (had to move this because it got lost amidst a discussion) |
Already addressed above: #120301 (comment) |
Adding an entirely different duration type seems like a major escalation to me. It's also a totally different discussion from my perspective, and starts walking down the path of "let's add datetime support to std." Doing it half-assed would be a disaster. If you really want to have that discussion, I'd suggest opening a new issue so that we don't get derailed from considering the much more narrow proposal of convenience constructors on |
I'm aware of this. The progression we all seem to foresee is,
And of course 2 is a huge task, but that shouldn't stop everyone from getting |
If |
I am glad we seem overcoming the leap second issue. Unfortunately, the definition of day is too much ambiguous. It even doesn't have a single definition in science. I'd like to remind a thing. Convenience constructors are provided for convenience. They don't provide a new real feature. If the name matters, I hope the next discussion to be not to have or not the specifically named function |
While I have no idea how much At the same time |
So, to play the devil's advocate against This is way more unlikely to be a problem. Even if we could end up just saying the programmer was wrong and had a fundamental misunderstanding of what Also, we may not even have more leap seconds in the future due to a decrease on the Earth's rotation cancelling it1. Footnotes |
I don't think there's any need to play devil's advocate nor a reason to continue blocking stabilizing |
@djc are you open to gut this to get I truly believe we should be free to get all of it, but I'd rather see some progress than waiting on a DateTime implementation getting to std. |
Feature gate:
#![feature(duration_constructors)]
This is a tracking issue for supporting larger unit sizes for
Duration
constructors.Public API
Add the following constructors to
Duration
:Duration::from_weeks()
Duration::from_days()
Duration::from_hours()
Duration::from_mins()
Steps / History
The text was updated successfully, but these errors were encountered: