diff --git a/Cargo.lock b/Cargo.lock index c28d777c3f4723fc5a498e3047de759e711dafad..18fd93aed13bebee782d4204bfbf095e750d7096 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17527,6 +17527,7 @@ dependencies = [ "core-foundation-sys", "sys-locale", "time", + "windows 0.61.3", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 148a909ccc8edb8f37ea7fd992ea6464c46ce0d5..39d331fd9ebde7ac0b861b6bf7dfc2ad28805c10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -783,11 +783,13 @@ zstd = "0.11" version = "0.61" features = [ "Foundation_Numerics", + "Globalization_DateTimeFormatting", "Storage_Search", "Storage_Streams", "System_Threading", "UI_ViewManagement", "Wdk_System_SystemServices", + "Win32_Foundation", "Win32_Globalization", "Win32_Graphics_Direct3D", "Win32_Graphics_Direct3D11", diff --git a/crates/etw_tracing/Cargo.toml b/crates/etw_tracing/Cargo.toml index 7f287307bc90e4462257fbeae8d5716dc5056ee7..c46e3b820a950f30f991f7de3dd27510db8825f8 100644 --- a/crates/etw_tracing/Cargo.toml +++ b/crates/etw_tracing/Cargo.toml @@ -21,10 +21,4 @@ workspace.workspace = true [target.'cfg(target_os = "windows")'.dependencies] wprcontrol = { git = "https://github.com/zed-industries/wprcontrol", rev = "cd811f7" } windows-core = "0.61" -windows = { workspace = true, features = [ - "Win32_Foundation", - "Win32_System_Com", - "Win32_System_Ole", - "Win32_System_Variant", - "Win32_UI_Shell", -] } +windows.workspace = true diff --git a/crates/time_format/Cargo.toml b/crates/time_format/Cargo.toml index b598d19887e128a0c5951c1d1bd5ec42f27f975b..7f5f2d9f1b56666036816d43bfa3564bf9721f05 100644 --- a/crates/time_format/Cargo.toml +++ b/crates/time_format/Cargo.toml @@ -19,3 +19,6 @@ time.workspace = true [target.'cfg(target_os = "macos")'.dependencies] core-foundation.workspace = true core-foundation-sys.workspace = true + +[target.'cfg(target_os = "windows")'.dependencies] +windows.workspace = true diff --git a/crates/time_format/src/time_format.rs b/crates/time_format/src/time_format.rs index 25a7ae84232b69570e8c800c5955e684a13dc08a..bbf214623eb4b5b9dd978a675551c25f5e937a8d 100644 --- a/crates/time_format/src/time_format.rs +++ b/crates/time_format/src/time_format.rs @@ -86,10 +86,25 @@ fn format_absolute_date( macos::format_date(×tamp) } } - #[cfg(not(target_os = "macos"))] + #[cfg(target_os = "windows")] + { + if !enhanced_date_formatting { + return windows::format_date(×tamp); + } + + let timestamp_date = timestamp.date(); + let reference_date = reference.date(); + if timestamp_date == reference_date { + "Today".to_string() + } else if reference_date.previous_day() == Some(timestamp_date) { + "Yesterday".to_string() + } else { + windows::format_date(×tamp) + } + } + #[cfg(not(any(target_os = "macos", target_os = "windows")))] { // todo(linux) respect user's date/time preferences - // todo(windows) respect user's date/time preferences let current_locale = CURRENT_LOCALE .get_or_init(|| sys_locale::get_locale().unwrap_or_else(|| String::from("en-US"))); format_timestamp_naive_date( @@ -105,10 +120,13 @@ fn format_absolute_time(timestamp: OffsetDateTime) -> String { { macos::format_time(×tamp) } - #[cfg(not(target_os = "macos"))] + #[cfg(target_os = "windows")] + { + windows::format_time(×tamp) + } + #[cfg(not(any(target_os = "macos", target_os = "windows")))] { // todo(linux) respect user's date/time preferences - // todo(windows) respect user's date/time preferences let current_locale = CURRENT_LOCALE .get_or_init(|| sys_locale::get_locale().unwrap_or_else(|| String::from("en-US"))); format_timestamp_naive_time( @@ -123,7 +141,7 @@ fn format_absolute_timestamp( reference: OffsetDateTime, #[allow(unused_variables)] enhanced_date_formatting: bool, ) -> String { - #[cfg(target_os = "macos")] + #[cfg(any(target_os = "macos", target_os = "windows"))] { if !enhanced_date_formatting { return format!( @@ -147,10 +165,9 @@ fn format_absolute_timestamp( ) } } - #[cfg(not(target_os = "macos"))] + #[cfg(not(any(target_os = "macos", target_os = "windows")))] { // todo(linux) respect user's date/time preferences - // todo(windows) respect user's date/time preferences format_timestamp_fallback(timestamp, reference) } } @@ -176,10 +193,25 @@ fn format_absolute_date_medium( macos::format_date_medium(×tamp) } } - #[cfg(not(target_os = "macos"))] + #[cfg(target_os = "windows")] + { + if !enhanced_formatting { + return windows::format_date_medium(×tamp); + } + + let timestamp_date = timestamp.date(); + let reference_date = reference.date(); + if timestamp_date == reference_date { + "Today".to_string() + } else if reference_date.previous_day() == Some(timestamp_date) { + "Yesterday".to_string() + } else { + windows::format_date_medium(×tamp) + } + } + #[cfg(not(any(target_os = "macos", target_os = "windows")))] { // todo(linux) respect user's date/time preferences - // todo(windows) respect user's date/time preferences let current_locale = CURRENT_LOCALE .get_or_init(|| sys_locale::get_locale().unwrap_or_else(|| String::from("en-US"))); if !enhanced_formatting { @@ -212,7 +244,11 @@ fn format_absolute_timestamp_medium( { format_absolute_date_medium(timestamp, reference, false) } - #[cfg(not(target_os = "macos"))] + #[cfg(target_os = "windows")] + { + format_absolute_date_medium(timestamp, reference, false) + } + #[cfg(not(any(target_os = "macos", target_os = "windows")))] { // todo(linux) respect user's date/time preferences // todo(windows) respect user's date/time preferences @@ -360,7 +396,7 @@ fn format_timestamp_naive_date( } } -#[cfg(not(target_os = "macos"))] +#[cfg(not(any(target_os = "macos", target_os = "windows")))] fn format_timestamp_naive_date_medium( timestamp_local: OffsetDateTime, is_12_hour_time: bool, @@ -415,10 +451,10 @@ pub fn format_timestamp_naive( } } -#[cfg(not(target_os = "macos"))] +#[cfg(not(any(target_os = "macos", target_os = "windows")))] static CURRENT_LOCALE: std::sync::OnceLock = std::sync::OnceLock::new(); -#[cfg(not(target_os = "macos"))] +#[cfg(not(any(target_os = "macos", target_os = "windows")))] fn format_timestamp_fallback(timestamp: OffsetDateTime, reference: OffsetDateTime) -> String { let current_locale = CURRENT_LOCALE .get_or_init(|| sys_locale::get_locale().unwrap_or_else(|| String::from("en-US"))); @@ -428,7 +464,7 @@ fn format_timestamp_fallback(timestamp: OffsetDateTime, reference: OffsetDateTim } /// Returns `true` if the locale is recognized as a 12-hour time locale. -#[cfg(not(target_os = "macos"))] +#[cfg(not(any(target_os = "macos", target_os = "windows")))] fn is_12_hour_time_by_locale(locale: &str) -> bool { [ "es-MX", "es-CO", "es-SV", "es-NI", @@ -522,6 +558,57 @@ mod macos { } } +#[cfg(target_os = "windows")] +mod windows { + use windows::Globalization::DateTimeFormatting::DateTimeFormatter; + + pub fn format_time(timestamp: &time::OffsetDateTime) -> String { + format_with_formatter(DateTimeFormatter::ShortTime(), timestamp, true) + } + + pub fn format_date(timestamp: &time::OffsetDateTime) -> String { + format_with_formatter(DateTimeFormatter::ShortDate(), timestamp, false) + } + + pub fn format_date_medium(timestamp: &time::OffsetDateTime) -> String { + format_with_formatter( + DateTimeFormatter::CreateDateTimeFormatter(windows::core::h!( + "month.abbreviated day year.full" + )), + timestamp, + false, + ) + } + + fn format_with_formatter( + formatter: windows::core::Result, + timestamp: &time::OffsetDateTime, + is_time: bool, + ) -> String { + formatter + .and_then(|formatter| formatter.Format(to_winrt_datetime(timestamp))) + .map(|hstring| hstring.to_string()) + .unwrap_or_else(|_| { + if is_time { + super::format_timestamp_naive_time(*timestamp, true) + } else { + super::format_timestamp_naive_date(*timestamp, *timestamp, true) + } + }) + } + + fn to_winrt_datetime(timestamp: &time::OffsetDateTime) -> windows::Foundation::DateTime { + // DateTime uses 100-nanosecond intervals since January 1, 1601 (UTC). + const WINDOWS_EPOCH: time::OffsetDateTime = time::macros::datetime!(1601-01-01 0:00 UTC); + let duration_since_winrt_epoch = *timestamp - WINDOWS_EPOCH; + let universal_time = duration_since_winrt_epoch.whole_nanoseconds() / 100; + + windows::Foundation::DateTime { + UniversalTime: universal_time as i64, + } + } +} + #[cfg(test)] mod tests { use super::*;