Cargo.lock 🔗
@@ -17527,6 +17527,7 @@ dependencies = [
"core-foundation-sys",
"sys-locale",
"time",
+ "windows 0.61.3",
]
[[package]]
scuzqy and John Tur created
<img width="827" height="643" alt="图片"
src="https://github.com/user-attachments/assets/de6279fa-c224-460d-8210-3eada416aca5"
/>
Release Notes:
- Date and time formatting on Windows now respects the system time
formatting preferences.
---------
Co-authored-by: John Tur <john-tur@outlook.com>
Cargo.lock | 1
Cargo.toml | 2
crates/etw_tracing/Cargo.toml | 8 -
crates/time_format/Cargo.toml | 3
crates/time_format/src/time_format.rs | 115 +++++++++++++++++++++++++---
5 files changed, 108 insertions(+), 21 deletions(-)
@@ -17527,6 +17527,7 @@ dependencies = [
"core-foundation-sys",
"sys-locale",
"time",
+ "windows 0.61.3",
]
[[package]]
@@ -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",
@@ -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
@@ -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
@@ -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<String> = 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<DateTimeFormatter>,
+ 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::*;