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" )] |
7 | use rkyv::{Archive, Deserialize, Serialize}; |
8 | |
9 | use super::fixed::FixedOffset; |
10 | use super::{LocalResult, TimeZone}; |
11 | use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; |
12 | #[allow (deprecated)] |
13 | use crate::Date; |
14 | use crate::{DateTime, Utc}; |
15 | |
16 | #[cfg (unix)] |
17 | #[path = "unix.rs" ] |
18 | mod inner; |
19 | |
20 | #[cfg (windows)] |
21 | #[path = "windows.rs" ] |
22 | mod 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 | ))] |
33 | mod 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 | ))] |
52 | mod 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)] |
66 | mod 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))] |
86 | pub struct Local; |
87 | |
88 | impl 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 | |
127 | impl 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)] |
156 | mod 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 | |