Now I'm using
use chrono::{DateTime, Utc};
pub fn today_three_years_earlier() -> DateTime<Utc> {
let now = Utc::now();
// this is February 29 and it doesn't work of course
// let now = DateTime::from_timestamp(1_709_228_325, 0).unwrap();
let from = now.with_year(now.year() - 3).unwrap();
from
}
but if now (today) is February 29 it doesn't work of course because I'm simply removing 3 years from today's year.
How can I subtract exactly 3 years from now's date?
It would also be enough for me to remove 365 days * 3 years (without considering leap years).
REPRODUCTION: https://www.rustexplorer.com/b/wkqutm
tl;dr: You could subtract 36 Months instead. The result will be Feb 28th.
The result will be clamped to valid days in the resulting month, see checked_add_months for details.
now - Months::new(3 * 12);
However, this has the result of mapping two days (Feb 28th and Feb 29th) to one day (Feb 28th). Depending on your application this might have negative consequences, like scheduling software piling two days worth of work into one day. In that case you probably want to catch the None result and ask the user what to do (see @cdhowie's answer).
In general, you must consider that calendar calculations might result in a date which does not exist.
Calendars are hard. Our calendar is full of exceptions, gaps, and cycles which do not line up, but we try to force it anyway.
"What is Feb 29th of last year?" is unanswerable. A human might arbitrarily decide to say Feb 28th or March 1st, but Rust won't decide this for you, it returns None
to inform you it doesn't exist. This is documented in with_year
.
Returns None if: The resulting date does not exist (February 29 in a non-leap year).
There is no way around this. You're trying to map 366 days into 365; it cannot neatly map 1-to-1.
Other date changes have similar problems. If you change Dec 31st to April. If you ask 2:30am on Sunday, March 10, 2024 in most of the United States (2am to 3am doesn't exist because of daylight savings). If you ask UTC for Sunday, November 3, 2024, 1:30:00 am in the US (because of daylight savings 1:30am happens twice). Etc.
Subtracting (or adding) 12 months works because the maintainers of chrono
have decided that if you ask to subtract 12 months in February you want a date in February.
The result will be clamped to valid days in the resulting month, see DateTime::checked_sub_months for details.