
Adds docs for: - distance_in_seconds - distance_string - naive_format_distance - naive_format_distance_from_now
231 lines
8.6 KiB
Rust
231 lines
8.6 KiB
Rust
use chrono::NaiveDateTime;
|
|
|
|
/// 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.
|
|
///
|
|
/// ## Arguments
|
|
///
|
|
/// * `date` - A NaiveDateTime object representing the date of interest
|
|
/// * `base_date` - A NaiveDateTime object representing the base date against which the comparison is made
|
|
fn distance_in_seconds(date: NaiveDateTime, base_date: NaiveDateTime) -> i64 {
|
|
let duration = date.signed_duration_since(base_date);
|
|
-duration.num_seconds()
|
|
}
|
|
|
|
/// 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 {
|
|
let suffix = if distance < 0 { " from now" } else { " ago" };
|
|
|
|
let d = distance.abs();
|
|
|
|
let minutes = d / 60;
|
|
let hours = d / 3600;
|
|
let days = d / 86400;
|
|
let months = d / 2592000;
|
|
let years = d / 31536000;
|
|
|
|
let string = if d < 5 && include_seconds {
|
|
"less than 5 seconds".to_string()
|
|
} else if d < 10 && include_seconds {
|
|
"less than 10 seconds".to_string()
|
|
} else if d < 20 && include_seconds {
|
|
"less than 20 seconds".to_string()
|
|
} else if d < 40 && include_seconds {
|
|
"half a minute".to_string()
|
|
} else if d < 60 && include_seconds {
|
|
"less than a minute".to_string()
|
|
} else if d < 90 && include_seconds {
|
|
"1 minute".to_string()
|
|
} else if d < 30 {
|
|
"less than a minute".to_string()
|
|
} else if d < 90 {
|
|
"1 minute".to_string()
|
|
} else if d < 2700 {
|
|
format!("{} minutes", minutes)
|
|
} else if d < 5400 {
|
|
"about 1 hour".to_string()
|
|
} else if d < 86400 {
|
|
format!("about {} hours", hours)
|
|
} else if d < 172800 {
|
|
"1 day".to_string()
|
|
} else if d < 2592000 {
|
|
format!("{} days", days)
|
|
} else if d < 5184000 {
|
|
"about 1 month".to_string()
|
|
} else if d < 7776000 {
|
|
"about 2 months".to_string()
|
|
} else if d < 31540000 {
|
|
format!("{} months", months)
|
|
} else if d < 39425000 {
|
|
"about 1 year".to_string()
|
|
} else if d < 55195000 {
|
|
"over 1 year".to_string()
|
|
} else if d < 63080000 {
|
|
"almost 2 years".to_string()
|
|
} else {
|
|
let years = d / 31536000;
|
|
let remaining_months = (d % 31536000) / 2592000;
|
|
|
|
if remaining_months < 3 {
|
|
format!("about {} years", years)
|
|
} else if remaining_months < 9 {
|
|
format!("over {} years", years)
|
|
} else {
|
|
format!("almost {} years", years + 1)
|
|
}
|
|
};
|
|
|
|
if add_suffix {
|
|
return format!("{}{}", string, suffix);
|
|
} else {
|
|
string
|
|
}
|
|
}
|
|
|
|
/// Get the time difference between two dates into a relative human readable string.
|
|
///
|
|
/// For example, "less than a minute ago", "about 2 hours ago", "3 months from now", etc.
|
|
///
|
|
/// Use [naive_format_distance_from_now] to compare a NaiveDateTime against now.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `date` - The NaiveDateTime to compare.
|
|
/// * `base_date` - The NaiveDateTime to compare against.
|
|
/// * `include_seconds` - A boolean. If true, distances less than a minute are more detailed
|
|
/// * `add_suffix` - A boolean. If true, result indicates if the time is in the past or future
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use chrono::DateTime;
|
|
/// use ui2::utils::naive_format_distance;
|
|
///
|
|
/// fn time_between_moon_landings() -> String {
|
|
/// let date = DateTime::parse_from_rfc3339("1969-07-20T00:00:00Z").unwrap().naive_local();
|
|
/// let base_date = DateTime::parse_from_rfc3339("1972-12-14T00:00:00Z").unwrap().naive_local();
|
|
/// format!("There was {} between the first and last crewed moon landings.", naive_format_distance(date, base_date, false, false))
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// Output: `"There was about 3 years between the first and last crewed moon landings."`
|
|
pub fn naive_format_distance(
|
|
date: NaiveDateTime,
|
|
base_date: NaiveDateTime,
|
|
include_seconds: bool,
|
|
add_suffix: bool,
|
|
) -> String {
|
|
let distance = distance_in_seconds(date, base_date);
|
|
|
|
distance_string(distance, include_seconds, add_suffix)
|
|
}
|
|
|
|
/// Get the time difference between a date and now as relative human readable string.
|
|
///
|
|
/// For example, "less than a minute ago", "about 2 hours ago", "3 months from now", etc.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `datetime` - The NaiveDateTime to compare with the current time.
|
|
/// * `include_seconds` - A boolean. If true, distances less than a minute are more detailed
|
|
/// * `add_suffix` - A boolean. If true, result indicates if the time is in the past or future
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use chrono::DateTime;
|
|
/// use ui2::utils::naive_format_distance_from_now;
|
|
///
|
|
/// fn time_since_first_moon_landing() -> String {
|
|
/// let date = DateTime::parse_from_rfc3339("1969-07-20T00:00:00Z").unwrap().naive_local();
|
|
/// format!("It's been {} since Apollo 11 first landed on the moon.", naive_format_distance_from_now(date, false, false))
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// Output: `It's been over 54 years since Apollo 11 first landed on the moon.`
|
|
pub fn naive_format_distance_from_now(
|
|
datetime: NaiveDateTime,
|
|
include_seconds: bool,
|
|
add_suffix: bool,
|
|
) -> String {
|
|
let now = chrono::offset::Local::now().naive_local();
|
|
|
|
naive_format_distance(datetime, now, include_seconds, add_suffix)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use chrono::NaiveDateTime;
|
|
|
|
#[test]
|
|
fn test_naive_format_distance() {
|
|
let date =
|
|
NaiveDateTime::from_timestamp_opt(9600, 0).expect("Invalid NaiveDateTime for date");
|
|
let base_date =
|
|
NaiveDateTime::from_timestamp_opt(0, 0).expect("Invalid NaiveDateTime for base_date");
|
|
|
|
assert_eq!(
|
|
"about 2 hours",
|
|
naive_format_distance(date, base_date, false, false)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_naive_format_distance_with_suffix() {
|
|
let date =
|
|
NaiveDateTime::from_timestamp_opt(9600, 0).expect("Invalid NaiveDateTime for date");
|
|
let base_date =
|
|
NaiveDateTime::from_timestamp_opt(0, 0).expect("Invalid NaiveDateTime for base_date");
|
|
|
|
assert_eq!(
|
|
"about 2 hours from now",
|
|
naive_format_distance(date, base_date, false, true)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_naive_format_distance_from_now() {
|
|
let date = NaiveDateTime::parse_from_str("1969-07-20T00:00:00Z", "%Y-%m-%dT%H:%M:%SZ")
|
|
.expect("Invalid NaiveDateTime for date");
|
|
|
|
assert_eq!(
|
|
"over 54 years ago",
|
|
naive_format_distance_from_now(date, false, true)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_naive_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");
|
|
}
|
|
|
|
#[test]
|
|
fn test_naive_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");
|
|
}
|
|
}
|