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(
25 not(unix),
26 not(windows),
27 not(all(
28 target_arch = "wasm32",
29 feature = "wasmbind",
30 not(any(target_os = "emscripten", target_os = "wasi"))
31 ))
32))]
33mod inner {
34 use crate::{FixedOffset, LocalResult, NaiveDateTime};
35
36 pub(super) fn offset_from_utc_datetime(_utc_time: &NaiveDateTime) -> LocalResult<FixedOffset> {
37 LocalResult::Single(FixedOffset::east_opt(0).unwrap())
38 }
39
40 pub(super) fn offset_from_local_datetime(
41 _local_time: &NaiveDateTime,
42 ) -> LocalResult<FixedOffset> {
43 LocalResult::Single(FixedOffset::east_opt(0).unwrap())
44 }
45}
46
47#[cfg(all(
48 target_arch = "wasm32",
49 feature = "wasmbind",
50 not(any(target_os = "emscripten", target_os = "wasi"))
51))]
52mod inner {
53 use crate::{FixedOffset, LocalResult, NaiveDateTime};
54
55 pub(super) fn offset_from_utc_datetime(_utc: &NaiveDateTime) -> LocalResult<FixedOffset> {
56 let offset = js_sys::Date::new_0().get_timezone_offset();
57 LocalResult::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap())
58 }
59
60 pub(super) fn offset_from_local_datetime(local: &NaiveDateTime) -> LocalResult<FixedOffset> {
61 offset_from_utc_datetime(local)
62 }
63}
64
65#[cfg(unix)]
66mod tz_info;
67
68/// The local timescale. This is implemented via the standard `time` crate.
69///
70/// Using the [`TimeZone`](./trait.TimeZone.html) methods
71/// on the Local struct is the preferred way to construct `DateTime<Local>`
72/// instances.
73///
74/// # Example
75///
76/// ```
77/// use chrono::{Local, DateTime, TimeZone};
78///
79/// let dt1: DateTime<Local> = Local::now();
80/// let dt2: DateTime<Local> = Local.timestamp_opt(0, 0).unwrap();
81/// assert!(dt1 >= dt2);
82/// ```
83#[derive(Copy, Clone, Debug)]
84#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
85#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
86pub struct Local;
87
88impl Local {
89 /// Returns a `Date` which corresponds to the current date.
90 #[deprecated(since = "0.4.23", note = "use `Local::now()` instead")]
91 #[allow(deprecated)]
92 #[must_use]
93 pub fn today() -> Date<Local> {
94 Local::now().date()
95 }
96
97 /// Returns a `DateTime` which corresponds to the current date and time.
98 #[cfg(not(all(
99 target_arch = "wasm32",
100 feature = "wasmbind",
101 not(any(target_os = "emscripten", target_os = "wasi"))
102 )))]
103 #[must_use]
104 pub fn now() -> DateTime<Local> {
105 Utc::now().with_timezone(&Local)
106 }
107
108 /// Returns a `DateTime` which corresponds to the current date and time.
109 #[cfg(all(
110 target_arch = "wasm32",
111 feature = "wasmbind",
112 not(any(target_os = "emscripten", target_os = "wasi"))
113 ))]
114 #[must_use]
115 pub fn now() -> DateTime<Local> {
116 use super::Utc;
117 let now: DateTime<Utc> = super::Utc::now();
118
119 // Workaround missing timezone logic in `time` crate
120 let offset =
121 FixedOffset::west_opt((js_sys::Date::new_0().get_timezone_offset() as i32) * 60)
122 .unwrap();
123 DateTime::from_utc(now.naive_utc(), offset)
124 }
125}
126
127impl TimeZone for Local {
128 type Offset = FixedOffset;
129
130 fn from_offset(_offset: &FixedOffset) -> Local {
131 Local
132 }
133
134 #[allow(deprecated)]
135 fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
136 // Get the offset at local midnight.
137 self.offset_from_local_datetime(&local.and_time(NaiveTime::MIN))
138 }
139
140 fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
141 inner::offset_from_local_datetime(local)
142 }
143
144 #[allow(deprecated)]
145 fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
146 // Get the offset at midnight.
147 self.offset_from_utc_datetime(&utc.and_time(NaiveTime::MIN))
148 }
149
150 fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
151 inner::offset_from_utc_datetime(utc).unwrap()
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::Local;
158 use crate::offset::TimeZone;
159 use crate::{Datelike, Duration, Utc};
160
161 #[test]
162 fn verify_correct_offsets() {
163 let now = Local::now();
164 let from_local = Local.from_local_datetime(&now.naive_local()).unwrap();
165 let from_utc = Local.from_utc_datetime(&now.naive_utc());
166
167 assert_eq!(now.offset().local_minus_utc(), from_local.offset().local_minus_utc());
168 assert_eq!(now.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
169
170 assert_eq!(now, from_local);
171 assert_eq!(now, from_utc);
172 }
173
174 #[test]
175 fn verify_correct_offsets_distant_past() {
176 // let distant_past = Local::now() - Duration::days(365 * 100);
177 let distant_past = Local::now() - Duration::days(250 * 31);
178 let from_local = Local.from_local_datetime(&distant_past.naive_local()).unwrap();
179 let from_utc = Local.from_utc_datetime(&distant_past.naive_utc());
180
181 assert_eq!(distant_past.offset().local_minus_utc(), from_local.offset().local_minus_utc());
182 assert_eq!(distant_past.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
183
184 assert_eq!(distant_past, from_local);
185 assert_eq!(distant_past, from_utc);
186 }
187
188 #[test]
189 fn verify_correct_offsets_distant_future() {
190 let distant_future = Local::now() + Duration::days(250 * 31);
191 let from_local = Local.from_local_datetime(&distant_future.naive_local()).unwrap();
192 let from_utc = Local.from_utc_datetime(&distant_future.naive_utc());
193
194 assert_eq!(
195 distant_future.offset().local_minus_utc(),
196 from_local.offset().local_minus_utc()
197 );
198 assert_eq!(distant_future.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
199
200 assert_eq!(distant_future, from_local);
201 assert_eq!(distant_future, from_utc);
202 }
203
204 #[test]
205 fn test_local_date_sanity_check() {
206 // issue #27
207 assert_eq!(Local.with_ymd_and_hms(2999, 12, 28, 0, 0, 0).unwrap().day(), 28);
208 }
209
210 #[test]
211 fn test_leap_second() {
212 // issue #123
213 let today = Utc::now().date_naive();
214
215 if let Some(dt) = today.and_hms_milli_opt(15, 2, 59, 1000) {
216 let timestr = dt.time().to_string();
217 // the OS API may or may not support the leap second,
218 // but there are only two sensible options.
219 assert!(
220 timestr == "15:02:60" || timestr == "15:03:00",
221 "unexpected timestr {:?}",
222 timestr
223 );
224 }
225
226 if let Some(dt) = today.and_hms_milli_opt(15, 2, 3, 1234) {
227 let timestr = dt.time().to_string();
228 assert!(
229 timestr == "15:02:03.234" || timestr == "15:02:04.234",
230 "unexpected timestr {:?}",
231 timestr
232 );
233 }
234 }
235
236 /// Test Issue #866
237 #[test]
238 fn test_issue_866() {
239 #[allow(deprecated)]
240 let local_20221106 = Local.ymd(2022, 11, 6);
241 let _dt_20221106 = local_20221106.and_hms_milli_opt(1, 2, 59, 1000).unwrap();
242 }
243}
244