diff --git a/Cargo.lock b/Cargo.lock index 618d4dd..e9eefcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,6 +196,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -869,6 +879,7 @@ dependencies = [ "anyhow", "axum", "chrono", + "chrono-tz", "noisebell-common", "reqwest", "rusqlite", @@ -985,6 +996,24 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1494,6 +1523,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + [[package]] name = "slab" version = "0.4.12" diff --git a/remote/cache-service/Cargo.toml b/remote/cache-service/Cargo.toml index d375e79..f1d481a 100644 --- a/remote/cache-service/Cargo.toml +++ b/remote/cache-service/Cargo.toml @@ -10,6 +10,7 @@ workspace = true anyhow = "1.0" axum = "0.8" chrono = "0.4" +chrono-tz = "0.10" noisebell-common = { path = "../noisebell-common" } reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } rusqlite = { version = "0.33", features = ["bundled"] } diff --git a/remote/cache-service/src/api.rs b/remote/cache-service/src/api.rs index 72d7352..aee065c 100644 --- a/remote/cache-service/src/api.rs +++ b/remote/cache-service/src/api.rs @@ -5,6 +5,7 @@ use axum::http::{header, HeaderMap, StatusCode}; use axum::response::{IntoResponse, Response}; use axum::Json; use chrono::{DateTime, Utc}; +use chrono_tz::America::Los_Angeles; use noisebell_common::{validate_bearer, CacheStatusResponse, DoorStatus, WebhookPayload}; use tokio::sync::Mutex; use tracing::{error, info}; @@ -115,7 +116,9 @@ fn unix_now() -> u64 { fn format_full_timestamp(ts: u64) -> String { DateTime::from_timestamp(ts as i64, 0) - .map(|dt: DateTime| dt.format("%A, %B %-d, %Y at %-I:%M:%S %p UTC").to_string()) + .map(|dt: DateTime| { + dt.with_timezone(&Los_Angeles).format("%A, %B %-d, %Y at %-I:%M:%S %p %Z").to_string() + }) .unwrap_or_else(|| format!("unix timestamp {ts}")) } @@ -364,6 +367,18 @@ mod tests { assert_eq!(format_duration(3_723), "1 hour and 2 minutes"); } + #[test] + fn format_full_timestamp_uses_san_francisco_timezone() { + let ts = 1_711_312_063; + let expected = DateTime::from_timestamp(ts, 0) + .unwrap() + .with_timezone(&Los_Angeles) + .format("%A, %B %-d, %Y at %-I:%M:%S %p %Z") + .to_string(); + + assert_eq!(format_full_timestamp(ts as u64), expected); + } + #[test] fn status_summary_includes_since_and_last_checked() { let summary = status_summary(DoorStatus::Open, Some(1_000), Some(1_125), 1_180);