Refactor timestamp formatting in Git UI components to use `chrono` for local time calculations (#41005)

Viraj Bhartiya created

- Updated `blame_ui.rs`, `branch_picker.rs`, `commit_tooltip.rs`, and
`commit_view.rs` to replace the previous timestamp formatting with
`chrono` for better accuracy in local time representation.
- Introduced `chrono::Local::now().offset().local_minus_utc()` to obtain
the local offset for timestamp formatting.

Closes #40878

Release Notes:
- Improved timestamp handling in various Git UI components for enhanced
user experience.

Change summary

Cargo.lock                          |  1 -
Cargo.toml                          |  1 +
crates/git_ui/Cargo.toml            |  1 -
crates/git_ui/src/blame_ui.rs       | 10 ++++++----
crates/git_ui/src/branch_picker.rs  |  6 ++++--
crates/git_ui/src/commit_tooltip.rs |  9 +++++----
crates/git_ui/src/commit_view.rs    |  4 +++-
crates/git_ui/src/stash_picker.rs   |  5 +----
8 files changed, 20 insertions(+), 17 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -7091,7 +7091,6 @@ dependencies = [
  "askpass",
  "buffer_diff",
  "call",
- "chrono",
  "cloud_llm_client",
  "collections",
  "command_palette_hooks",

Cargo.toml 🔗

@@ -663,6 +663,7 @@ time = { version = "0.3", features = [
     "serde",
     "serde-well-known",
     "formatting",
+    "local-offset",
 ] }
 tiny_http = "0.8"
 tokio = { version = "1" }

crates/git_ui/Cargo.toml 🔗

@@ -22,7 +22,6 @@ anyhow.workspace = true
 askpass.workspace = true
 buffer_diff.workspace = true
 call.workspace = true
-chrono.workspace = true
 cloud_llm_client.workspace = true
 collections.workspace = true
 command_palette_hooks.workspace = true

crates/git_ui/src/blame_ui.rs 🔗

@@ -16,7 +16,6 @@ use project::{git_store::Repository, project_settings::ProjectSettings};
 use settings::Settings as _;
 use theme::ThemeSettings;
 use time::OffsetDateTime;
-use time_format::format_local_timestamp;
 use ui::{ContextMenu, Divider, prelude::*, tooltip_container};
 use workspace::Workspace;
 
@@ -188,9 +187,11 @@ impl BlameRenderer for GitBlameRenderer {
             .get(..8)
             .map(|sha| sha.to_string().into())
             .unwrap_or_else(|| sha.clone());
-        let absolute_timestamp = format_local_timestamp(
+        let local_offset = time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC);
+        let absolute_timestamp = time_format::format_localized_timestamp(
             commit_time,
             OffsetDateTime::now_utc(),
+            local_offset,
             time_format::TimestampFormat::MediumAbsolute,
         );
         let link_color = cx.theme().colors().text_accent;
@@ -403,11 +404,12 @@ fn deploy_blame_entry_context_menu(
 fn blame_entry_relative_timestamp(blame_entry: &BlameEntry) -> String {
     match blame_entry.author_offset_date_time() {
         Ok(timestamp) => {
-            let local = chrono::Local::now().offset().local_minus_utc();
+            let local_offset =
+                time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC);
             time_format::format_localized_timestamp(
                 timestamp,
                 time::OffsetDateTime::now_utc(),
-                time::UtcOffset::from_whole_seconds(local).unwrap(),
+                local_offset,
                 time_format::TimestampFormat::Relative,
             )
         }

crates/git_ui/src/branch_picker.rs 🔗

@@ -14,7 +14,6 @@ use project::project_settings::ProjectSettings;
 use settings::Settings;
 use std::sync::Arc;
 use time::OffsetDateTime;
-use time_format::format_local_timestamp;
 use ui::{HighlightedLabel, ListItem, ListItemSpacing, Tooltip, prelude::*};
 use util::ResultExt;
 use workspace::notifications::DetachAndPromptErr;
@@ -447,9 +446,12 @@ impl PickerDelegate for BranchListDelegate {
                 let subject = commit.subject.clone();
                 let commit_time = OffsetDateTime::from_unix_timestamp(commit.commit_timestamp)
                     .unwrap_or_else(|_| OffsetDateTime::now_utc());
-                let formatted_time = format_local_timestamp(
+                let local_offset =
+                    time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC);
+                let formatted_time = time_format::format_localized_timestamp(
                     commit_time,
                     OffsetDateTime::now_utc(),
+                    local_offset,
                     time_format::TimestampFormat::Relative,
                 );
                 let author = commit.author_name.clone();

crates/git_ui/src/commit_tooltip.rs 🔗

@@ -14,7 +14,6 @@ use settings::Settings;
 use std::hash::Hash;
 use theme::ThemeSettings;
 use time::{OffsetDateTime, UtcOffset};
-use time_format::format_local_timestamp;
 use ui::{Avatar, Divider, IconButtonShape, prelude::*, tooltip_container};
 use workspace::Workspace;
 
@@ -190,9 +189,11 @@ impl Render for CommitTooltip {
             .map(|sha| sha.to_string().into())
             .unwrap_or_else(|| self.commit.sha.clone());
         let full_sha = self.commit.sha.to_string();
-        let absolute_timestamp = format_local_timestamp(
+        let local_offset = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC);
+        let absolute_timestamp = time_format::format_localized_timestamp(
             self.commit.commit_time,
             OffsetDateTime::now_utc(),
+            local_offset,
             time_format::TimestampFormat::MediumAbsolute,
         );
         let markdown_style = {
@@ -351,11 +352,11 @@ impl Render for CommitTooltip {
 fn blame_entry_timestamp(blame_entry: &BlameEntry, format: time_format::TimestampFormat) -> String {
     match blame_entry.author_offset_date_time() {
         Ok(timestamp) => {
-            let local = chrono::Local::now().offset().local_minus_utc();
+            let local_offset = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC);
             time_format::format_localized_timestamp(
                 timestamp,
                 time::OffsetDateTime::now_utc(),
-                UtcOffset::from_whole_seconds(local).unwrap(),
+                local_offset,
                 format,
             )
         }

crates/git_ui/src/commit_view.rs 🔗

@@ -415,12 +415,14 @@ fn format_commit(commit: &CommitDetails, is_stash: bool) -> String {
         commit.author_name, commit.author_email
     )
     .unwrap();
+    let local_offset = time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC);
     writeln!(
         &mut result,
         "Date:   {}",
-        time_format::format_local_timestamp(
+        time_format::format_localized_timestamp(
             time::OffsetDateTime::from_unix_timestamp(commit.commit_timestamp).unwrap(),
             time::OffsetDateTime::now_utc(),
+            local_offset,
             time_format::TimestampFormat::MediumAbsolute,
         ),
     )

crates/git_ui/src/stash_picker.rs 🔗

@@ -1,6 +1,5 @@
 use fuzzy::StringMatchCandidate;
 
-use chrono;
 use git::stash::StashEntry;
 use gpui::{
     Action, AnyElement, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
@@ -207,9 +206,7 @@ impl StashListDelegate {
         _window: &mut Window,
         cx: &mut Context<StashList>,
     ) -> Self {
-        let timezone =
-            UtcOffset::from_whole_seconds(chrono::Local::now().offset().local_minus_utc())
-                .unwrap_or(UtcOffset::UTC);
+        let timezone = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC);
 
         Self {
             matches: vec![],