Add FormatDistance struct, add hide_prefix option

Nate Butler created

Change summary

crates/ui2/src/utils/format_distance.rs | 265 ++++++++++++++++++++++----
1 file changed, 217 insertions(+), 48 deletions(-)

Detailed changes

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");
     }
 }