time: more time-ago granularity in months/years
before: 1y after: 1y 8mo etc Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
@@ -10,73 +10,75 @@ const ONE_WEEK_IN_SECONDS: u64 = 604_800;
|
|||||||
const ONE_MONTH_IN_SECONDS: u64 = 2_592_000; // 30 days
|
const ONE_MONTH_IN_SECONDS: u64 = 2_592_000; // 30 days
|
||||||
const ONE_YEAR_IN_SECONDS: u64 = 31_536_000; // 365 days
|
const ONE_YEAR_IN_SECONDS: u64 = 31_536_000; // 365 days
|
||||||
|
|
||||||
// Range boundary constants for match patterns
|
/// Calculate relative time between two timestamps, with two units only
|
||||||
const MAX_SECONDS: u64 = ONE_MINUTE_IN_SECONDS - 1;
|
/// when the scale is large enough (e.g., "1y 6m", "5d 4h"),
|
||||||
const MAX_SECONDS_FOR_MINUTES: u64 = ONE_HOUR_IN_SECONDS - 1;
|
/// but not for hours/minutes/seconds.
|
||||||
const MAX_SECONDS_FOR_HOURS: u64 = ONE_DAY_IN_SECONDS - 1;
|
|
||||||
const MAX_SECONDS_FOR_DAYS: u64 = ONE_WEEK_IN_SECONDS - 1;
|
|
||||||
const MAX_SECONDS_FOR_WEEKS: u64 = ONE_MONTH_IN_SECONDS - 1;
|
|
||||||
const MAX_SECONDS_FOR_MONTHS: u64 = ONE_YEAR_IN_SECONDS - 1;
|
|
||||||
|
|
||||||
/// Calculate relative time between two timestamps
|
|
||||||
fn time_ago_between(i18n: &mut Localization, timestamp: u64, now: u64) -> String {
|
fn time_ago_between(i18n: &mut Localization, timestamp: u64, now: u64) -> String {
|
||||||
// Determine if the timestamp is in the future or the past
|
|
||||||
let duration = if now >= timestamp {
|
let duration = if now >= timestamp {
|
||||||
now.saturating_sub(timestamp)
|
now.saturating_sub(timestamp)
|
||||||
} else {
|
} else {
|
||||||
timestamp.saturating_sub(now)
|
timestamp.saturating_sub(now)
|
||||||
};
|
};
|
||||||
|
|
||||||
let time_str = match duration {
|
// Special-case: "now" for < 3 seconds
|
||||||
0..=2 => tr!(
|
if duration <= 2 {
|
||||||
|
let s = tr!(
|
||||||
i18n,
|
i18n,
|
||||||
"now",
|
"now",
|
||||||
"Relative time for very recent events (less than 3 seconds)"
|
"Relative time for very recent events (less than 3 seconds)"
|
||||||
),
|
);
|
||||||
3..=MAX_SECONDS => tr!(
|
return if timestamp > now { format!("+{s}") } else { s };
|
||||||
i18n,
|
}
|
||||||
"{count}s",
|
|
||||||
"Relative time in seconds",
|
// Break into buckets
|
||||||
count = duration
|
let years = duration / ONE_YEAR_IN_SECONDS;
|
||||||
),
|
let rem_y = duration % ONE_YEAR_IN_SECONDS;
|
||||||
ONE_MINUTE_IN_SECONDS..=MAX_SECONDS_FOR_MINUTES => tr!(
|
|
||||||
i18n,
|
let months = rem_y / ONE_MONTH_IN_SECONDS;
|
||||||
"{count}m",
|
let rem_m = rem_y % ONE_MONTH_IN_SECONDS;
|
||||||
"Relative time in minutes",
|
|
||||||
count = duration / ONE_MINUTE_IN_SECONDS
|
let weeks = rem_m / ONE_WEEK_IN_SECONDS;
|
||||||
),
|
let rem_w = rem_m % ONE_WEEK_IN_SECONDS;
|
||||||
ONE_HOUR_IN_SECONDS..=MAX_SECONDS_FOR_HOURS => tr!(
|
|
||||||
i18n,
|
let days = rem_w / ONE_DAY_IN_SECONDS;
|
||||||
"{count}h",
|
let rem_d = rem_w % ONE_DAY_IN_SECONDS;
|
||||||
"Relative time in hours",
|
|
||||||
count = duration / ONE_HOUR_IN_SECONDS
|
let hours = rem_d / ONE_HOUR_IN_SECONDS;
|
||||||
),
|
let rem_h = rem_d % ONE_HOUR_IN_SECONDS;
|
||||||
ONE_DAY_IN_SECONDS..=MAX_SECONDS_FOR_DAYS => tr!(
|
|
||||||
i18n,
|
let mins = rem_h / ONE_MINUTE_IN_SECONDS;
|
||||||
"{count}d",
|
let secs = rem_h % ONE_MINUTE_IN_SECONDS;
|
||||||
"Relative time in days",
|
|
||||||
count = duration / ONE_DAY_IN_SECONDS
|
let mut parts: Vec<String> = Vec::with_capacity(2);
|
||||||
),
|
|
||||||
ONE_WEEK_IN_SECONDS..=MAX_SECONDS_FOR_WEEKS => tr!(
|
let mut push_part = |count: u64, key: &str, desc: &str| {
|
||||||
i18n,
|
if count > 0 && parts.len() < 2 {
|
||||||
"{count}w",
|
parts.push(tr!(i18n, key, desc, count = count));
|
||||||
"Relative time in weeks",
|
}
|
||||||
count = duration / ONE_WEEK_IN_SECONDS
|
|
||||||
),
|
|
||||||
ONE_MONTH_IN_SECONDS..=MAX_SECONDS_FOR_MONTHS => tr!(
|
|
||||||
i18n,
|
|
||||||
"{count}mo",
|
|
||||||
"Relative time in months",
|
|
||||||
count = duration / ONE_MONTH_IN_SECONDS
|
|
||||||
),
|
|
||||||
_ => tr!(
|
|
||||||
i18n,
|
|
||||||
"{count}y",
|
|
||||||
"Relative time in years",
|
|
||||||
count = duration / ONE_YEAR_IN_SECONDS
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if years > 0 {
|
||||||
|
push_part(years, "{count}y", "Relative time in years");
|
||||||
|
push_part(months, "{count}mo", "Relative time in months");
|
||||||
|
} else if months > 0 {
|
||||||
|
push_part(months, "{count}mo", "Relative time in months");
|
||||||
|
push_part(weeks, "{count}w", "Relative time in weeks");
|
||||||
|
} else if weeks > 0 {
|
||||||
|
push_part(weeks, "{count}w", "Relative time in weeks");
|
||||||
|
push_part(days, "{count}d", "Relative time in days");
|
||||||
|
} else if days > 0 {
|
||||||
|
push_part(days, "{count}d", "Relative time in days");
|
||||||
|
push_part(hours, "{count}h", "Relative time in hours");
|
||||||
|
} else if hours > 0 {
|
||||||
|
push_part(hours, "{count}h", "Relative time in hours");
|
||||||
|
} else if mins > 0 {
|
||||||
|
push_part(mins, "{count}m", "Relative time in minutes");
|
||||||
|
} else {
|
||||||
|
push_part(secs.max(1), "{count}s", "Relative time in seconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
let time_str = parts.join(" ");
|
||||||
|
|
||||||
if timestamp > now {
|
if timestamp > now {
|
||||||
format!("+{time_str}")
|
format!("+{time_str}")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user