From 013c9f0420307e1a71c54c9d05adedc04839fc78 Mon Sep 17 00:00:00 2001 From: apricotbucket28 <71973804+apricotbucket28@users.noreply.github.com> Date: Thu, 18 Jul 2024 08:42:18 -0300 Subject: [PATCH] linux: Implement local time zone support (#14610) I decided to remove the GPUI APIs since `chrono` already provides this functionality, and is already been used for this purpose in other parts of the code (e.g. [here](https://github.com/zed-industries/zed/blob/80402a684066628793695d9303979ad993803740/crates/zed/src/main.rs#L756) or [here](https://github.com/zed-industries/zed/blob/80402a684066628793695d9303979ad993803740/crates/ui/src/utils/format_distance.rs#L258)) These usages end up calling the `time_format` crate, which takes in a `UtcOffset`. It's probably cleaner to rewrite the crate to take in `chrono` types, but that would require rewriting most of the code there. Release Notes: - linux: Use local time zone in chat and Git blame --- Cargo.lock | 2 ++ Cargo.toml | 1 - crates/collab_ui/Cargo.toml | 1 + crates/collab_ui/src/chat_panel.rs | 3 +- crates/collab_ui/src/notification_panel.rs | 3 +- crates/editor/Cargo.toml | 1 + crates/editor/src/blame_entry_tooltip.rs | 36 +++++++++----------- crates/editor/src/element.rs | 4 +-- crates/gpui/src/app.rs | 6 ---- crates/gpui/src/platform.rs | 2 -- crates/gpui/src/platform/linux/platform.rs | 5 --- crates/gpui/src/platform/mac/platform.rs | 9 ----- crates/gpui/src/platform/test/platform.rs | 4 --- crates/gpui/src/platform/windows/platform.rs | 21 ------------ 14 files changed, 26 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1a451075d..a8dfcbbabd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2564,6 +2564,7 @@ dependencies = [ "anyhow", "call", "channel", + "chrono", "client", "collections", "db", @@ -3593,6 +3594,7 @@ dependencies = [ "aho-corasick", "anyhow", "assets", + "chrono", "client", "clock", "collections", diff --git a/Cargo.toml b/Cargo.toml index 1c83a0d7fa..f3f584f636 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -457,7 +457,6 @@ features = [ "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", - "Win32_System_Time", "Win32_System_WinRT", "Win32_UI_Controls", "Win32_UI_HiDpi", diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index 305a10d5f3..6bfcca832b 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -32,6 +32,7 @@ test-support = [ anyhow.workspace = true call.workspace = true channel.workspace = true +chrono.workspace = true client.workspace = true collections.workspace = true db.workspace = true diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 107c983cf9..67d5047a51 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -111,6 +111,7 @@ impl ChatPanel { this.is_scrolled_to_bottom = !event.is_scrolled; })); + let local_offset = chrono::Local::now().offset().local_minus_utc(); let mut this = Self { fs, client, @@ -120,7 +121,7 @@ impl ChatPanel { active_chat: Default::default(), pending_serialization: Task::ready(None), message_editor: input_editor, - local_timezone: cx.local_timezone(), + local_timezone: UtcOffset::from_whole_seconds(local_offset).unwrap(), subscriptions: Vec::new(), is_scrolled_to_bottom: true, active: false, diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs index c8e58b1e85..08dee3686d 100644 --- a/crates/collab_ui/src/notification_panel.rs +++ b/crates/collab_ui/src/notification_panel.rs @@ -127,11 +127,12 @@ impl NotificationPanel { }, )); + let local_offset = chrono::Local::now().offset().local_minus_utc(); let mut this = Self { fs, client, user_store, - local_timezone: cx.local_timezone(), + local_timezone: UtcOffset::from_whole_seconds(local_offset).unwrap(), channel_store: ChannelStore::global(cx), notification_store: NotificationStore::global(cx), notification_list, diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index d5c7c2cded..dae32ff582 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -31,6 +31,7 @@ test-support = [ aho-corasick = "1.1" anyhow.workspace = true assets.workspace = true +chrono.workspace = true client.workspace = true clock.workspace = true collections.workspace = true diff --git a/crates/editor/src/blame_entry_tooltip.rs b/crates/editor/src/blame_entry_tooltip.rs index 7864338550..dd1b3a3ea5 100644 --- a/crates/editor/src/blame_entry_tooltip.rs +++ b/crates/editor/src/blame_entry_tooltip.rs @@ -8,6 +8,7 @@ use gpui::{ use settings::Settings; use std::hash::Hash; use theme::{ActiveTheme, ThemeSettings}; +use time::UtcOffset; use ui::{ div, h_flex, tooltip_container, v_flex, Avatar, Button, ButtonStyle, Clickable as _, Color, FluentBuilder, Icon, IconName, IconPosition, InteractiveElement as _, IntoElement, @@ -129,7 +130,7 @@ impl Render for BlameEntryTooltip { let author_email = self.blame_entry.author_mail.clone(); let short_commit_id = self.blame_entry.sha.display_short(); - let absolute_timestamp = blame_entry_absolute_timestamp(&self.blame_entry, cx); + let absolute_timestamp = blame_entry_absolute_timestamp(&self.blame_entry); let message = self .details @@ -247,30 +248,25 @@ impl Render for BlameEntryTooltip { } } -fn blame_entry_timestamp( - blame_entry: &BlameEntry, - format: time_format::TimestampFormat, - cx: &WindowContext, -) -> String { +fn blame_entry_timestamp(blame_entry: &BlameEntry, format: time_format::TimestampFormat) -> String { match blame_entry.author_offset_date_time() { - Ok(timestamp) => time_format::format_localized_timestamp( - timestamp, - time::OffsetDateTime::now_utc(), - cx.local_timezone(), - format, - ), + Ok(timestamp) => { + let local = chrono::Local::now().offset().local_minus_utc(); + time_format::format_localized_timestamp( + timestamp, + time::OffsetDateTime::now_utc(), + UtcOffset::from_whole_seconds(local).unwrap(), + format, + ) + } Err(_) => "Error parsing date".to_string(), } } -pub fn blame_entry_relative_timestamp(blame_entry: &BlameEntry, cx: &WindowContext) -> String { - blame_entry_timestamp(blame_entry, time_format::TimestampFormat::Relative, cx) +pub fn blame_entry_relative_timestamp(blame_entry: &BlameEntry) -> String { + blame_entry_timestamp(blame_entry, time_format::TimestampFormat::Relative) } -fn blame_entry_absolute_timestamp(blame_entry: &BlameEntry, cx: &WindowContext) -> String { - blame_entry_timestamp( - blame_entry, - time_format::TimestampFormat::MediumAbsolute, - cx, - ) +fn blame_entry_absolute_timestamp(blame_entry: &BlameEntry) -> String { + blame_entry_timestamp(blame_entry, time_format::TimestampFormat::MediumAbsolute) } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 08ce02a679..decbe56994 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3923,7 +3923,7 @@ fn render_inline_blame_entry( workspace: Option>, cx: &mut WindowContext<'_>, ) -> AnyElement { - let relative_timestamp = blame_entry_relative_timestamp(&blame_entry, cx); + let relative_timestamp = blame_entry_relative_timestamp(&blame_entry); let author = blame_entry.author.as_deref().unwrap_or_default(); let text = format!("{}, {}", author, relative_timestamp); @@ -3969,7 +3969,7 @@ fn render_blame_entry( }; last_used_color.replace((sha_color, blame_entry.sha)); - let relative_timestamp = blame_entry_relative_timestamp(&blame_entry, cx); + let relative_timestamp = blame_entry_relative_timestamp(&blame_entry); let short_commit_id = blame_entry.sha.display_short(); diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index adf512bc6a..76e2bb9a25 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -14,7 +14,6 @@ use derive_more::{Deref, DerefMut}; use futures::{channel::oneshot, future::LocalBoxFuture, Future}; use slotmap::SlotMap; use smol::future::FutureExt; -use time::UtcOffset; pub use async_context::*; use collections::{FxHashMap, FxHashSet, VecDeque}; @@ -647,11 +646,6 @@ impl AppContext { self.platform.restart(binary_path) } - /// Returns the local timezone at the platform level. - pub fn local_timezone(&self) -> UtcOffset { - self.platform.local_timezone() - } - /// Updates the http client assigned to GPUI pub fn update_http_client(&mut self, new_client: Arc) { self.http_client = new_client; diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 4b479111ae..e2b1a59035 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -60,7 +60,6 @@ pub(crate) use mac::*; pub use semantic_version::SemanticVersion; #[cfg(any(test, feature = "test-support"))] pub(crate) use test::*; -use time::UtcOffset; #[cfg(target_os = "windows")] pub(crate) use windows::*; @@ -159,7 +158,6 @@ pub(crate) trait Platform: 'static { "" } fn app_path(&self) -> Result; - fn local_timezone(&self) -> UtcOffset; fn path_for_auxiliary_executable(&self, name: &str) -> Result; fn set_cursor_style(&self, style: CursorStyle); diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index a9079779d9..c3b00d3c9c 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -30,7 +30,6 @@ use filedescriptor::FileDescriptor; use flume::{Receiver, Sender}; use futures::channel::oneshot; use parking_lot::Mutex; -use time::UtcOffset; use util::ResultExt; use wayland_client::Connection; use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape; @@ -397,10 +396,6 @@ impl Platform for P { fn set_dock_menu(&self, menu: Vec, keymap: &Keymap) {} - fn local_timezone(&self) -> UtcOffset { - UtcOffset::UTC - } - fn path_for_auxiliary_executable(&self, name: &str) -> Result { Err(anyhow::Error::msg( "Platform::path_for_auxiliary_executable is not implemented yet", diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 396797053a..de7da30d1a 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -49,7 +49,6 @@ use std::{ slice, str, sync::Arc, }; -use time::UtcOffset; use super::renderer; @@ -760,14 +759,6 @@ impl Platform for MacPlatform { } } - fn local_timezone(&self) -> UtcOffset { - unsafe { - let local_timezone: id = msg_send![class!(NSTimeZone), localTimeZone]; - let seconds_from_gmt: NSInteger = msg_send![local_timezone, secondsFromGMT]; - UtcOffset::from_whole_seconds(seconds_from_gmt.try_into().unwrap()).unwrap() - } - } - fn path_for_auxiliary_executable(&self, name: &str) -> Result { unsafe { let bundle: id = NSBundle::mainBundle(); diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index d13006aace..0483953028 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -257,10 +257,6 @@ impl Platform for TestPlatform { unimplemented!() } - fn local_timezone(&self) -> time::UtcOffset { - time::UtcOffset::UTC - } - fn path_for_auxiliary_executable(&self, _name: &str) -> Result { unimplemented!() } diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 60b06747db..3b2f5095e2 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -16,7 +16,6 @@ use futures::channel::oneshot::{self, Receiver}; use itertools::Itertools; use parking_lot::RwLock; use smallvec::SmallVec; -use time::UtcOffset; use windows::{ core::*, Win32::{ @@ -35,7 +34,6 @@ use windows::{ Ole::*, SystemInformation::*, Threading::*, - Time::*, }, UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*}, }, @@ -482,25 +480,6 @@ impl Platform for WindowsPlatform { Ok(std::env::current_exe()?) } - fn local_timezone(&self) -> UtcOffset { - let mut info = unsafe { std::mem::zeroed() }; - let ret = unsafe { GetTimeZoneInformation(&mut info) }; - if ret == TIME_ZONE_ID_INVALID { - log::error!( - "Unable to get local timezone: {}", - std::io::Error::last_os_error() - ); - return UtcOffset::UTC; - } - // Windows treat offset as: - // UTC = localtime + offset - // so we add a minus here - let hours = -info.Bias / 60; - let minutes = -info.Bias % 60; - - UtcOffset::from_hms(hours as _, minutes as _, 0).unwrap() - } - // todo(windows) fn path_for_auxiliary_executable(&self, name: &str) -> Result { Err(anyhow!("not yet implemented"))