From d8a8feb45c35f910c199c94f2ae96f5ea2ecc39a Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 19 Dec 2023 11:41:14 -0500 Subject: [PATCH] Add FormatDistance struct, add hide_prefix option --- crates/ui2/src/utils/format_distance.rs | 265 +++++++++++++++++++----- 1 file changed, 217 insertions(+), 48 deletions(-) diff --git a/crates/ui2/src/utils/format_distance.rs b/crates/ui2/src/utils/format_distance.rs index ec9a8ad5d879296467c8fcb22caf4b25ea4bfa7d..1586a6e155b5ddc10391c25254d2e30b1e89a598 100644 --- a/crates/ui2/src/utils/format_distance.rs +++ b/crates/ui2/src/utils/format_distance.rs @@ -19,6 +19,45 @@ impl DateTimeType { } } +pub struct FormatDistance { + date: DateTimeType, + base_date: DateTimeType, + include_seconds: bool, + add_suffix: bool, + hide_prefix: bool, +} + +impl FormatDistance { + pub fn new(date: DateTimeType, base_date: DateTimeType) -> Self { + Self { + date, + base_date, + include_seconds: false, + add_suffix: false, + hide_prefix: false, + } + } + + pub fn from_now(date: DateTimeType) -> Self { + Self::new(date, DateTimeType::Local(Local::now())) + } + + pub fn include_seconds(mut self, include_seconds: bool) -> Self { + self.include_seconds = include_seconds; + self + } + + pub fn add_suffix(mut self, add_suffix: bool) -> Self { + self.add_suffix = add_suffix; + self + } + + pub fn hide_prefix(mut self, hide_prefix: bool) -> Self { + self.hide_prefix = hide_prefix; + self + } +} + /// Calculates the distance in seconds between two NaiveDateTime objects. /// It returns a signed integer denoting the difference. If `date` is earlier than `base_date`, the returned value will be negative. /// @@ -32,7 +71,12 @@ fn distance_in_seconds(date: NaiveDateTime, base_date: NaiveDateTime) -> i64 { } /// Generates a string describing the time distance between two dates in a human-readable way. -fn distance_string(distance: i64, include_seconds: bool, add_suffix: bool) -> String { +fn distance_string( + distance: i64, + include_seconds: bool, + add_suffix: bool, + hide_prefix: bool, +) -> String { let suffix = if distance < 0 { " from now" } else { " ago" }; let distance = distance.abs(); @@ -43,53 +87,128 @@ fn distance_string(distance: i64, include_seconds: bool, add_suffix: bool) -> St let months = distance / 2_592_000; let string = if distance < 5 && include_seconds { - "less than 5 seconds".to_string() + if hide_prefix { + "5 seconds" + } else { + "less than 5 seconds" + } + .to_string() } else if distance < 10 && include_seconds { - "less than 10 seconds".to_string() + if hide_prefix { + "10 seconds" + } else { + "less than 10 seconds" + } + .to_string() } else if distance < 20 && include_seconds { - "less than 20 seconds".to_string() + if hide_prefix { + "20 seconds" + } else { + "less than 20 seconds" + } + .to_string() } else if distance < 40 && include_seconds { - "half a minute".to_string() + if hide_prefix { + "half a minute" + } else { + "half a minute" + } + .to_string() } else if distance < 60 && include_seconds { - "less than a minute".to_string() + if hide_prefix { + "a minute" + } else { + "less than a minute" + } + .to_string() } else if distance < 90 && include_seconds { "1 minute".to_string() } else if distance < 30 { - "less than a minute".to_string() + if hide_prefix { + "a minute" + } else { + "less than a minute" + } + .to_string() } else if distance < 90 { "1 minute".to_string() } else if distance < 2_700 { format!("{} minutes", minutes) } else if distance < 5_400 { - "about 1 hour".to_string() + if hide_prefix { + "1 hour" + } else { + "about 1 hour" + } + .to_string() } else if distance < 86_400 { - format!("about {} hours", hours) + if hide_prefix { + format!("{} hours", hours) + } else { + format!("about {} hours", hours) + } + .to_string() } else if distance < 172_800 { "1 day".to_string() } else if distance < 2_592_000 { format!("{} days", days) } else if distance < 5_184_000 { - "about 1 month".to_string() + if hide_prefix { + "1 month" + } else { + "about 1 month" + } + .to_string() } else if distance < 7_776_000 { - "about 2 months".to_string() + if hide_prefix { + "2 months" + } else { + "about 2 months" + } + .to_string() } else if distance < 31_540_000 { format!("{} months", months) } else if distance < 39_425_000 { - "about 1 year".to_string() + if hide_prefix { + "1 year" + } else { + "about 1 year" + } + .to_string() } else if distance < 55_195_000 { - "over 1 year".to_string() + if hide_prefix { "1 year" } else { "over 1 year" }.to_string() } else if distance < 63_080_000 { - "almost 2 years".to_string() + if hide_prefix { + "2 years" + } else { + "almost 2 years" + } + .to_string() } else { let years = distance / 31_536_000; let remaining_months = (distance % 31_536_000) / 2_592_000; if remaining_months < 3 { - format!("about {} years", years) + if hide_prefix { + format!("{} years", years) + } else { + format!("about {} years", years) + } + .to_string() } else if remaining_months < 9 { - format!("over {} years", years) + if hide_prefix { + format!("{} years", years) + } else { + format!("over {} years", years) + } + .to_string() } else { - format!("almost {} years", years + 1) + if hide_prefix { + format!("{} years", years + 1) + } else { + format!("almost {} years", years + 1) + } + .to_string() } }; @@ -132,10 +251,11 @@ pub fn format_distance( base_date: NaiveDateTime, include_seconds: bool, add_suffix: bool, + hide_prefix: bool, ) -> String { let distance = distance_in_seconds(date.to_naive(), base_date); - distance_string(distance, include_seconds, add_suffix) + distance_string(distance, include_seconds, add_suffix, hide_prefix) } /// Get the time difference between a date and now as relative human readable string. @@ -165,10 +285,11 @@ pub fn format_distance_from_now( datetime: DateTimeType, include_seconds: bool, add_suffix: bool, + hide_prefix: bool, ) -> String { let now = chrono::offset::Local::now().naive_local(); - format_distance(datetime, now, include_seconds, add_suffix) + format_distance(datetime, now, include_seconds, add_suffix, hide_prefix) } #[cfg(test)] @@ -187,7 +308,7 @@ mod tests { assert_eq!( "about 2 hours", - format_distance(date, base_date.to_naive(), false, false) + format_distance(date, base_date.to_naive(), false, false, false) ); } @@ -202,7 +323,7 @@ mod tests { assert_eq!( "about 2 hours from now", - format_distance(date, base_date.to_naive(), false, true) + format_distance(date, base_date.to_naive(), false, true, false) ); } @@ -215,41 +336,89 @@ mod tests { assert_eq!( "over 54 years ago", - format_distance_from_now(date, false, true) + format_distance_from_now(date, false, true, false) ); } #[test] fn test_format_distance_string() { - assert_eq!(distance_string(3, false, false), "less than a minute"); - assert_eq!(distance_string(7, false, false), "less than a minute"); - assert_eq!(distance_string(13, false, false), "less than a minute"); - assert_eq!(distance_string(21, false, false), "less than a minute"); - assert_eq!(distance_string(45, false, false), "1 minute"); - assert_eq!(distance_string(61, false, false), "1 minute"); - assert_eq!(distance_string(1920, false, false), "32 minutes"); - assert_eq!(distance_string(3902, false, false), "about 1 hour"); - assert_eq!(distance_string(18002, false, false), "about 5 hours"); - assert_eq!(distance_string(86470, false, false), "1 day"); - assert_eq!(distance_string(345880, false, false), "4 days"); - assert_eq!(distance_string(2764800, false, false), "about 1 month"); - assert_eq!(distance_string(5184000, false, false), "about 2 months"); - assert_eq!(distance_string(10368000, false, false), "4 months"); - assert_eq!(distance_string(34694000, false, false), "about 1 year"); - assert_eq!(distance_string(47310000, false, false), "over 1 year"); - assert_eq!(distance_string(61503000, false, false), "almost 2 years"); - assert_eq!(distance_string(160854000, false, false), "about 5 years"); - assert_eq!(distance_string(236550000, false, false), "over 7 years"); - assert_eq!(distance_string(249166000, false, false), "almost 8 years"); + assert_eq!( + distance_string(3, false, false, false), + "less than a minute" + ); + assert_eq!( + distance_string(7, false, false, false), + "less than a minute" + ); + assert_eq!( + distance_string(13, false, false, false), + "less than a minute" + ); + assert_eq!( + distance_string(21, false, false, false), + "less than a minute" + ); + assert_eq!(distance_string(45, false, false, false), "1 minute"); + assert_eq!(distance_string(61, false, false, false), "1 minute"); + assert_eq!(distance_string(1920, false, false, false), "32 minutes"); + assert_eq!(distance_string(3902, false, false, false), "about 1 hour"); + assert_eq!(distance_string(18002, false, false, false), "about 5 hours"); + assert_eq!(distance_string(86470, false, false, false), "1 day"); + assert_eq!(distance_string(345880, false, false, false), "4 days"); + assert_eq!( + distance_string(2764800, false, false, false), + "about 1 month" + ); + assert_eq!( + distance_string(5184000, false, false, false), + "about 2 months" + ); + assert_eq!(distance_string(10368000, false, false, false), "4 months"); + assert_eq!( + distance_string(34694000, false, false, false), + "about 1 year" + ); + assert_eq!( + distance_string(47310000, false, false, false), + "over 1 year" + ); + assert_eq!( + distance_string(61503000, false, false, false), + "almost 2 years" + ); + assert_eq!( + distance_string(160854000, false, false, false), + "about 5 years" + ); + assert_eq!( + distance_string(236550000, false, false, false), + "over 7 years" + ); + assert_eq!( + distance_string(249166000, false, false, false), + "almost 8 years" + ); } #[test] fn test_format_distance_string_include_seconds() { - assert_eq!(distance_string(3, true, false), "less than 5 seconds"); - assert_eq!(distance_string(7, true, false), "less than 10 seconds"); - assert_eq!(distance_string(13, true, false), "less than 20 seconds"); - assert_eq!(distance_string(21, true, false), "half a minute"); - assert_eq!(distance_string(45, true, false), "less than a minute"); - assert_eq!(distance_string(61, true, false), "1 minute"); + assert_eq!( + distance_string(3, true, false, false), + "less than 5 seconds" + ); + assert_eq!( + distance_string(7, true, false, false), + "less than 10 seconds" + ); + assert_eq!( + distance_string(13, true, false, false), + "less than 20 seconds" + ); + assert_eq!(distance_string(21, true, false, false), "half a minute"); + assert_eq!( + distance_string(45, true, false, false), + "less than a minute" + ); + assert_eq!(distance_string(61, true, false, false), "1 minute"); } }