-
Notifications
You must be signed in to change notification settings - Fork 533
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
RFC: Bring some of the Rubedo extensions for Chrono into Chrono #1392
Comments
I'll need a couple of comments to form an opinion. List of the constants and methods in your Rubedo crate: impl Duration {
const MAX_NANOSECONDS: i64 = 9_223_372_036_854_775_807i64;
const MAX_NANOSECONDS_FULL: i128 = 9_223_372_036_854_775_807_000_000i128;
const MAX_MICROSECONDS: i64 = 9_223_372_036_854_775_807i64;
const MAX_MICROSECONDS_FULL: i128 = 9_223_372_036_854_775_807_000i128;
const MAX_MILLISECONDS: i64 = 9_223_372_036_854_775_807i64;
const MAX_SECONDS: i64 = 9_223_372_036_854_775i64;
const MAX_MINUTES: i64 = 153_722_867_280_912i64;
const MAX_HOURS: i64 = 2_562_047_788_015i64;
const MAX_DAYS: i64 = 106_751_991_167i64;
const MAX_WEEKS: i64 = 15_250_284_452i64;
const MIN_NANOSECONDS: i64 = -9_223_372_036_854_775_808i64;
const MIN_NANOSECONDS_FULL: i128 = -9_223_372_036_854_775_807_000_000i128;
const MIN_MICROSECONDS: i64 = -9_223_372_036_854_775_808i64;
const MIN_MICROSECONDS_FULL: i128 = -9_223_372_036_854_775_807_000i128;
const MIN_MILLISECONDS: i64 = -9_223_372_036_854_775_807i64;
const MIN_SECONDS: i64 = -9_223_372_036_854_775i64;
const MIN_MINUTES: i64 = -153_722_867_280_912i64;
const MIN_HOURS: i64 = -2_562_047_788_015i64;
const MIN_DAYS: i64 = -106_751_991_167i64;
const MIN_WEEKS: i64 = -15_250_284_452i64;
const UNITS: [(i64, &'static str); 7] = _;
fn nanoseconds_full(nanoseconds: i128) -> Option<Duration>;
fn microseconds_full(microseconds: i128) -> Option<Duration>;
fn num_nanoseconds_full(&self) -> i128;
fn num_microseconds_full(&self) -> i128;
}
impl Months {
const MAX_MONTHS: u32 = 4_294_967_295u32;
const MAX_YEARS: u32 = 357_913_941u32;
fn months(months: u32) -> Self;
fn years(years: u32) -> Option<Months>;
fn num_months(&self) -> u32;
fn num_years(&self) -> u32;
}
impl NaiveDate {
const MAX_YEAR: i32 = 262_142i32;
const MIN_YEAR: i32 = -262_143i32;
fn today() -> NaiveDate;
fn days_in_month(&self) -> u32;
fn days_in_month_opt(year: i32, month: u32) -> Option<u32>;
fn days_in_year(&self) -> u32;
fn days_in_year_opt(year: i32) -> Option<u32>;
fn is_leap_year(&self) -> bool;
fn is_leap_year_opt(year: i32) -> Option<bool>;
fn start_of_month(&self) -> NaiveDate;
fn start_of_month_opt(year: i32, month: u32) -> Option<NaiveDate>;
fn end_of_month(&self) -> NaiveDate;
fn end_of_month_opt(year: i32, month: u32) -> Option<NaiveDate>;
fn start_of_year(&self) -> NaiveDate;
fn start_of_year_opt(year: i32) -> Option<NaiveDate>;
fn end_of_year(&self) -> NaiveDate;
fn end_of_year_opt(year: i32) -> Option<NaiveDate>;
} |
MonthsThis partly matches your work in #1373. With Like @djc I am not a fan of You would mainly want to add |
Duration
The API of chrono is, in Rust terms, ancient 😆. These methods pre-date the stable We could bikeshed the names, but I see no harm in having them. Also not much benefit to be honest, the PR should come with some good rationale. What I like to see more is conversion to/from floating point values #1365. |
NaiveDate
However once you start adding convenience methods like this, where do you draw the line? We currently have the line at little to none, and that is easy to defend. I wonder if it would be good to have an extension trait for everything-and-the-kitchen-sink convenience methods, with no stability guarantees when it comes to extending the trait. |
@danwilliams Some general comments. All just my personal thoughts, @djc is the primary maintainer. I only got involved around March/April, then dropped out for a couple of months, and am just getting back again. Basically the API of chrono is large and pre-dates Rust 1.0 and current conventions. The hope is to reduce the API surface, make methods less panic-prone, use I have a good number of open PRs that tackle tricky issues, mostly against the branch for 0.4. They represent a lot of review-work which mostly comes on the shoulders of @djc, who already does an amazing amount of work. Not sure how to make good progress there to be honest. We got a lot done last year, but there is also a lot left to do for a good minimal 0.5 release with a nicer API. Chrono could use some more help, and you seem to be willing to contribute. Do you want to dive into some of the open PRs or issues? O, and have you found our discord? Linked as a badge on the readme. |
I really like this crate @danwilliams ! Here are my recommended "takes" to copy from rubedo to chrono, listed by module.
|
I am not so sure about the @danwilliams I guess you had to add these constants because before chrono 0.4.32 the initializers would panic on out of bound values. Do you still see a need for them now that we have alternatives that return an option? Note that we already have
🤣 We would just get different trade-offs though, and still have to support the existing system. No thanks. |
Forgot about this one. We used to have On the other hand it is just |
NaiveDate::end_of_monthLast summer I was writing a fast, compactly encoded alternative to chrono_tz (not finished sadly). And 'last day of the month' or 'last x weekday of the month` is needed for a lot of transition date rules. I wanted to have a reliable, fast way to get that. The current methods to get the last day of the month are:
The last solution is recommended in our documentation. Both solutions are not especially efficient, and the last solution has the disadvantage that it can not return I found a neat trick that seemed to fit in the spirit of chrono's design. To convert year-month-day to a So Edit: a fast solution would also be helpful when doing calculations with a |
This discussion is a little unstructured, so here's a proposed structure: everyone who wants to participate, please pick your top 5 added methods/constants (one constant or method per item!) from the Rubedo crate and list them, in most-to-least important order, with a quick explanation for how/where you'd use it. |
I was not even at the point yet to have a wishlist 😄. Basically just thinking out loud. Three seem useful to me: As a list:
|
You didn't do use cases, but here are some thoughts:
|
Then we seem to have an answer for @danwilliams.
Not entirely convinced yet, they would need a use case. It may just be the name; maybe use
Other methods would need an explanation why alternatives using the currently available methods are not nice enough.
Agreed.
Hopefully most would not be needed when a couple more methods become const. |
@pitdicker @jtmoon79 @djc thank you all very much for your time and consideration! 🙂 I appreciate the effort you have gone to in order to review and assess - and each of your suggestions and points of view are useful and insightful. To be honest there is more support and positivity than I had expected, so I am very pleased that there will be some things of use I can bring across in order to benefit the main crate. I waited a day before replying, to give time for the discussion to take place, and to ensure that all three of you had chance to respond. Please find below my thoughts on each area discussed.
|
So in previous discussions I think my preferred direction at least was to avoid adding more stuff to
Well, that is exactly the point.
You make this sound straightforward, but I disagree that it is. Even in your statement you mention "avoiding clutter", which I would definitely classify your range of
Well, doesn't Rubedo already provide this? The helpful part of extension traits is that they don't have to live in the source crate.
I'm open to exposing more of the constants that are already being used, especially unopinionated ones.
Pff, we just had a bunch of issues reported by users who were depending on the exact value of some of the limit constants, which I think goes to show that these APIs do get misused. I'm not saying we shouldn't have them, but I think you're optimistic on how they get used and how much exposing these contrains future API evolution.
Yes, the goal is to streamline the API, but that includes shrinking the surface for parts of the API that add little value.
Yes. 👍 |
That's interesting - I will be following that as it unfolds, as I am curious to see how it turns out. Personally I have nothing against
I agree that I find I feel that providing the methods presented are sensible and usable, useful to people, and consistent with the overall crate, it is generally better to add them than not. There is an extreme of minimalism that hampers usability, which is where Chrono is in a few places right now. Some of my proposals help with this, and some go a little further and provide more sugar for common actions. These things tend to generally be a net benefit in developer perception and favour.
Well, no, actually, the majority of my time in programming is spent on library functionality, and it has been that way for at least the last twenty years. So I tend to naturally think about things from both perspectives - although I would add that there is an implicit obligation on a library maintainer to consider "what is going to be most useful to users" and not simply "what is easiest to maintain". The latter speaks to a problem - but one easily resolved in most cases; the former is the target 🙂 It should also be pointed out that useful tertiary functions added in a correct and consistent way generally don't increase the maintenance weight of a library.
Yes, but I didn't want to discount @pitdicker's suggestion out-of-hand. I was curious as to what he had in mind. Let's remember, though, that my RFC here is to bring things into the Chrono mainstream, to benefit Chrono and the general audience - which is likely better than keeping things in my small and humble crate, which is not widely used 🙂 Therefore, given this aspect, I am of course open to discussing any way that you guys might want to achieve that outcome.
That's cool - how can I tell which ones are opinionated and which are unopinionated? I'm thinking I'll just go and create a PR with some most-obvious suggestions in the first instance, but any elaboration on your position here would be appreciated.
Yes... that's kinda the point 😄 Unfortunately, recent changes made broke other crates, including Diesel. I actually agree with them that it should have been a point release under semver, but it is what it is... the key thing here is that people will use what is available, and if it's not available, they will create their own things in order to fill the gap. That leads to a brittle interface. It's not misuse - it's compensating. It's exactly how I encountered the issues, too. This is why I am proposing that Chrono should make itself the source of authority on such things, and make the information publicly available. It's what is generally done elsewhere.
Can you define and clarify this? What needs shrinking - is there a list? I can think of a few areas that I don't feel are consistent, and need massaging, but overall I think the primary issue is exactly that: inconsistency and incompleteness, rather than too much or too little per sé.
Excellent - I will wait for further clarification on some of the above first, as I am curious, but then I will aim to get some PRs in this week for further detailed discussion and review 🙂 Thank you! |
By unopinionated I mean things like
Semver-incompatible releases for a widely used crate like chrono are expensive for the ecosystem, so where we can get away with making minor changes in behavior while providing transparent benefits to a large audience while breaking only a few test cases in some downstream crates still seems like good idea to me. This is an old crate and it hasn't always been maintained well, so the API is full of warts and we have to navigate carefully to make it better while maintaining compatibility where possible.
There is no list, but I would be interested to see PRs with semver-compatible changes for areas that you think are inconsistent. |
I've always felt uneasy about
This worries me a little, because things like the milliseconds in a second are readily known, and although useful to publish, the other constants that are Chrono-specific and could change are more important to publish. That's how I see it, anyway... those constants give better inspection of the library and its fundamentals, and making them available is idiomatic Rust.
I totally get your point, and I think it's a very subjective issue, with difficult choices on either side. However, people downstream are never happy when things break. That's why making certain key fundamentals available publicly can help in this regard, at the very least because people are better-informed, and ideally because they can use those reference points in their code, too.
Excellent, I will try to find time to take a look through at some point - however, all of the ones I can think of at present are not semver-compatible, and would cause breaking changes to release, so perhaps are better off in v0.5. |
Excellent! In a few cases I have made minimals PRs to test the waters, with only the API and motivating docs and examples. Complete tests and polish then come if acceptable. Maybe that is a strategy? Up to you of course. |
@pitdicker that sounds like a sensible strategy for brand-new code 🙂 In this situation I already have full tests, so I will bring them over, BUT there will be a fair few things about naming, shape, approach, etc. that I expect to be up for discussion. Now that I know Chrono is more active, and that you guys are encouraging submissions, I will look to suggest any new ideas here first, and then add to my crate only if not suitable for Chrono - and I think your suggestion there fits very well indeed 👍 |
I had a similar feeling after #1382 and #1389 (wouldn't call it misuse though). Adding public constants has some overlap with giving stability guarantees. I'm not sure how much we already promise in the documentation though. B.t.w. what is the current convention in the ecosystem for constants? Do we prefer |
@pitdicker I would say something along the lines of |
Yes, I agree that |
@danwilliams Are you still interested in working on some of these additions? |
@djc @pitdicker I have a number of pieces of functionality to extend Chrono, which are available under my Rubedo crate, with there being a specific module for the Chrono extensions.
I would like to bring some of that functionality into Chrono, to benefit a wider audience, but I am not sure how much of it is appropriate and would gain approval, so I am raising this RFC ticket to get feedback on that before I spend time bringing anything across into Chrono PRs.
Here are some initial thoughts, although I would welcome perusal of the full page linked to:
Duration
, which provide constructors and getters for using the full range of nanoseconds and microseconds that aDuration
can store. Due to the limitations of the native Chrono functions in this regard, I believe these functions would be useful to have in the main library, and they could even replace the current functions (as they extend the range) - although this would be a breaking change in my view, as they accept/returni128
instead ofi64
. So perhaps just adding my functions would be sufficient here.NaiveDate
, as they are common operations that have been very helpful in my various project codebases.Duration.humanize()
is useful but specific, and I doubt you will want theMonths
extensions as I know you have aCalendarDuration
on the way 🙂I don't know how these suggestions fit in with your plans for v0.5, but I think they can all target v0.4.x as they are fully compatible. I am of course open to adjusting the approach, naming, scope, etc. of any of these functions if you want them brought in, and would also submit them according to Chrono's coding standards as I have done with my other PRs.
Let me know!
The text was updated successfully, but these errors were encountered: