Skip to content
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

Years type implementation #900

Closed
wants to merge 3 commits into from

Conversation

Rafael-Conde
Copy link

I started a simple implementation that I intend to add proper add checks once the problem is actually solved.

I implemented simples tests too, but there is still a lot to do. I'm currently opening the PR so discussions about the implementation can occur here and you guys can follow the progress. I believe this is semver compatible since I'm only adding a new type so I targeted the 0.4.x branch, hopefully, this type will make it into that version.

I already applied the recommendations @esheppa made in this comment on issue #416

@Rafael-Conde
Copy link
Author

I believe I already made the mistake to "commit to main". I branched from main and that's the problem. I'll change that later because I don't have more time now. I decided to open the PR since I said I would. I'll fix that later, wether opening a new PR with the proper branch or another fix for this one.

One thing too, is there a deadline for the 0.4.x version to be out?

@djc
Copy link
Contributor

djc commented Nov 28, 2022

There's no deadline! Thanks for working on this, I'll take a more detailed look after you rebase on 0.4.

I started a simple implementation that I intend to add proper add checks
once the problem is actually solved.
@Rafael-Conde
Copy link
Author

There's no deadline!

Nice! I'm likely to work on it on the weekends,

Thanks for working on this

Not a problem! I appreciate the opportunity to contribute!

I'll take a more detailed look after you rebase on 0.4.

Ok! I'm still in the middle of the implementation(I couldn't finish it on my first session), I wrote a few lines of code but basically it does nothing so far. But help is always welcome! you guys are very polite when helping out! Thank you for that!

Also I got a bit messed up while rebasing but I think everything should be in order right now.

If you guys want, I can leave a comment here once I got any of the implementations actually working. To review and improve the code.

Also, during development I plan to leave comments on the code I'm working on to try and complement the code with some thoughts. Other than that I could mark the lines of code here and leave the comment here, where I would otherwise leave in the code.

@djc djc marked this pull request as draft December 1, 2022 10:20
@djc
Copy link
Contributor

djc commented Dec 1, 2022

I've converted this to a draft PR, please push the "Ready for review" button once you think it's ready!

@Rafael-Conde
Copy link
Author

Rafael-Conde commented Dec 1, 2022

Ok! Thank you for that! I believe I should've done it from the beginning, I'm still learning haha

I would like to ask too, the checks in the end of the PR are only for my modifications? in order to merge, all of them should be ok, right?

@djc
Copy link
Contributor

djc commented Dec 2, 2022

Yes, CI should pass. It's possible that some failures are unrelated to your changes, but should be unlikely.

Copy link
Collaborator

@esheppa esheppa left a comment

Choose a reason for hiding this comment

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

just leaving some initial comments, mostly on code location

src/years.rs Outdated
}

impl NaiveDate {
pub fn checked_add_years(self, years: Years) -> Option<NaiveDate> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this impl should be in naive/date.rs - you can import Years over there

src/years.rs Outdated
pub fn checked_sub_years(self, num_years: Years) -> Option<NaiveDate> {}
}

// impl Add<Years> for NaiveDate {}
Copy link
Collaborator

Choose a reason for hiding this comment

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

these impls should be in the module where the target type is defined

src/years.rs Outdated
}

// almost surely there is a more precise way to implement this
fn is_leap_year(year: i32) -> bool {
Copy link
Collaborator

Choose a reason for hiding this comment

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

you can use ordinal flags and then use the ndays method to figure out whether the target year is a leap year or not. You can construct the ordinal flags directly from the target year itself

Copy link
Author

Choose a reason for hiding this comment

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

Thank you for the pointers! I'll surely make the requested changes! This one in special will be of great help! really appreciated!

I'll move the other impl blocks to the proper locations too!

@Rafael-Conde
Copy link
Author

Rafael-Conde commented Dec 17, 2022

I finished the implementation, it ended up a lot simpler than I first imagined but I currently don't see why not do that haha

I still need to do the documentation, but the code is compiling and the simple tests I wrote are working. (I made a simple TODO docs just to run but removed from the commit so I don't forget to do it once I'm back at it).

you can use ordinal flags and then use the ndays method to figure out whether the target year is a leap year or not. You can construct the ordinal flags directly from the target year itself

this helped a lot and ended up pointing me to the solution haha Thank you!!

I'm planning on implementing the addition of Years and I'm still thinking about how to get the difference, in complete years, between 2 dates.

That specific difference between dates helps me a lot.

PS: took me a while to get back to it because of life, but I should work more on it in the following days.

Copy link
Collaborator

@pitdicker pitdicker left a comment

Choose a reason for hiding this comment

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

My personal opinion is that I don't care much for the Day and Month types. I prefer writing date.checked_add_years(5) over date.checked_add_years(Years::new(5)) or date + Years::new(5). It seems like over-using the type system to me. But others may like it 🤷.

The DateTime<Tz> does not have the extra methods yet.

@@ -23,6 +23,8 @@ use crate::oldtime::Duration as OldDuration;
use crate::DateTime;
use crate::{Datelike, Weekday};

use crate::Years;

Copy link
Collaborator

Choose a reason for hiding this comment

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

The Date type is deprecated, better to not add new methods to it.

@@ -1248,6 +1250,23 @@ impl NaiveDate {
NaiveWeek { date: *self, start }
}

// I think self here is a copy, but I still have to make sure it's
Copy link
Collaborator

Choose a reason for hiding this comment

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

It is a copy, you only want to take self by reference for DateTime<Tz>.

return Some(self);
}
let num_years = i32::try_from(years.0).ok()?;
self.with_year(self.year() + num_years)
Copy link
Collaborator

@pitdicker pitdicker Jul 6, 2023

Choose a reason for hiding this comment

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

This will fail if you add 1 year to February 29th. This is different from checked_add_months, which will fall back to the last day of the month. Shouldn't they work the same?

Copy link
Collaborator

@pitdicker pitdicker Jul 6, 2023

Choose a reason for hiding this comment

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

How do you handle integer overflow in for example self.year() + i32::MAX?

pub fn checked_sub_years(mut self, num_years: Years) -> Option<NaiveDateTime> {
self.date = self.date.checked_sub_years(num_years)?;
Some(self)
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

NaiveDateTime could also use Add and Sub implementations.

@@ -0,0 +1,58 @@
// I usually develop with this clippy flags
// but I can remove once the this module is done
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 when done.

clippy::unwrap_used,
clippy::expect_used)]

/// TODO
Copy link
Collaborator

Choose a reason for hiding this comment

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

Needs documentation.

clippy::expect_used)]

/// TODO
#[derive(Debug)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you make this match the derives and attributes of Month?

return Some(self);
}
let num_years = i32::try_from(years.0).ok()?;
self.with_year(self.year() - num_years)
Copy link
Collaborator

Choose a reason for hiding this comment

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

As above.

@djc
Copy link
Contributor

djc commented Jul 6, 2023

Yeah, I've been on the fence about the proliferation of types here -- not sure it's the best idea, even though I may have started it. (I think the original idea was that it allowed for using the operators, which does seem like nice API.)

@pitdicker
Copy link
Collaborator

I was thinking we could add impl From<u32> for Month and make checked_add_month take Into<Month> to make the API a little nicer to use. And the same for Day.

We can stuff the types inside a module mostly out of sight 😄.

@pitdicker
Copy link
Collaborator

Closing due to inactivity, and because we may want to step away from Day and Month types, and Year as in this PR, in favour of a more feature-complete CalendarDuration type.

@Rafael-Conde Thank you for working on this!

@pitdicker pitdicker closed this Sep 18, 2023
@Rafael-Conde
Copy link
Author

Thank you for notifying my @pitdicker!!

I recently came back to this PR and saw how much work you did!

I was really impressed, a lot of time and thought!!

One last recommendation as I leave this, I saw a(apparently old) discussion about what to do with invalid dates(e.g. adding a month to January 31st, should it be February 28 or March 02?)

For this matter I would recommend a cpp conference video of the creator of Chrono describing how he handled that providing invalid dates and methods for resolving it to the preferred way(it was possible to yield both results from the invalid date I mentioned in the example). I'll search for the link and I'll post here or maybe in the new PR, if you folks find it useful. Keep up the good work!!!

@pitdicker
Copy link
Collaborator

Thank you, and that would be interesting!

I suppose we have the capability to represent an invalid date with the internal Mdf type (and wrap into something nicer and public). But then we also have two other places where the result could become invalid: DST transitions and leap seconds. Something to think about 😄.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants