Skip to main content

qubit_clock/clock/
zoned.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Timezone wrapper for clocks.
11//!
12//! This module provides [`Zoned`], a generic wrapper that adds timezone
13//! support to any clock implementation.
14//!
15
16use crate::{
17    Clock,
18    ControllableClock,
19    NanoClock,
20    ZonedClock,
21};
22use chrono::{
23    DateTime,
24    Duration,
25    TimeZone,
26    Utc,
27};
28use chrono_tz::Tz;
29use std::ops::Deref;
30
31/// A wrapper that adds timezone support to any clock.
32///
33/// `Zoned<C>` wraps any clock implementing [`Clock`] and adds timezone
34/// functionality by implementing [`ZonedClock`]. It can convert UTC time to
35/// local time in the specified timezone.
36///
37/// # Deref Behavior
38///
39/// This type implements [`Deref`] to allow direct access to the wrapped
40/// clock's methods. This is particularly useful when wrapping controllable
41/// clocks like [`MockClock`](crate::MockClock).
42///
43/// # Trait Forwarding
44///
45/// When the wrapped clock implements [`NanoClock`] or [`ControllableClock`],
46/// `Zoned<C>` implements the same trait and delegates calls to the wrapped
47/// clock. This lets `Zoned<MockClock>` be used as `dyn ControllableClock` and
48/// `Zoned<NanoMonotonicClock>` be used as `dyn NanoClock`.
49///
50/// # Examples
51///
52/// ```
53/// use qubit_clock::{Clock, ZonedClock, SystemClock, Zoned};
54/// use chrono_tz::Asia::Shanghai;
55///
56/// // Wrap a SystemClock with Shanghai timezone
57/// let clock = Zoned::new(SystemClock::new(), Shanghai);
58/// let local = clock.local_time();
59/// println!("Local time in Shanghai: {}", local);
60/// ```
61///
62/// ## Using with MockClock
63///
64/// ```
65/// use qubit_clock::{
66///     Clock, ZonedClock, ControllableClock, MockClock, Zoned
67/// };
68/// use chrono::{DateTime, Utc};
69/// use chrono_tz::Asia::Shanghai;
70///
71/// let mock = MockClock::new();
72/// let clock = Zoned::new(mock, Shanghai);
73///
74/// // Can use ZonedClock methods
75/// let local = clock.local_time();
76///
77/// // Can also use ControllableClock methods via Deref
78/// let time = DateTime::parse_from_rfc3339(
79///     "2024-01-01T00:00:00Z"
80/// ).unwrap().with_timezone(&Utc);
81/// clock.set_time(time);
82/// ```
83///
84#[derive(Debug, Clone)]
85pub struct Zoned<C: Clock> {
86    /// The wrapped clock.
87    clock: C,
88    /// The timezone for this clock.
89    timezone: Tz,
90}
91
92impl<C: Clock> Zoned<C> {
93    /// Creates a new `Zoned` clock wrapping the given clock with the
94    /// specified timezone.
95    ///
96    /// # Arguments
97    ///
98    /// * `clock` - The clock to wrap.
99    /// * `timezone` - The timezone to use for local time conversions.
100    ///
101    /// # Returns
102    ///
103    /// A new `Zoned<C>` instance.
104    ///
105    /// # Examples
106    ///
107    /// ```
108    /// use qubit_clock::{SystemClock, Zoned};
109    /// use chrono_tz::Asia::Shanghai;
110    ///
111    /// let clock = Zoned::new(SystemClock::new(), Shanghai);
112    /// ```
113    ///
114    #[inline]
115    pub fn new(clock: C, timezone: Tz) -> Self {
116        Zoned { clock, timezone }
117    }
118
119    /// Returns a reference to the inner clock.
120    ///
121    /// # Returns
122    ///
123    /// A reference to the wrapped clock.
124    ///
125    /// # Examples
126    ///
127    /// ```
128    /// use qubit_clock::{Clock, SystemClock, Zoned};
129    /// use chrono_tz::Asia::Shanghai;
130    ///
131    /// let clock = Zoned::new(SystemClock::new(), Shanghai);
132    /// let inner = clock.inner();
133    /// let millis = inner.millis();
134    /// ```
135    ///
136    #[inline]
137    pub fn inner(&self) -> &C {
138        &self.clock
139    }
140
141    /// Consumes the `Zoned` wrapper and returns the inner clock.
142    ///
143    /// # Returns
144    ///
145    /// The wrapped clock.
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// use qubit_clock::{SystemClock, Zoned};
151    /// use chrono_tz::Asia::Shanghai;
152    ///
153    /// let clock = Zoned::new(SystemClock::new(), Shanghai);
154    /// let inner = clock.into_inner();
155    /// ```
156    ///
157    #[inline]
158    pub fn into_inner(self) -> C {
159        self.clock
160    }
161}
162
163impl<C: Clock> Clock for Zoned<C> {
164    #[inline]
165    fn millis(&self) -> i64 {
166        self.clock.millis()
167    }
168
169    #[inline]
170    fn time(&self) -> DateTime<Utc> {
171        self.clock.time()
172    }
173}
174
175impl<C: Clock> ZonedClock for Zoned<C> {
176    #[inline]
177    fn timezone(&self) -> Tz {
178        self.timezone
179    }
180
181    #[inline]
182    fn local_time(&self) -> DateTime<Tz> {
183        self.timezone.from_utc_datetime(&self.time().naive_utc())
184    }
185}
186
187impl<C: NanoClock> NanoClock for Zoned<C> {
188    #[inline]
189    fn nanos(&self) -> i128 {
190        self.clock.nanos()
191    }
192
193    #[inline]
194    fn time_precise(&self) -> DateTime<Utc> {
195        self.clock.time_precise()
196    }
197}
198
199impl<C: ControllableClock> ControllableClock for Zoned<C> {
200    #[inline]
201    fn set_time(&self, instant: DateTime<Utc>) {
202        self.clock.set_time(instant);
203    }
204
205    #[inline]
206    fn add_duration(&self, duration: Duration) {
207        self.clock.add_duration(duration);
208    }
209
210    #[inline]
211    fn reset(&self) {
212        self.clock.reset();
213    }
214}
215
216impl<C: Clock> Deref for Zoned<C> {
217    type Target = C;
218
219    #[inline]
220    fn deref(&self) -> &Self::Target {
221        &self.clock
222    }
223}