chrono/offset/local/
mod.rs

1// This is a part of Chrono.
2// See README.md and LICENSE.txt for details.
3
4//! The local (system) time zone.
5
6#[cfg(feature = "rkyv")]
7use rkyv::{Archive, Deserialize, Serialize};
8
9use super::fixed::FixedOffset;
10use super::{LocalResult, TimeZone};
11use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
12#[allow(deprecated)]
13use crate::Date;
14use crate::{DateTime, Utc};
15
16#[cfg(unix)]
17#[path = "unix.rs"]
18mod inner;
19
20#[cfg(windows)]
21#[path = "windows.rs"]
22mod inner;
23
24#[cfg(all(windows, feature = "clock"))]
25#[allow(unreachable_pub)]
26mod win_bindings;
27
28#[cfg(all(
29    not(unix),
30    not(windows),
31    not(all(
32        target_arch = "wasm32",
33        feature = "wasmbind",
34        not(any(target_os = "emscripten", target_os = "wasi"))
35    ))
36))]
37mod inner {
38    use crate::{FixedOffset, LocalResult, NaiveDateTime};
39
40    pub(super) fn offset_from_utc_datetime(_utc_time: &NaiveDateTime) -> LocalResult<FixedOffset> {
41        LocalResult::Single(FixedOffset::east_opt(0).unwrap())
42    }
43
44    pub(super) fn offset_from_local_datetime(
45        _local_time: &NaiveDateTime,
46    ) -> LocalResult<FixedOffset> {
47        LocalResult::Single(FixedOffset::east_opt(0).unwrap())
48    }
49}
50
51#[cfg(all(
52    target_arch = "wasm32",
53    feature = "wasmbind",
54    not(any(target_os = "emscripten", target_os = "wasi"))
55))]
56mod inner {
57    use crate::{Datelike, FixedOffset, LocalResult, NaiveDateTime, Timelike};
58
59    pub(super) fn offset_from_utc_datetime(utc: &NaiveDateTime) -> LocalResult<FixedOffset> {
60        let offset = js_sys::Date::from(utc.and_utc()).get_timezone_offset();
61        LocalResult::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap())
62    }
63
64    pub(super) fn offset_from_local_datetime(local: &NaiveDateTime) -> LocalResult<FixedOffset> {
65        let mut year = local.year();
66        if year < 100 {
67            // The API in `js_sys` does not let us create a `Date` with negative years.
68            // And values for years from `0` to `99` map to the years `1900` to `1999`.
69            // Shift the value by a multiple of 400 years until it is `>= 100`.
70            let shift_cycles = (year - 100).div_euclid(400);
71            year -= shift_cycles * 400;
72        }
73        let js_date = js_sys::Date::new_with_year_month_day_hr_min_sec(
74            year as u32,
75            local.month0() as i32,
76            local.day() as i32,
77            local.hour() as i32,
78            local.minute() as i32,
79            local.second() as i32,
80            // ignore milliseconds, our representation of leap seconds may be problematic
81        );
82        let offset = js_date.get_timezone_offset();
83        // We always get a result, even if this time does not exist or is ambiguous.
84        LocalResult::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap())
85    }
86}
87
88#[cfg(unix)]
89mod tz_info;
90
91/// The local timescale. This is implemented via the standard `time` crate.
92///
93/// Using the [`TimeZone`](./trait.TimeZone.html) methods
94/// on the Local struct is the preferred way to construct `DateTime<Local>`
95/// instances.
96///
97/// # Example
98///
99/// ```
100/// use chrono::{Local, DateTime, TimeZone};
101///
102/// let dt1: DateTime<Local> = Local::now();
103/// let dt2: DateTime<Local> = Local.timestamp_opt(0, 0).unwrap();
104/// assert!(dt1 >= dt2);
105/// ```
106#[derive(Copy, Clone, Debug)]
107#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
108#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
109pub struct Local;
110
111impl Local {
112    /// Returns a `Date` which corresponds to the current date.
113    #[deprecated(since = "0.4.23", note = "use `Local::now()` instead")]
114    #[allow(deprecated)]
115    #[must_use]
116    pub fn today() -> Date<Local> {
117        Local::now().date()
118    }
119
120    /// Returns a `DateTime<Local>` which corresponds to the current date, time and offset from
121    /// UTC.
122    ///
123    /// See also the similar [`Utc::now()`] which returns `DateTime<Utc>`, i.e. without the local
124    /// offset.
125    ///
126    /// # Example
127    ///
128    /// ```
129    /// # #![allow(unused_variables)]
130    /// # use chrono::{DateTime, FixedOffset, Local};
131    /// // Current local time
132    /// let now = Local::now();
133    ///
134    /// // Current local date
135    /// let today = now.date_naive();
136    ///
137    /// // Current local time, converted to `DateTime<FixedOffset>`
138    /// let now_fixed_offset = Local::now().fixed_offset();
139    /// // or
140    /// let now_fixed_offset: DateTime<FixedOffset> = Local::now().into();
141    ///
142    /// // Current time in some timezone (let's use +05:00)
143    /// // Note that it is usually more efficient to use `Utc::now` for this use case.
144    /// let offset = FixedOffset::east_opt(5 * 60 * 60).unwrap();
145    /// let now_with_offset = Local::now().with_timezone(&offset);
146    /// ```
147    pub fn now() -> DateTime<Local> {
148        Utc::now().with_timezone(&Local)
149    }
150}
151
152impl TimeZone for Local {
153    type Offset = FixedOffset;
154
155    fn from_offset(_offset: &FixedOffset) -> Local {
156        Local
157    }
158
159    #[allow(deprecated)]
160    fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
161        // Get the offset at local midnight.
162        self.offset_from_local_datetime(&local.and_time(NaiveTime::MIN))
163    }
164
165    fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
166        inner::offset_from_local_datetime(local)
167    }
168
169    #[allow(deprecated)]
170    fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
171        // Get the offset at midnight.
172        self.offset_from_utc_datetime(&utc.and_time(NaiveTime::MIN))
173    }
174
175    fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
176        inner::offset_from_utc_datetime(utc).unwrap()
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::Local;
183    use crate::offset::TimeZone;
184    use crate::{Datelike, Duration, Utc};
185
186    #[test]
187    fn verify_correct_offsets() {
188        let now = Local::now();
189        let from_local = Local.from_local_datetime(&now.naive_local()).unwrap();
190        let from_utc = Local.from_utc_datetime(&now.naive_utc());
191
192        assert_eq!(now.offset().local_minus_utc(), from_local.offset().local_minus_utc());
193        assert_eq!(now.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
194
195        assert_eq!(now, from_local);
196        assert_eq!(now, from_utc);
197    }
198
199    #[test]
200    fn verify_correct_offsets_distant_past() {
201        // let distant_past = Local::now() - Duration::days(365 * 100);
202        let distant_past = Local::now() - Duration::days(250 * 31);
203        let from_local = Local.from_local_datetime(&distant_past.naive_local()).unwrap();
204        let from_utc = Local.from_utc_datetime(&distant_past.naive_utc());
205
206        assert_eq!(distant_past.offset().local_minus_utc(), from_local.offset().local_minus_utc());
207        assert_eq!(distant_past.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
208
209        assert_eq!(distant_past, from_local);
210        assert_eq!(distant_past, from_utc);
211    }
212
213    #[test]
214    fn verify_correct_offsets_distant_future() {
215        let distant_future = Local::now() + Duration::days(250 * 31);
216        let from_local = Local.from_local_datetime(&distant_future.naive_local()).unwrap();
217        let from_utc = Local.from_utc_datetime(&distant_future.naive_utc());
218
219        assert_eq!(
220            distant_future.offset().local_minus_utc(),
221            from_local.offset().local_minus_utc()
222        );
223        assert_eq!(distant_future.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
224
225        assert_eq!(distant_future, from_local);
226        assert_eq!(distant_future, from_utc);
227    }
228
229    #[test]
230    fn test_local_date_sanity_check() {
231        // issue #27
232        assert_eq!(Local.with_ymd_and_hms(2999, 12, 28, 0, 0, 0).unwrap().day(), 28);
233    }
234
235    #[test]
236    fn test_leap_second() {
237        // issue #123
238        let today = Utc::now().date_naive();
239
240        if let Some(dt) = today.and_hms_milli_opt(15, 2, 59, 1000) {
241            let timestr = dt.time().to_string();
242            // the OS API may or may not support the leap second,
243            // but there are only two sensible options.
244            assert!(
245                timestr == "15:02:60" || timestr == "15:03:00",
246                "unexpected timestr {:?}",
247                timestr
248            );
249        }
250
251        if let Some(dt) = today.and_hms_milli_opt(15, 2, 3, 1234) {
252            let timestr = dt.time().to_string();
253            assert!(
254                timestr == "15:02:03.234" || timestr == "15:02:04.234",
255                "unexpected timestr {:?}",
256                timestr
257            );
258        }
259    }
260}