From 1d33cbcb6ecbcd5ed1c91e1bbf2d3a60c36cab27 Mon Sep 17 00:00:00 2001 From: Yohan Boogaert Date: Wed, 8 Jun 2022 13:06:27 +0200 Subject: [PATCH] Add `elapsed_years` to `Date` and `DateTime` (#557) Co-authored-by: Cecile Tonglet Co-authored-by: Milo <50248166+Milo123459@users.noreply.github.com> --- AUTHORS.txt | 1 + CHANGELOG.md | 1 + src/date.rs | 41 +++++++++++++++++++++++++++++++++++++++++ src/datetime/mod.rs | 18 ++++++++++++++++++ src/datetime/tests.rs | 17 +++++++++++++++++ src/lib.rs | 14 ++++++++++++++ 6 files changed, 92 insertions(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index 9501a9d059..e4d7d2ad1d 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -39,3 +39,4 @@ Steve Klabnik Tom Gallacher klutzy kud1ing +Yohan Boogaert diff --git a/CHANGELOG.md b/CHANGELOG.md index 839bdeeda1..39174ef312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Versions with only mechanical changes will be omitted from the following list. * Fix `DurationRound` is not TZ aware (#495) * Implement `DurationRound` for `NaiveDateTime` * Add `DateTime::from_local()` to construct from given local date and time (#572) +* Add a function that calculates the number of years elapsed between now and a given `Date` or `DateTime` (#557) * Correct build for wasm32-unknown-emscripten target (#568) * Change `Local::now()` and `Utc::now()` documentation from "current date" to "current date and time" (#647) * Fix `duration_round` panic on rounding by `Duration::zero()` (#658) diff --git a/src/date.rs b/src/date.rs index 0758cc844f..d6d76d8997 100644 --- a/src/date.rs +++ b/src/date.rs @@ -275,6 +275,24 @@ impl Date { pub fn naive_local(&self) -> NaiveDate { self.date } + + /// Retrieves the elapsed years from now to the given [`Date`]. + #[cfg(feature = "clock")] + pub fn elapsed_years(&self) -> u32 { + let now = Utc::today().with_timezone(&self.timezone()); + + let years = if (now.month(), now.day()) < (self.month(), self.day()) { + now.year() - self.year() - 1 + } else { + now.year() - self.year() + }; + + if years.is_positive() { + years as u32 + } else { + 0 + } + } } /// Maps the local date to other date with given conversion function. @@ -498,3 +516,26 @@ where write!(f, "{}{}", self.naive_local(), self.offset) } } + +#[cfg(test)] +mod tests { + use crate::consts::f64; + use crate::offset::Utc; + use crate::oldtime::Duration; + + #[test] + #[cfg(feature = "clock")] + fn test_years_elapsed() { + // This is always at least one year because 1 year = 52.1775 weeks. + let one_year_ago = + Utc::today() - Duration::weeks((f64::WEEKS_PER_YEAR * 1.5).ceil() as i64); + // A bit more than 2 years. + let two_year_ago = + Utc::today() - Duration::weeks((f64::WEEKS_PER_YEAR * 2.5).ceil() as i64); + + assert_eq!(one_year_ago.elapsed_years(), 1); + assert_eq!(two_year_ago.elapsed_years(), 2); + // if the given Date is later than now, the function will always return 0. + assert_eq!((Utc::today() + Duration::weeks(12)).elapsed_years(), 0); + } +} diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index ab924a92ab..e742e591b9 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -355,6 +355,24 @@ impl DateTime { pub fn naive_local(&self) -> NaiveDateTime { self.datetime + self.offset.fix() } + + /// Retrieve the elapsed years from now to the given [`DateTime`]. + #[cfg(feature = "clock")] + pub fn elapsed_years(&self) -> u32 { + let now = Utc::now().with_timezone(&self.timezone()); + + let years = + if (now.month(), now.day(), now.time()) < (self.month(), self.day(), self.time()) { + now.year() - self.year() - 1 + } else { + now.year() - self.year() + }; + if years.is_positive() { + years as u32 + } else { + 0 + } + } } impl Default for DateTime { diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index 8c373d3ba3..86036040d9 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -1,6 +1,8 @@ use std::time::{SystemTime, UNIX_EPOCH}; use super::DateTime; +#[cfg(feature = "clock")] +use crate::consts::f64; use crate::naive::{NaiveDate, NaiveTime}; #[cfg(feature = "clock")] use crate::offset::Local; @@ -407,3 +409,18 @@ fn test_datetime_from_local() { assert_eq!(datetime_east, datetime_utc.with_timezone(&timezone_east)); assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west)); } + +#[test] +#[cfg(feature = "clock")] +fn test_years_elapsed() { + // This is always at least one year because 1 year = 52.1775 weeks. + let one_year_ago = Utc::today() - Duration::weeks((f64::WEEKS_PER_YEAR * 1.5).ceil() as i64); + // A bit more than 2 years. + let two_year_ago = Utc::today() - Duration::weeks((f64::WEEKS_PER_YEAR * 2.5).ceil() as i64); + + assert_eq!(one_year_ago.elapsed_years(), 1); + assert_eq!(two_year_ago.elapsed_years(), 2); + + // If the given DateTime is later than now, the function will always return 0. + assert_eq!((Utc::today() + Duration::weeks(12)).elapsed_years(), 0); +} diff --git a/src/lib.rs b/src/lib.rs index d81cfe1e51..70ade573f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -508,6 +508,20 @@ pub use month::{Month, ParseMonthError}; mod traits; pub use traits::{Datelike, Timelike}; +/// Constants that can be used by all components. +pub mod consts { + /// Constants of type `f32`. + pub mod f32 { + /// Number of weeks in a year. + pub const WEEKS_PER_YEAR: f32 = 52.1775; + } + /// Constants of type `f64`. + pub mod f64 { + /// Number of weeks in a year. + pub const WEEKS_PER_YEAR: f64 = 52.1775; + } +} + #[cfg(feature = "__internal_bench")] #[doc(hidden)] pub use naive::__BenchYearFlags;