refactor(time): update to icu 2.0.0

This commit is contained in:
Vukašin Vojinović 2025-09-12 14:55:31 +02:00 • committed by Ashley Wulber
parent 0e5054a4c5
commit 2533b5542b
4 changed files with 308 additions and 478 deletions

View file

@ -91,8 +91,8 @@ num-derive = "0.4"
pwhash = "1"
[dependencies.icu]
version = "1.5.0"
features = ["experimental", "compiled_data", "icu_datetime_experimental"]
version = "2.0.0"
features = ["compiled_data"]
[dependencies.i18n-embed]
version = "0.16.0"

View file

@ -14,9 +14,13 @@ use cosmic::{
};
use cosmic_settings_page::{self as page, Section, section};
use icu::{
calendar::{DateTime, Iso, types::IsoWeekday, week},
datetime::DateTimeFormatter,
locid::Locale,
calendar::{Gregorian, types::Weekday, week},
datetime::{
DateTimeFormatter, DateTimeFormatterPreferences, fieldsets,
input::{Date, DateTime, Time},
options::TimePrecision,
},
locale::{Locale, preferences::extensions::unicode::keywords::HourCycle},
};
use slab::Slab;
use slotmap::{Key, SlotMap};
@ -43,7 +47,7 @@ pub struct Page {
ntp_enabled: bool,
show_date_in_top_panel: bool,
timezone_context: bool,
local_time: Option<DateTime<Iso>>,
local_time: Option<DateTime<Gregorian>>,
timezone: Option<usize>,
timezone_list: Vec<String>,
timezone_search: String,
@ -513,67 +517,51 @@ fn locale() -> Result<Locale, Box<dyn std::error::Error>> {
Ok(locale)
}
fn format_date(date: &DateTime<Iso>, military: bool, show_seconds: bool) -> String {
fn format_date(date: &DateTime<Gregorian>, military: bool, show_seconds: bool) -> String {
let Ok(locale) = locale() else {
return String::new();
};
let mut bag = icu::datetime::options::components::Bag::empty();
let mut prefs = DateTimeFormatterPreferences::from(locale);
prefs.hour_cycle = Some(if military {
HourCycle::H23
} else {
HourCycle::H12
});
bag.year = Some(icu::datetime::options::components::Year::Numeric);
bag.day = Some(icu::datetime::options::components::Day::NumericDayOfMonth);
bag.month = Some(icu::datetime::options::components::Month::Long);
bag.hour = Some(icu::datetime::options::components::Numeric::Numeric);
bag.minute = Some(icu::datetime::options::components::Numeric::Numeric);
bag.second = show_seconds.then_some(icu::datetime::options::components::Numeric::Numeric);
bag.preferences = Some(icu::datetime::options::preferences::Bag::from_hour_cycle(
if military {
icu::datetime::options::preferences::HourCycle::H23
} else {
icu::datetime::options::preferences::HourCycle::H12
},
));
let mut fs = fieldsets::YMDT::long();
if !show_seconds {
fs = fs.with_time_precision(TimePrecision::Minute);
}
let dtf = DateTimeFormatter::try_new_experimental(&locale.into(), bag.into()).unwrap();
let dtf = DateTimeFormatter::try_new(prefs, fs).unwrap();
dtf.format(&date.to_any())
.expect("can't format value")
.to_string()
dtf.format(date).to_string()
}
fn update_local_time() -> DateTime<Iso> {
fn update_local_time() -> DateTime<Gregorian> {
let now = chrono::Local::now();
DateTime::try_new_gregorian_datetime(
now.year(),
now.month() as u8,
now.day() as u8,
now.hour() as u8,
now.minute() as u8,
now.second() as u8,
)
.unwrap()
.to_iso()
DateTime {
date: Date::try_new_gregorian(now.year(), now.month() as u8, now.day() as u8).unwrap(),
time: Time::try_new(now.hour() as u8, now.minute() as u8, now.second() as u8, 0).unwrap(),
}
}
fn get_locale_default_24h() -> bool {
let Ok(locale) = locale() else { return false };
let test_time = icu::calendar::DateTime::try_new_gregorian_datetime(2024, 1, 1, 13, 0, 0)
.unwrap()
.to_iso();
let test_time = DateTime {
date: Date::try_new_gregorian(2024, 1, 1).unwrap(),
time: Time::try_new(13, 0, 0, 0).unwrap(),
};
let bag = icu::datetime::options::length::Bag::from_time_style(
icu::datetime::options::length::Time::Medium,
);
let Ok(dtf) =
icu::datetime::DateTimeFormatter::try_new_experimental(&locale.into(), bag.into())
else {
let prefs = DateTimeFormatterPreferences::from(locale);
let Ok(dtf) = DateTimeFormatter::try_new(prefs, fieldsets::T::medium()) else {
return false;
};
let formatted = dtf.format(&test_time.to_any()).unwrap().to_string();
let formatted = dtf.format(&test_time).to_string();
// If we see "13" in the output, it's 24-hour format
// If we see "1" (but not "13"), it's 12-hour format
@ -582,17 +570,17 @@ fn get_locale_default_24h() -> bool {
fn get_locale_default_first_day() -> usize {
let Ok(locale) = locale() else { return 6 };
let Ok(week_calc) = week::WeekCalculator::try_new(&locale.into()) else {
let Ok(week_info) = week::WeekInformation::try_new(week::WeekPreferences::from(&locale)) else {
return 6;
};
match week_calc.first_weekday {
IsoWeekday::Monday => 0,
IsoWeekday::Tuesday => 1,
IsoWeekday::Wednesday => 2,
IsoWeekday::Thursday => 3,
IsoWeekday::Friday => 4,
IsoWeekday::Saturday => 5,
IsoWeekday::Sunday => 6,
match week_info.first_weekday {
Weekday::Monday => 0,
Weekday::Tuesday => 1,
Weekday::Wednesday => 2,
Weekday::Thursday => 3,
Weekday::Friday => 4,
Weekday::Saturday => 5,
Weekday::Sunday => 6,
}
}

View file

@ -15,16 +15,14 @@ use cosmic_config::{ConfigGet, ConfigSet};
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use eyre::Context;
use fixed_decimal::FixedDecimal;
use icu::calendar::DateTime;
use icu::datetime::options::components::{self, Bag};
use icu::datetime::options::length;
use icu::datetime::{DateTimeFormatter, DateTimeFormatterOptions};
use icu::decimal::FixedDecimalFormatter;
use icu::decimal::options::FixedDecimalFormatterOptions;
use icu::{
calendar::{types::IsoWeekday, week},
locid::Locale,
calendar::{types::Weekday, week},
datetime::{
DateTimeFormatter, DateTimeFormatterPreferences, fieldsets,
input::{Date, DateTime, Time},
},
decimal::{DecimalFormatter, input::Decimal},
locale::Locale,
};
use locales_rs as locale;
use slotmap::{DefaultKey, SlotMap};
@ -409,27 +407,19 @@ impl Page {
.next()
.unwrap_or("en_US");
let Ok(locale) = icu::locid::Locale::from_str(time_locale) else {
let Ok(locale) = Locale::from_str(time_locale) else {
return String::new();
};
let mut bag = Bag::empty();
bag.day = Some(components::Day::TwoDigitDayOfMonth);
bag.month = Some(components::Month::TwoDigit);
bag.year = Some(components::Year::Numeric);
let prefs = DateTimeFormatterPreferences::from(locale);
let dtf = DateTimeFormatter::try_new(prefs, fieldsets::YMD::medium()).unwrap();
let options = icu::datetime::DateTimeFormatterOptions::Components(bag);
let datetime = DateTime {
date: Date::try_new_gregorian(1776, 7, 4).unwrap(),
time: Time::try_new(12, 0, 0, 0).unwrap(),
};
let dtf = DateTimeFormatter::try_new_experimental(&locale.into(), options).unwrap();
let datetime = DateTime::try_new_gregorian_datetime(1776, 7, 4, 12, 0, 0)
.unwrap()
.to_iso()
.to_any();
dtf.format(&datetime)
.expect("can't format value")
.to_string()
dtf.format(&datetime).to_string()
}
fn formatted_dates_and_times(&self) -> String {
@ -442,23 +432,19 @@ impl Page {
.next()
.unwrap_or("en_US");
let Ok(locale) = icu::locid::Locale::from_str(time_locale) else {
let Ok(locale) = Locale::from_str(time_locale) else {
return String::new();
};
let bag = length::Bag::from_date_time_style(length::Date::Long, length::Time::Medium);
let options = DateTimeFormatterOptions::Length(bag);
let prefs = DateTimeFormatterPreferences::from(locale);
let dtf = DateTimeFormatter::try_new(prefs, fieldsets::YMDT::long()).unwrap();
let dtf = DateTimeFormatter::try_new_experimental(&locale.into(), options).unwrap();
let datetime = DateTime {
date: Date::try_new_gregorian(1776, 7, 4).unwrap(),
time: Time::try_new(13, 0, 0, 0).unwrap(),
};
let datetime = DateTime::try_new_gregorian_datetime(1776, 7, 4, 13, 0, 0)
.unwrap()
.to_iso()
.to_any();
dtf.format(&datetime)
.expect("can't format value")
.to_string()
dtf.format(&datetime).to_string()
}
fn formatted_time(&self) -> String {
@ -471,22 +457,19 @@ impl Page {
.next()
.unwrap_or("en_US");
let Ok(locale) = icu::locid::Locale::from_str(time_locale) else {
let Ok(locale) = Locale::from_str(time_locale) else {
return String::new();
};
let options = length::Bag::from_time_style(length::Time::Medium);
let prefs = DateTimeFormatterPreferences::from(locale);
let dtf = DateTimeFormatter::try_new(prefs, fieldsets::T::medium()).unwrap();
let dtf = DateTimeFormatter::try_new_experimental(&locale.into(), options.into()).unwrap();
let datetime = DateTime {
date: Date::try_new_gregorian(1776, 7, 4).unwrap(),
time: Time::try_new(13, 0, 0, 0).unwrap(),
};
let datetime = DateTime::try_new_gregorian_datetime(1776, 7, 4, 13, 0, 0)
.unwrap()
.to_iso()
.to_any();
dtf.format(&datetime)
.expect("can't format value")
.to_string()
dtf.format(&datetime).to_string()
}
fn formatted_numbers(&self) -> String {
@ -499,13 +482,12 @@ impl Page {
.next()
.unwrap_or("en_US");
let Ok(locale) = icu::locid::Locale::from_str(numerical_locale) else {
let Ok(locale) = Locale::from_str(numerical_locale) else {
return String::new();
};
let options = FixedDecimalFormatterOptions::default();
let formatter = FixedDecimalFormatter::try_new(&locale.into(), options).unwrap();
let mut value = FixedDecimal::from(123456789);
let formatter = DecimalFormatter::try_new(locale.into(), Default::default()).unwrap();
let mut value = Decimal::from(123456789);
value.multiply_pow10(-2);
formatter.format(&value).to_string()
@ -967,24 +949,17 @@ fn get_default_24h(locale: String) -> bool {
return false;
};
let test_time = icu::calendar::DateTime::try_new_gregorian_datetime(2024, 1, 1, 13, 0, 0)
.unwrap()
.to_iso();
let test_time = DateTime {
date: Date::try_new_gregorian(2024, 1, 1).unwrap(),
time: Time::try_new(13, 0, 0, 0).unwrap(),
};
let bag = icu::datetime::options::length::Bag::from_time_style(
icu::datetime::options::length::Time::Medium,
);
let Ok(dtf) =
icu::datetime::DateTimeFormatter::try_new_experimental(&locale.into(), bag.into())
else {
let prefs = DateTimeFormatterPreferences::from(locale);
let Ok(dtf) = DateTimeFormatter::try_new(prefs, fieldsets::T::medium()) else {
return false;
};
let formatted = match dtf.format(&test_time.to_any()) {
Ok(formatted) => formatted.to_string(),
Err(_) => return false,
};
let formatted = dtf.format(&test_time).to_string();
// If we see "13" in the output, it's 24-hour format
// If we see "1" (but not "13"), it's 12-hour format
@ -995,18 +970,18 @@ fn get_default_first_day(locale: String) -> usize {
let Ok(locale) = parse_locale(locale) else {
return 6;
};
let Ok(week_calc) = week::WeekCalculator::try_new(&locale.into()) else {
let Ok(week_info) = week::WeekInformation::try_new(week::WeekPreferences::from(&locale)) else {
return 6;
};
match week_calc.first_weekday {
IsoWeekday::Monday => 0,
IsoWeekday::Tuesday => 1,
IsoWeekday::Wednesday => 2,
IsoWeekday::Thursday => 3,
IsoWeekday::Friday => 4,
IsoWeekday::Saturday => 5,
IsoWeekday::Sunday => 6,
match week_info.first_weekday {
Weekday::Monday => 0,
Weekday::Tuesday => 1,
Weekday::Wednesday => 2,
Weekday::Thursday => 3,
Weekday::Friday => 4,
Weekday::Saturday => 5,
Weekday::Sunday => 6,
}
}