mirror of
https://github.com/zed-industries/zed.git
synced 2026-04-18 07:47:53 +00:00
time_format: Add Windows implementation (#50227)
<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>
This commit is contained in:
Generated
+1
@@ -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::*;
|
||||
|
||||
Reference in New Issue
Block a user