use crate::Localize;
use fixed_decimal::{Decimal, FloatPrecision};
use icu_experimental::relativetime::{
RelativeTimeFormatter, RelativeTimeFormatterOptions, options::Numeric,
};
use icu_locale::Locale;
use jiff::{SpanRound, Timestamp, Unit, tz::TimeZone};
const FORMATTER_OPTIONS: RelativeTimeFormatterOptions = RelativeTimeFormatterOptions {
numeric: Numeric::Auto,
};
impl Localize for Timestamp {
fn localize_for(&self, locale: &Locale) -> String {
let current_timestamp = Self::now();
let current_datetime = current_timestamp.to_zoned(TimeZone::UTC).datetime();
let unformatted_span = current_timestamp
.until(*self)
.unwrap()
.round(
SpanRound::new()
.largest(Unit::Year)
.relative(current_datetime),
)
.unwrap();
let units: [(Unit, i64); 7] = [
(Unit::Year, i64::from(unformatted_span.get_years())),
(Unit::Month, i64::from(unformatted_span.get_months())),
(Unit::Week, i64::from(unformatted_span.get_weeks())),
(Unit::Day, i64::from(unformatted_span.get_days())),
(Unit::Hour, i64::from(unformatted_span.get_hours())),
(Unit::Minute, unformatted_span.get_minutes()),
(Unit::Second, unformatted_span.get_seconds()),
];
let selected_unit = units
.iter()
.find(|(_unit, value)| value.abs() > 0)
.map_or(Unit::Second, |(unit, _value)| *unit);
let rounding_options = SpanRound::new()
.smallest(selected_unit)
.largest(selected_unit)
.relative(current_datetime);
let formatted_span = current_timestamp
.until(*self)
.unwrap()
.round(rounding_options)
.unwrap();
let selected_value = match selected_unit {
Unit::Year => i64::from(formatted_span.get_years()),
Unit::Month => i64::from(formatted_span.get_months()),
Unit::Week => i64::from(formatted_span.get_weeks()),
Unit::Day => i64::from(formatted_span.get_days()),
Unit::Hour => i64::from(formatted_span.get_hours()),
Unit::Minute => formatted_span.get_minutes(),
Unit::Second => formatted_span.get_seconds(),
_ => unreachable!(),
};
let formatter = match selected_unit {
Unit::Year => {
RelativeTimeFormatter::try_new_long_year(locale.into(), FORMATTER_OPTIONS)
}
Unit::Month => {
RelativeTimeFormatter::try_new_long_month(locale.into(), FORMATTER_OPTIONS)
}
Unit::Week => {
RelativeTimeFormatter::try_new_long_week(locale.into(), FORMATTER_OPTIONS)
}
Unit::Day => RelativeTimeFormatter::try_new_long_day(locale.into(), FORMATTER_OPTIONS),
Unit::Hour => {
RelativeTimeFormatter::try_new_long_hour(locale.into(), FORMATTER_OPTIONS)
}
Unit::Minute => {
RelativeTimeFormatter::try_new_long_minute(locale.into(), FORMATTER_OPTIONS)
}
Unit::Second => {
RelativeTimeFormatter::try_new_long_second(locale.into(), FORMATTER_OPTIONS)
}
_ => unreachable!(),
}
.unwrap();
let decimal =
Decimal::try_from_f64(selected_value as f64, FloatPrecision::Integer).unwrap();
let formatted_text = formatter.format(decimal);
formatted_text.to_string()
}
}