qubit_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}