1 | // This is a part of Chrono. |
2 | // See README.md and LICENSE.txt for details. |
3 | |
4 | //! ISO 8601 date and time with time zone. |
5 | |
6 | #[cfg (feature = "alloc" )] |
7 | extern crate alloc; |
8 | |
9 | #[cfg (all(not(feature = "std" ), feature = "alloc" ))] |
10 | use alloc::string::{String, ToString}; |
11 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
12 | use core::borrow::Borrow; |
13 | use core::cmp::Ordering; |
14 | use core::fmt::Write; |
15 | use core::ops::{Add, AddAssign, Sub, SubAssign}; |
16 | use core::{fmt, hash, str}; |
17 | #[cfg (feature = "std" )] |
18 | use std::string::ToString; |
19 | #[cfg (any(feature = "std" , test))] |
20 | use std::time::{SystemTime, UNIX_EPOCH}; |
21 | |
22 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
23 | use crate::format::DelayedFormat; |
24 | #[cfg (feature = "unstable-locales" )] |
25 | use crate::format::Locale; |
26 | use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems}; |
27 | use crate::format::{Fixed, Item}; |
28 | use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime}; |
29 | #[cfg (feature = "clock" )] |
30 | use crate::offset::Local; |
31 | use crate::offset::{FixedOffset, Offset, TimeZone, Utc}; |
32 | use crate::oldtime::Duration as OldDuration; |
33 | #[allow (deprecated)] |
34 | use crate::Date; |
35 | use crate::Months; |
36 | use crate::{Datelike, Timelike, Weekday}; |
37 | |
38 | #[cfg (feature = "rkyv" )] |
39 | use rkyv::{Archive, Deserialize, Serialize}; |
40 | |
41 | #[cfg (feature = "rustc-serialize" )] |
42 | pub(super) mod rustc_serialize; |
43 | |
44 | /// documented at re-export site |
45 | #[cfg (feature = "serde" )] |
46 | pub(super) mod serde; |
47 | |
48 | #[cfg (test)] |
49 | mod tests; |
50 | |
51 | /// Specific formatting options for seconds. This may be extended in the |
52 | /// future, so exhaustive matching in external code is not recommended. |
53 | /// |
54 | /// See the `TimeZone::to_rfc3339_opts` function for usage. |
55 | #[derive (Clone, Copy, Debug, Eq, PartialEq, Hash)] |
56 | pub enum SecondsFormat { |
57 | /// Format whole seconds only, with no decimal point nor subseconds. |
58 | Secs, |
59 | |
60 | /// Use fixed 3 subsecond digits. This corresponds to |
61 | /// [Fixed::Nanosecond3](format/enum.Fixed.html#variant.Nanosecond3). |
62 | Millis, |
63 | |
64 | /// Use fixed 6 subsecond digits. This corresponds to |
65 | /// [Fixed::Nanosecond6](format/enum.Fixed.html#variant.Nanosecond6). |
66 | Micros, |
67 | |
68 | /// Use fixed 9 subsecond digits. This corresponds to |
69 | /// [Fixed::Nanosecond9](format/enum.Fixed.html#variant.Nanosecond9). |
70 | Nanos, |
71 | |
72 | /// Automatically select one of `Secs`, `Millis`, `Micros`, or `Nanos` to |
73 | /// display all available non-zero sub-second digits. This corresponds to |
74 | /// [Fixed::Nanosecond](format/enum.Fixed.html#variant.Nanosecond). |
75 | AutoSi, |
76 | |
77 | // Do not match against this. |
78 | #[doc (hidden)] |
79 | __NonExhaustive, |
80 | } |
81 | |
82 | /// ISO 8601 combined date and time with time zone. |
83 | /// |
84 | /// There are some constructors implemented here (the `from_*` methods), but |
85 | /// the general-purpose constructors are all via the methods on the |
86 | /// [`TimeZone`](./offset/trait.TimeZone.html) implementations. |
87 | #[derive (Clone)] |
88 | #[cfg_attr (feature = "rkyv" , derive(Archive, Deserialize, Serialize))] |
89 | pub struct DateTime<Tz: TimeZone> { |
90 | datetime: NaiveDateTime, |
91 | offset: Tz::Offset, |
92 | } |
93 | |
94 | /// The minimum possible `DateTime<Utc>`. |
95 | #[deprecated (since = "0.4.20" , note = "Use DateTime::MIN_UTC instead" )] |
96 | pub const MIN_DATETIME: DateTime<Utc> = DateTime::<Utc>::MIN_UTC; |
97 | /// The maximum possible `DateTime<Utc>`. |
98 | #[deprecated (since = "0.4.20" , note = "Use DateTime::MAX_UTC instead" )] |
99 | pub const MAX_DATETIME: DateTime<Utc> = DateTime::<Utc>::MAX_UTC; |
100 | |
101 | impl<Tz: TimeZone> DateTime<Tz> { |
102 | /// Makes a new `DateTime` with given *UTC* datetime and offset. |
103 | /// The local datetime should be constructed via the `TimeZone` trait. |
104 | /// |
105 | /// # Example |
106 | /// |
107 | /// ``` |
108 | /// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc}; |
109 | /// |
110 | /// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp_opt(61, 0).unwrap(), Utc); |
111 | /// assert_eq!(Utc.timestamp_opt(61, 0).unwrap(), dt); |
112 | /// ``` |
113 | // |
114 | // note: this constructor is purposely not named to `new` to discourage the direct usage. |
115 | #[inline ] |
116 | #[must_use ] |
117 | pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> { |
118 | DateTime { datetime, offset } |
119 | } |
120 | |
121 | /// Makes a new `DateTime` with given **local** datetime and offset that |
122 | /// presents local timezone. |
123 | /// |
124 | /// # Example |
125 | /// |
126 | /// ``` |
127 | /// use chrono::DateTime; |
128 | /// use chrono::naive::NaiveDate; |
129 | /// use chrono::offset::{Utc, FixedOffset}; |
130 | /// |
131 | /// let naivedatetime_utc = NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(2, 0, 0).unwrap(); |
132 | /// let datetime_utc = DateTime::<Utc>::from_utc(naivedatetime_utc, Utc); |
133 | /// |
134 | /// let timezone_east = FixedOffset::east_opt(8 * 60 * 60).unwrap(); |
135 | /// let naivedatetime_east = NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(10, 0, 0).unwrap(); |
136 | /// let datetime_east = DateTime::<FixedOffset>::from_local(naivedatetime_east, timezone_east); |
137 | /// |
138 | /// let timezone_west = FixedOffset::west_opt(7 * 60 * 60).unwrap(); |
139 | /// let naivedatetime_west = NaiveDate::from_ymd_opt(2000, 1, 11).unwrap().and_hms_opt(19, 0, 0).unwrap(); |
140 | /// let datetime_west = DateTime::<FixedOffset>::from_local(naivedatetime_west, timezone_west); |
141 | |
142 | /// assert_eq!(datetime_east, datetime_utc.with_timezone(&timezone_east)); |
143 | /// assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west)); |
144 | /// ``` |
145 | #[inline ] |
146 | #[must_use ] |
147 | pub fn from_local(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> { |
148 | let datetime_utc = datetime - offset.fix(); |
149 | |
150 | DateTime { datetime: datetime_utc, offset } |
151 | } |
152 | |
153 | /// Retrieves a date component |
154 | /// |
155 | /// Unless you are immediately planning on turning this into a `DateTime` |
156 | /// with the same Timezone you should use the |
157 | /// [`date_naive`](DateTime::date_naive) method. |
158 | #[inline ] |
159 | #[deprecated (since = "0.4.23" , note = "Use `date_naive()` instead" )] |
160 | #[allow (deprecated)] |
161 | #[must_use ] |
162 | pub fn date(&self) -> Date<Tz> { |
163 | Date::from_utc(self.naive_local().date(), self.offset.clone()) |
164 | } |
165 | |
166 | /// Retrieves the Date without an associated timezone |
167 | /// |
168 | /// [`NaiveDate`] is a more well-defined type, and has more traits implemented on it, |
169 | /// so should be preferred to [`Date`] any time you truly want to operate on Dates. |
170 | /// |
171 | /// ``` |
172 | /// use chrono::prelude::*; |
173 | /// |
174 | /// let date: DateTime<Utc> = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(); |
175 | /// let other: DateTime<FixedOffset> = FixedOffset::east_opt(23).unwrap().with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(); |
176 | /// assert_eq!(date.date_naive(), other.date_naive()); |
177 | /// ``` |
178 | #[inline ] |
179 | #[must_use ] |
180 | pub fn date_naive(&self) -> NaiveDate { |
181 | let local = self.naive_local(); |
182 | NaiveDate::from_ymd_opt(local.year(), local.month(), local.day()).unwrap() |
183 | } |
184 | |
185 | /// Retrieves a time component. |
186 | /// Unlike `date`, this is not associated to the time zone. |
187 | #[inline ] |
188 | #[must_use ] |
189 | pub fn time(&self) -> NaiveTime { |
190 | self.datetime.time() + self.offset.fix() |
191 | } |
192 | |
193 | /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC |
194 | /// (aka "UNIX timestamp"). |
195 | #[inline ] |
196 | #[must_use ] |
197 | pub fn timestamp(&self) -> i64 { |
198 | self.datetime.timestamp() |
199 | } |
200 | |
201 | /// Returns the number of non-leap-milliseconds since January 1, 1970 UTC |
202 | /// |
203 | /// Note that this does reduce the number of years that can be represented |
204 | /// from ~584 Billion to ~584 Million. (If this is a problem, please file |
205 | /// an issue to let me know what domain needs millisecond precision over |
206 | /// billions of years, I'm curious.) |
207 | /// |
208 | /// # Example |
209 | /// |
210 | /// ``` |
211 | /// use chrono::{Utc, NaiveDate}; |
212 | /// |
213 | /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_milli_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap(); |
214 | /// assert_eq!(dt.timestamp_millis(), 1_444); |
215 | /// |
216 | /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_milli_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap(); |
217 | /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); |
218 | /// ``` |
219 | #[inline ] |
220 | #[must_use ] |
221 | pub fn timestamp_millis(&self) -> i64 { |
222 | self.datetime.timestamp_millis() |
223 | } |
224 | |
225 | /// Returns the number of non-leap-microseconds since January 1, 1970 UTC |
226 | /// |
227 | /// Note that this does reduce the number of years that can be represented |
228 | /// from ~584 Billion to ~584 Thousand. (If this is a problem, please file |
229 | /// an issue to let me know what domain needs microsecond precision over |
230 | /// millennia, I'm curious.) |
231 | /// |
232 | /// # Example |
233 | /// |
234 | /// ``` |
235 | /// use chrono::{Utc, NaiveDate}; |
236 | /// |
237 | /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_micro_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap(); |
238 | /// assert_eq!(dt.timestamp_micros(), 1_000_444); |
239 | /// |
240 | /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_micro_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap(); |
241 | /// assert_eq!(dt.timestamp_micros(), 1_000_000_000_000_555); |
242 | /// ``` |
243 | #[inline ] |
244 | #[must_use ] |
245 | pub fn timestamp_micros(&self) -> i64 { |
246 | self.datetime.timestamp_micros() |
247 | } |
248 | |
249 | /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC |
250 | /// |
251 | /// Note that this does reduce the number of years that can be represented |
252 | /// from ~584 Billion to ~584. (If this is a problem, please file |
253 | /// an issue to let me know what domain needs nanosecond precision over |
254 | /// millennia, I'm curious.) |
255 | /// |
256 | /// # Example |
257 | /// |
258 | /// ``` |
259 | /// use chrono::{Utc, NaiveDate}; |
260 | /// |
261 | /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_nano_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap(); |
262 | /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); |
263 | /// |
264 | /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap(); |
265 | /// assert_eq!(dt.timestamp_nanos(), 1_000_000_000_000_000_555); |
266 | /// ``` |
267 | #[inline ] |
268 | #[must_use ] |
269 | pub fn timestamp_nanos(&self) -> i64 { |
270 | self.datetime.timestamp_nanos() |
271 | } |
272 | |
273 | /// Returns the number of milliseconds since the last second boundary |
274 | /// |
275 | /// warning: in event of a leap second, this may exceed 999 |
276 | /// |
277 | /// note: this is not the number of milliseconds since January 1, 1970 0:00:00 UTC |
278 | #[inline ] |
279 | #[must_use ] |
280 | pub fn timestamp_subsec_millis(&self) -> u32 { |
281 | self.datetime.timestamp_subsec_millis() |
282 | } |
283 | |
284 | /// Returns the number of microseconds since the last second boundary |
285 | /// |
286 | /// warning: in event of a leap second, this may exceed 999_999 |
287 | /// |
288 | /// note: this is not the number of microseconds since January 1, 1970 0:00:00 UTC |
289 | #[inline ] |
290 | #[must_use ] |
291 | pub fn timestamp_subsec_micros(&self) -> u32 { |
292 | self.datetime.timestamp_subsec_micros() |
293 | } |
294 | |
295 | /// Returns the number of nanoseconds since the last second boundary |
296 | /// |
297 | /// warning: in event of a leap second, this may exceed 999_999_999 |
298 | /// |
299 | /// note: this is not the number of nanoseconds since January 1, 1970 0:00:00 UTC |
300 | #[inline ] |
301 | #[must_use ] |
302 | pub fn timestamp_subsec_nanos(&self) -> u32 { |
303 | self.datetime.timestamp_subsec_nanos() |
304 | } |
305 | |
306 | /// Retrieves an associated offset from UTC. |
307 | #[inline ] |
308 | #[must_use ] |
309 | pub fn offset(&self) -> &Tz::Offset { |
310 | &self.offset |
311 | } |
312 | |
313 | /// Retrieves an associated time zone. |
314 | #[inline ] |
315 | #[must_use ] |
316 | pub fn timezone(&self) -> Tz { |
317 | TimeZone::from_offset(&self.offset) |
318 | } |
319 | |
320 | /// Changes the associated time zone. |
321 | /// The returned `DateTime` references the same instant of time from the perspective of the provided time zone. |
322 | #[inline ] |
323 | #[must_use ] |
324 | pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> DateTime<Tz2> { |
325 | tz.from_utc_datetime(&self.datetime) |
326 | } |
327 | |
328 | /// Fix the offset from UTC to its current value, dropping the associated timezone information. |
329 | /// This it useful for converting a generic `DateTime<Tz: Timezone>` to `DateTime<FixedOffset>`. |
330 | #[inline ] |
331 | #[must_use ] |
332 | pub fn fixed_offset(&self) -> DateTime<FixedOffset> { |
333 | self.with_timezone(&self.offset().fix()) |
334 | } |
335 | |
336 | /// Adds given `Duration` to the current date and time. |
337 | /// |
338 | /// Returns `None` when it will result in overflow. |
339 | #[inline ] |
340 | #[must_use ] |
341 | pub fn checked_add_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> { |
342 | let datetime = self.datetime.checked_add_signed(rhs)?; |
343 | let tz = self.timezone(); |
344 | Some(tz.from_utc_datetime(&datetime)) |
345 | } |
346 | |
347 | /// Adds given `Months` to the current date and time. |
348 | /// |
349 | /// Returns `None` when it will result in overflow, or if the |
350 | /// local time is not valid on the newly calculated date. |
351 | /// |
352 | /// See [`NaiveDate::checked_add_months`] for more details on behavior |
353 | #[must_use ] |
354 | pub fn checked_add_months(self, rhs: Months) -> Option<DateTime<Tz>> { |
355 | self.naive_local() |
356 | .checked_add_months(rhs)? |
357 | .and_local_timezone(Tz::from_offset(&self.offset)) |
358 | .single() |
359 | } |
360 | |
361 | /// Subtracts given `Duration` from the current date and time. |
362 | /// |
363 | /// Returns `None` when it will result in overflow. |
364 | #[inline ] |
365 | #[must_use ] |
366 | pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> { |
367 | let datetime = self.datetime.checked_sub_signed(rhs)?; |
368 | let tz = self.timezone(); |
369 | Some(tz.from_utc_datetime(&datetime)) |
370 | } |
371 | |
372 | /// Subtracts given `Months` from the current date and time. |
373 | /// |
374 | /// Returns `None` when it will result in overflow, or if the |
375 | /// local time is not valid on the newly calculated date. |
376 | /// |
377 | /// See [`NaiveDate::checked_sub_months`] for more details on behavior |
378 | #[must_use ] |
379 | pub fn checked_sub_months(self, rhs: Months) -> Option<DateTime<Tz>> { |
380 | self.naive_local() |
381 | .checked_sub_months(rhs)? |
382 | .and_local_timezone(Tz::from_offset(&self.offset)) |
383 | .single() |
384 | } |
385 | |
386 | /// Add a duration in [`Days`] to the date part of the `DateTime` |
387 | /// |
388 | /// Returns `None` if the resulting date would be out of range. |
389 | #[must_use ] |
390 | pub fn checked_add_days(self, days: Days) -> Option<Self> { |
391 | self.naive_local() |
392 | .checked_add_days(days)? |
393 | .and_local_timezone(TimeZone::from_offset(&self.offset)) |
394 | .single() |
395 | } |
396 | |
397 | /// Subtract a duration in [`Days`] from the date part of the `DateTime` |
398 | /// |
399 | /// Returns `None` if the resulting date would be out of range. |
400 | #[must_use ] |
401 | pub fn checked_sub_days(self, days: Days) -> Option<Self> { |
402 | self.naive_local() |
403 | .checked_sub_days(days)? |
404 | .and_local_timezone(TimeZone::from_offset(&self.offset)) |
405 | .single() |
406 | } |
407 | |
408 | /// Subtracts another `DateTime` from the current date and time. |
409 | /// This does not overflow or underflow at all. |
410 | #[inline ] |
411 | #[must_use ] |
412 | pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: DateTime<Tz2>) -> OldDuration { |
413 | self.datetime.signed_duration_since(rhs.datetime) |
414 | } |
415 | |
416 | /// Returns a view to the naive UTC datetime. |
417 | #[inline ] |
418 | #[must_use ] |
419 | pub fn naive_utc(&self) -> NaiveDateTime { |
420 | self.datetime |
421 | } |
422 | |
423 | /// Returns a view to the naive local datetime. |
424 | #[inline ] |
425 | #[must_use ] |
426 | pub fn naive_local(&self) -> NaiveDateTime { |
427 | self.datetime + self.offset.fix() |
428 | } |
429 | |
430 | /// Retrieve the elapsed years from now to the given [`DateTime`]. |
431 | #[must_use ] |
432 | pub fn years_since(&self, base: Self) -> Option<u32> { |
433 | let mut years = self.year() - base.year(); |
434 | let earlier_time = |
435 | (self.month(), self.day(), self.time()) < (base.month(), base.day(), base.time()); |
436 | |
437 | years -= match earlier_time { |
438 | true => 1, |
439 | false => 0, |
440 | }; |
441 | |
442 | match years >= 0 { |
443 | true => Some(years as u32), |
444 | false => None, |
445 | } |
446 | } |
447 | |
448 | /// The minimum possible `DateTime<Utc>`. |
449 | pub const MIN_UTC: DateTime<Utc> = DateTime { datetime: NaiveDateTime::MIN, offset: Utc }; |
450 | /// The maximum possible `DateTime<Utc>`. |
451 | pub const MAX_UTC: DateTime<Utc> = DateTime { datetime: NaiveDateTime::MAX, offset: Utc }; |
452 | } |
453 | |
454 | impl Default for DateTime<Utc> { |
455 | fn default() -> Self { |
456 | Utc.from_utc_datetime(&NaiveDateTime::default()) |
457 | } |
458 | } |
459 | |
460 | #[cfg (feature = "clock" )] |
461 | #[cfg_attr (docsrs, doc(cfg(feature = "clock" )))] |
462 | impl Default for DateTime<Local> { |
463 | fn default() -> Self { |
464 | Local.from_utc_datetime(&NaiveDateTime::default()) |
465 | } |
466 | } |
467 | |
468 | impl Default for DateTime<FixedOffset> { |
469 | fn default() -> Self { |
470 | FixedOffset::west_opt(0).unwrap().from_utc_datetime(&NaiveDateTime::default()) |
471 | } |
472 | } |
473 | |
474 | /// Convert a `DateTime<Utc>` instance into a `DateTime<FixedOffset>` instance. |
475 | impl From<DateTime<Utc>> for DateTime<FixedOffset> { |
476 | /// Convert this `DateTime<Utc>` instance into a `DateTime<FixedOffset>` instance. |
477 | /// |
478 | /// Conversion is done via [`DateTime::with_timezone`]. Note that the converted value returned by |
479 | /// this will be created with a fixed timezone offset of 0. |
480 | fn from(src: DateTime<Utc>) -> Self { |
481 | src.with_timezone(&FixedOffset::east_opt(secs:0).unwrap()) |
482 | } |
483 | } |
484 | |
485 | /// Convert a `DateTime<Utc>` instance into a `DateTime<Local>` instance. |
486 | #[cfg (feature = "clock" )] |
487 | #[cfg_attr (docsrs, doc(cfg(feature = "clock" )))] |
488 | impl From<DateTime<Utc>> for DateTime<Local> { |
489 | /// Convert this `DateTime<Utc>` instance into a `DateTime<Local>` instance. |
490 | /// |
491 | /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in timezones. |
492 | fn from(src: DateTime<Utc>) -> Self { |
493 | src.with_timezone(&Local) |
494 | } |
495 | } |
496 | |
497 | /// Convert a `DateTime<FixedOffset>` instance into a `DateTime<Utc>` instance. |
498 | impl From<DateTime<FixedOffset>> for DateTime<Utc> { |
499 | /// Convert this `DateTime<FixedOffset>` instance into a `DateTime<Utc>` instance. |
500 | /// |
501 | /// Conversion is performed via [`DateTime::with_timezone`], accounting for the timezone |
502 | /// difference. |
503 | fn from(src: DateTime<FixedOffset>) -> Self { |
504 | src.with_timezone(&Utc) |
505 | } |
506 | } |
507 | |
508 | /// Convert a `DateTime<FixedOffset>` instance into a `DateTime<Local>` instance. |
509 | #[cfg (feature = "clock" )] |
510 | #[cfg_attr (docsrs, doc(cfg(feature = "clock" )))] |
511 | impl From<DateTime<FixedOffset>> for DateTime<Local> { |
512 | /// Convert this `DateTime<FixedOffset>` instance into a `DateTime<Local>` instance. |
513 | /// |
514 | /// Conversion is performed via [`DateTime::with_timezone`]. Returns the equivalent value in local |
515 | /// time. |
516 | fn from(src: DateTime<FixedOffset>) -> Self { |
517 | src.with_timezone(&Local) |
518 | } |
519 | } |
520 | |
521 | /// Convert a `DateTime<Local>` instance into a `DateTime<Utc>` instance. |
522 | #[cfg (feature = "clock" )] |
523 | #[cfg_attr (docsrs, doc(cfg(feature = "clock" )))] |
524 | impl From<DateTime<Local>> for DateTime<Utc> { |
525 | /// Convert this `DateTime<Local>` instance into a `DateTime<Utc>` instance. |
526 | /// |
527 | /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in |
528 | /// timezones. |
529 | fn from(src: DateTime<Local>) -> Self { |
530 | src.with_timezone(&Utc) |
531 | } |
532 | } |
533 | |
534 | /// Convert a `DateTime<Local>` instance into a `DateTime<FixedOffset>` instance. |
535 | #[cfg (feature = "clock" )] |
536 | #[cfg_attr (docsrs, doc(cfg(feature = "clock" )))] |
537 | impl From<DateTime<Local>> for DateTime<FixedOffset> { |
538 | /// Convert this `DateTime<Local>` instance into a `DateTime<FixedOffset>` instance. |
539 | /// |
540 | /// Conversion is performed via [`DateTime::with_timezone`]. |
541 | fn from(src: DateTime<Local>) -> Self { |
542 | src.with_timezone(&src.offset().fix()) |
543 | } |
544 | } |
545 | |
546 | /// Maps the local datetime to other datetime with given conversion function. |
547 | fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz>> |
548 | where |
549 | F: FnMut(NaiveDateTime) -> Option<NaiveDateTime>, |
550 | { |
551 | f(dt.naive_local()).and_then(|datetime: NaiveDateTime| dt.timezone().from_local_datetime(&datetime).single()) |
552 | } |
553 | |
554 | impl DateTime<FixedOffset> { |
555 | /// Parses an RFC 2822 date-and-time string into a `DateTime<FixedOffset>` value. |
556 | /// |
557 | /// This parses valid RFC 2822 datetime strings (such as `Tue, 1 Jul 2003 10:52:37 +0200`) |
558 | /// and returns a new [`DateTime`] instance with the parsed timezone as the [`FixedOffset`]. |
559 | /// |
560 | /// RFC 2822 is the internet message standard that specifies the representation of times in HTTP |
561 | /// and email headers. |
562 | /// |
563 | /// ``` |
564 | /// # use chrono::{DateTime, FixedOffset, TimeZone}; |
565 | /// assert_eq!( |
566 | /// DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT" ).unwrap(), |
567 | /// FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap() |
568 | /// ); |
569 | /// ``` |
570 | pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> { |
571 | const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; |
572 | let mut parsed = Parsed::new(); |
573 | parse(&mut parsed, s, ITEMS.iter())?; |
574 | parsed.to_datetime() |
575 | } |
576 | |
577 | /// Parses an RFC 3339 date-and-time string into a `DateTime<FixedOffset>` value. |
578 | /// |
579 | /// Parses all valid RFC 3339 values (as well as the subset of valid ISO 8601 values that are |
580 | /// also valid RFC 3339 date-and-time values) and returns a new [`DateTime`] with a |
581 | /// [`FixedOffset`] corresponding to the parsed timezone. While RFC 3339 values come in a wide |
582 | /// variety of shapes and sizes, `1996-12-19T16:39:57-08:00` is an example of the most commonly |
583 | /// encountered variety of RFC 3339 formats. |
584 | /// |
585 | /// Why isn't this named `parse_from_iso8601`? That's because ISO 8601 allows representing |
586 | /// values in a wide range of formats, only some of which represent actual date-and-time |
587 | /// instances (rather than periods, ranges, dates, or times). Some valid ISO 8601 values are |
588 | /// also simultaneously valid RFC 3339 values, but not all RFC 3339 values are valid ISO 8601 |
589 | /// values (or the other way around). |
590 | pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> { |
591 | const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; |
592 | let mut parsed = Parsed::new(); |
593 | parse(&mut parsed, s, ITEMS.iter())?; |
594 | parsed.to_datetime() |
595 | } |
596 | |
597 | /// Parses a string from a user-specified format into a `DateTime<FixedOffset>` value. |
598 | /// |
599 | /// Note that this method *requires a timezone* in the input string. See |
600 | /// [`NaiveDateTime::parse_from_str`](./naive/struct.NaiveDateTime.html#method.parse_from_str) |
601 | /// for a version that does not require a timezone in the to-be-parsed str. The returned |
602 | /// [`DateTime`] value will have a [`FixedOffset`] reflecting the parsed timezone. |
603 | /// |
604 | /// See the [`format::strftime` module](./format/strftime/index.html) for supported format |
605 | /// sequences. |
606 | /// |
607 | /// # Example |
608 | /// |
609 | /// ```rust |
610 | /// use chrono::{DateTime, FixedOffset, TimeZone, NaiveDate}; |
611 | /// |
612 | /// let dt = DateTime::parse_from_str( |
613 | /// "1983 Apr 13 12:09:14.274 +0000" , "%Y %b %d %H:%M:%S%.3f %z" ); |
614 | /// assert_eq!(dt, Ok(FixedOffset::east_opt(0).unwrap().from_local_datetime(&NaiveDate::from_ymd_opt(1983, 4, 13).unwrap().and_hms_milli_opt(12, 9, 14, 274).unwrap()).unwrap())); |
615 | /// ``` |
616 | pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> { |
617 | let mut parsed = Parsed::new(); |
618 | parse(&mut parsed, s, StrftimeItems::new(fmt))?; |
619 | parsed.to_datetime() |
620 | } |
621 | |
622 | /// Parses a string from a user-specified format into a `DateTime<FixedOffset>` value, and a |
623 | /// slice with the remaining portion of the string. |
624 | /// |
625 | /// Note that this method *requires a timezone* in the input string. See |
626 | /// [`NaiveDateTime::parse_and_remainder`] for a version that does not |
627 | /// require a timezone in `s`. The returned [`DateTime`] value will have a [`FixedOffset`] |
628 | /// reflecting the parsed timezone. |
629 | /// |
630 | /// See the [`format::strftime` module](./format/strftime/index.html) for supported format |
631 | /// sequences. |
632 | /// |
633 | /// Similar to [`parse_from_str`](#method.parse_from_str). |
634 | /// |
635 | /// # Example |
636 | /// |
637 | /// ```rust |
638 | /// # use chrono::{DateTime, FixedOffset, TimeZone}; |
639 | /// let (datetime, remainder) = DateTime::parse_and_remainder( |
640 | /// "2015-02-18 23:16:09 +0200 trailing text" , "%Y-%m-%d %H:%M:%S %z" ).unwrap(); |
641 | /// assert_eq!( |
642 | /// datetime, |
643 | /// FixedOffset::east_opt(2*3600).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap() |
644 | /// ); |
645 | /// assert_eq!(remainder, " trailing text" ); |
646 | /// ``` |
647 | pub fn parse_and_remainder<'a>( |
648 | s: &'a str, |
649 | fmt: &str, |
650 | ) -> ParseResult<(DateTime<FixedOffset>, &'a str)> { |
651 | let mut parsed = Parsed::new(); |
652 | let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?; |
653 | parsed.to_datetime().map(|d| (d, remainder)) |
654 | } |
655 | } |
656 | |
657 | impl<Tz: TimeZone> DateTime<Tz> |
658 | where |
659 | Tz::Offset: fmt::Display, |
660 | { |
661 | /// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`. |
662 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
663 | #[cfg_attr (docsrs, doc(cfg(any(feature = "alloc" , feature = "std" ))))] |
664 | #[must_use ] |
665 | pub fn to_rfc2822(&self) -> String { |
666 | let mut result = String::with_capacity(32); |
667 | crate::format::write_rfc2822(&mut result, self.naive_local(), self.offset.fix()) |
668 | .expect("writing rfc2822 datetime to string should never fail" ); |
669 | result |
670 | } |
671 | |
672 | /// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`. |
673 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
674 | #[cfg_attr (docsrs, doc(cfg(any(feature = "alloc" , feature = "std" ))))] |
675 | #[must_use ] |
676 | pub fn to_rfc3339(&self) -> String { |
677 | let mut result = String::with_capacity(32); |
678 | crate::format::write_rfc3339(&mut result, self.naive_local(), self.offset.fix()) |
679 | .expect("writing rfc3339 datetime to string should never fail" ); |
680 | result |
681 | } |
682 | |
683 | /// Return an RFC 3339 and ISO 8601 date and time string with subseconds |
684 | /// formatted as per `SecondsFormat`. |
685 | /// |
686 | /// If `use_z` is true and the timezone is UTC (offset 0), uses `Z` as |
687 | /// per [`Fixed::TimezoneOffsetColonZ`]. If `use_z` is false, uses |
688 | /// [`Fixed::TimezoneOffsetColon`] |
689 | /// |
690 | /// # Examples |
691 | /// |
692 | /// ```rust |
693 | /// # use chrono::{FixedOffset, SecondsFormat, TimeZone, Utc, NaiveDate}; |
694 | /// let dt = NaiveDate::from_ymd_opt(2018, 1, 26).unwrap().and_hms_micro_opt(18, 30, 9, 453_829).unwrap().and_local_timezone(Utc).unwrap(); |
695 | /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, false), |
696 | /// "2018-01-26T18:30:09.453+00:00" ); |
697 | /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, true), |
698 | /// "2018-01-26T18:30:09.453Z" ); |
699 | /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), |
700 | /// "2018-01-26T18:30:09Z" ); |
701 | /// |
702 | /// let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); |
703 | /// let dt = pst.from_local_datetime(&NaiveDate::from_ymd_opt(2018, 1, 26).unwrap().and_hms_micro_opt(10, 30, 9, 453_829).unwrap()).unwrap(); |
704 | /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), |
705 | /// "2018-01-26T10:30:09+08:00" ); |
706 | /// ``` |
707 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
708 | #[cfg_attr (docsrs, doc(cfg(any(feature = "alloc" , feature = "std" ))))] |
709 | #[must_use ] |
710 | pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String { |
711 | use crate::format::Numeric::*; |
712 | use crate::format::Pad::Zero; |
713 | use crate::SecondsFormat::*; |
714 | |
715 | debug_assert!(secform != __NonExhaustive, "Do not use __NonExhaustive!" ); |
716 | |
717 | const PREFIX: &[Item<'static>] = &[ |
718 | Item::Numeric(Year, Zero), |
719 | Item::Literal("-" ), |
720 | Item::Numeric(Month, Zero), |
721 | Item::Literal("-" ), |
722 | Item::Numeric(Day, Zero), |
723 | Item::Literal("T" ), |
724 | Item::Numeric(Hour, Zero), |
725 | Item::Literal(":" ), |
726 | Item::Numeric(Minute, Zero), |
727 | Item::Literal(":" ), |
728 | Item::Numeric(Second, Zero), |
729 | ]; |
730 | |
731 | let ssitem = match secform { |
732 | Secs => None, |
733 | Millis => Some(Item::Fixed(Fixed::Nanosecond3)), |
734 | Micros => Some(Item::Fixed(Fixed::Nanosecond6)), |
735 | Nanos => Some(Item::Fixed(Fixed::Nanosecond9)), |
736 | AutoSi => Some(Item::Fixed(Fixed::Nanosecond)), |
737 | __NonExhaustive => unreachable!(), |
738 | }; |
739 | |
740 | let tzitem = Item::Fixed(if use_z { |
741 | Fixed::TimezoneOffsetColonZ |
742 | } else { |
743 | Fixed::TimezoneOffsetColon |
744 | }); |
745 | |
746 | match ssitem { |
747 | None => self.format_with_items(PREFIX.iter().chain([tzitem].iter())).to_string(), |
748 | Some(s) => self.format_with_items(PREFIX.iter().chain([s, tzitem].iter())).to_string(), |
749 | } |
750 | } |
751 | |
752 | /// Formats the combined date and time with the specified formatting items. |
753 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
754 | #[cfg_attr (docsrs, doc(cfg(any(feature = "alloc" , feature = "std" ))))] |
755 | #[inline ] |
756 | #[must_use ] |
757 | pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> |
758 | where |
759 | I: Iterator<Item = B> + Clone, |
760 | B: Borrow<Item<'a>>, |
761 | { |
762 | let local = self.naive_local(); |
763 | DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items) |
764 | } |
765 | |
766 | /// Formats the combined date and time per the specified format string. |
767 | /// |
768 | /// See the [`crate::format::strftime`] module for the supported escape sequences. |
769 | /// |
770 | /// # Example |
771 | /// ```rust |
772 | /// use chrono::prelude::*; |
773 | /// |
774 | /// let date_time: DateTime<Utc> = Utc.with_ymd_and_hms(2017, 04, 02, 12, 50, 32).unwrap(); |
775 | /// let formatted = format!("{}" , date_time.format("%d/%m/%Y %H:%M" )); |
776 | /// assert_eq!(formatted, "02/04/2017 12:50" ); |
777 | /// ``` |
778 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
779 | #[cfg_attr (docsrs, doc(cfg(any(feature = "alloc" , feature = "std" ))))] |
780 | #[inline ] |
781 | #[must_use ] |
782 | pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { |
783 | self.format_with_items(StrftimeItems::new(fmt)) |
784 | } |
785 | |
786 | /// Formats the combined date and time with the specified formatting items and locale. |
787 | #[cfg (feature = "unstable-locales" )] |
788 | #[cfg_attr (docsrs, doc(cfg(feature = "unstable-locales" )))] |
789 | #[inline ] |
790 | #[must_use ] |
791 | pub fn format_localized_with_items<'a, I, B>( |
792 | &self, |
793 | items: I, |
794 | locale: Locale, |
795 | ) -> DelayedFormat<I> |
796 | where |
797 | I: Iterator<Item = B> + Clone, |
798 | B: Borrow<Item<'a>>, |
799 | { |
800 | let local = self.naive_local(); |
801 | DelayedFormat::new_with_offset_and_locale( |
802 | Some(local.date()), |
803 | Some(local.time()), |
804 | &self.offset, |
805 | items, |
806 | locale, |
807 | ) |
808 | } |
809 | |
810 | /// Formats the combined date and time per the specified format string and |
811 | /// locale. |
812 | /// |
813 | /// See the [`crate::format::strftime`] module on the supported escape |
814 | /// sequences. |
815 | #[cfg (feature = "unstable-locales" )] |
816 | #[cfg_attr (docsrs, doc(cfg(feature = "unstable-locales" )))] |
817 | #[inline ] |
818 | #[must_use ] |
819 | pub fn format_localized<'a>( |
820 | &self, |
821 | fmt: &'a str, |
822 | locale: Locale, |
823 | ) -> DelayedFormat<StrftimeItems<'a>> { |
824 | self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) |
825 | } |
826 | } |
827 | |
828 | impl<Tz: TimeZone> Datelike for DateTime<Tz> { |
829 | #[inline ] |
830 | fn year(&self) -> i32 { |
831 | self.naive_local().year() |
832 | } |
833 | #[inline ] |
834 | fn month(&self) -> u32 { |
835 | self.naive_local().month() |
836 | } |
837 | #[inline ] |
838 | fn month0(&self) -> u32 { |
839 | self.naive_local().month0() |
840 | } |
841 | #[inline ] |
842 | fn day(&self) -> u32 { |
843 | self.naive_local().day() |
844 | } |
845 | #[inline ] |
846 | fn day0(&self) -> u32 { |
847 | self.naive_local().day0() |
848 | } |
849 | #[inline ] |
850 | fn ordinal(&self) -> u32 { |
851 | self.naive_local().ordinal() |
852 | } |
853 | #[inline ] |
854 | fn ordinal0(&self) -> u32 { |
855 | self.naive_local().ordinal0() |
856 | } |
857 | #[inline ] |
858 | fn weekday(&self) -> Weekday { |
859 | self.naive_local().weekday() |
860 | } |
861 | #[inline ] |
862 | fn iso_week(&self) -> IsoWeek { |
863 | self.naive_local().iso_week() |
864 | } |
865 | |
866 | #[inline ] |
867 | fn with_year(&self, year: i32) -> Option<DateTime<Tz>> { |
868 | map_local(self, |datetime| datetime.with_year(year)) |
869 | } |
870 | |
871 | #[inline ] |
872 | fn with_month(&self, month: u32) -> Option<DateTime<Tz>> { |
873 | map_local(self, |datetime| datetime.with_month(month)) |
874 | } |
875 | |
876 | #[inline ] |
877 | fn with_month0(&self, month0: u32) -> Option<DateTime<Tz>> { |
878 | map_local(self, |datetime| datetime.with_month0(month0)) |
879 | } |
880 | |
881 | #[inline ] |
882 | fn with_day(&self, day: u32) -> Option<DateTime<Tz>> { |
883 | map_local(self, |datetime| datetime.with_day(day)) |
884 | } |
885 | |
886 | #[inline ] |
887 | fn with_day0(&self, day0: u32) -> Option<DateTime<Tz>> { |
888 | map_local(self, |datetime| datetime.with_day0(day0)) |
889 | } |
890 | |
891 | #[inline ] |
892 | fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Tz>> { |
893 | map_local(self, |datetime| datetime.with_ordinal(ordinal)) |
894 | } |
895 | |
896 | #[inline ] |
897 | fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Tz>> { |
898 | map_local(self, |datetime| datetime.with_ordinal0(ordinal0)) |
899 | } |
900 | } |
901 | |
902 | impl<Tz: TimeZone> Timelike for DateTime<Tz> { |
903 | #[inline ] |
904 | fn hour(&self) -> u32 { |
905 | self.naive_local().hour() |
906 | } |
907 | #[inline ] |
908 | fn minute(&self) -> u32 { |
909 | self.naive_local().minute() |
910 | } |
911 | #[inline ] |
912 | fn second(&self) -> u32 { |
913 | self.naive_local().second() |
914 | } |
915 | #[inline ] |
916 | fn nanosecond(&self) -> u32 { |
917 | self.naive_local().nanosecond() |
918 | } |
919 | |
920 | #[inline ] |
921 | fn with_hour(&self, hour: u32) -> Option<DateTime<Tz>> { |
922 | map_local(self, |datetime| datetime.with_hour(hour)) |
923 | } |
924 | |
925 | #[inline ] |
926 | fn with_minute(&self, min: u32) -> Option<DateTime<Tz>> { |
927 | map_local(self, |datetime| datetime.with_minute(min)) |
928 | } |
929 | |
930 | #[inline ] |
931 | fn with_second(&self, sec: u32) -> Option<DateTime<Tz>> { |
932 | map_local(self, |datetime| datetime.with_second(sec)) |
933 | } |
934 | |
935 | #[inline ] |
936 | fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Tz>> { |
937 | map_local(self, |datetime| datetime.with_nanosecond(nano)) |
938 | } |
939 | } |
940 | |
941 | // we need them as automatic impls cannot handle associated types |
942 | impl<Tz: TimeZone> Copy for DateTime<Tz> where <Tz as TimeZone>::Offset: Copy {} |
943 | unsafe impl<Tz: TimeZone> Send for DateTime<Tz> where <Tz as TimeZone>::Offset: Send {} |
944 | |
945 | impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<DateTime<Tz2>> for DateTime<Tz> { |
946 | fn eq(&self, other: &DateTime<Tz2>) -> bool { |
947 | self.datetime == other.datetime |
948 | } |
949 | } |
950 | |
951 | impl<Tz: TimeZone> Eq for DateTime<Tz> {} |
952 | |
953 | impl<Tz: TimeZone, Tz2: TimeZone> PartialOrd<DateTime<Tz2>> for DateTime<Tz> { |
954 | /// Compare two DateTimes based on their true time, ignoring time zones |
955 | /// |
956 | /// # Example |
957 | /// |
958 | /// ``` |
959 | /// use chrono::prelude::*; |
960 | /// |
961 | /// let earlier = Utc.with_ymd_and_hms(2015, 5, 15, 2, 0, 0).unwrap().with_timezone(&FixedOffset::west_opt(1 * 3600).unwrap()); |
962 | /// let later = Utc.with_ymd_and_hms(2015, 5, 15, 3, 0, 0).unwrap().with_timezone(&FixedOffset::west_opt(5 * 3600).unwrap()); |
963 | /// |
964 | /// assert_eq!(earlier.to_string(), "2015-05-15 01:00:00 -01:00" ); |
965 | /// assert_eq!(later.to_string(), "2015-05-14 22:00:00 -05:00" ); |
966 | /// |
967 | /// assert!(later > earlier); |
968 | /// ``` |
969 | fn partial_cmp(&self, other: &DateTime<Tz2>) -> Option<Ordering> { |
970 | self.datetime.partial_cmp(&other.datetime) |
971 | } |
972 | } |
973 | |
974 | impl<Tz: TimeZone> Ord for DateTime<Tz> { |
975 | fn cmp(&self, other: &DateTime<Tz>) -> Ordering { |
976 | self.datetime.cmp(&other.datetime) |
977 | } |
978 | } |
979 | |
980 | impl<Tz: TimeZone> hash::Hash for DateTime<Tz> { |
981 | fn hash<H: hash::Hasher>(&self, state: &mut H) { |
982 | self.datetime.hash(state) |
983 | } |
984 | } |
985 | |
986 | impl<Tz: TimeZone> Add<OldDuration> for DateTime<Tz> { |
987 | type Output = DateTime<Tz>; |
988 | |
989 | #[inline ] |
990 | fn add(self, rhs: OldDuration) -> DateTime<Tz> { |
991 | self.checked_add_signed(rhs).expect(msg:"`DateTime + Duration` overflowed" ) |
992 | } |
993 | } |
994 | |
995 | impl<Tz: TimeZone> AddAssign<OldDuration> for DateTime<Tz> { |
996 | #[inline ] |
997 | fn add_assign(&mut self, rhs: OldDuration) { |
998 | let datetime: NaiveDateTime = |
999 | self.datetime.checked_add_signed(rhs).expect(msg:"`DateTime + Duration` overflowed" ); |
1000 | let tz: Tz = self.timezone(); |
1001 | *self = tz.from_utc_datetime(&datetime); |
1002 | } |
1003 | } |
1004 | |
1005 | impl<Tz: TimeZone> Add<Months> for DateTime<Tz> { |
1006 | type Output = DateTime<Tz>; |
1007 | |
1008 | fn add(self, rhs: Months) -> Self::Output { |
1009 | self.checked_add_months(rhs).unwrap() |
1010 | } |
1011 | } |
1012 | |
1013 | impl<Tz: TimeZone> Sub<OldDuration> for DateTime<Tz> { |
1014 | type Output = DateTime<Tz>; |
1015 | |
1016 | #[inline ] |
1017 | fn sub(self, rhs: OldDuration) -> DateTime<Tz> { |
1018 | self.checked_sub_signed(rhs).expect(msg:"`DateTime - Duration` overflowed" ) |
1019 | } |
1020 | } |
1021 | |
1022 | impl<Tz: TimeZone> SubAssign<OldDuration> for DateTime<Tz> { |
1023 | #[inline ] |
1024 | fn sub_assign(&mut self, rhs: OldDuration) { |
1025 | let datetime: NaiveDateTime = |
1026 | self.datetime.checked_sub_signed(rhs).expect(msg:"`DateTime - Duration` overflowed" ); |
1027 | let tz: Tz = self.timezone(); |
1028 | *self = tz.from_utc_datetime(&datetime) |
1029 | } |
1030 | } |
1031 | |
1032 | impl<Tz: TimeZone> Sub<Months> for DateTime<Tz> { |
1033 | type Output = DateTime<Tz>; |
1034 | |
1035 | fn sub(self, rhs: Months) -> Self::Output { |
1036 | self.checked_sub_months(rhs).unwrap() |
1037 | } |
1038 | } |
1039 | |
1040 | impl<Tz: TimeZone> Sub<DateTime<Tz>> for DateTime<Tz> { |
1041 | type Output = OldDuration; |
1042 | |
1043 | #[inline ] |
1044 | fn sub(self, rhs: DateTime<Tz>) -> OldDuration { |
1045 | self.signed_duration_since(rhs) |
1046 | } |
1047 | } |
1048 | |
1049 | impl<Tz: TimeZone> Add<Days> for DateTime<Tz> { |
1050 | type Output = DateTime<Tz>; |
1051 | |
1052 | fn add(self, days: Days) -> Self::Output { |
1053 | self.checked_add_days(days).unwrap() |
1054 | } |
1055 | } |
1056 | |
1057 | impl<Tz: TimeZone> Sub<Days> for DateTime<Tz> { |
1058 | type Output = DateTime<Tz>; |
1059 | |
1060 | fn sub(self, days: Days) -> Self::Output { |
1061 | self.checked_sub_days(days).unwrap() |
1062 | } |
1063 | } |
1064 | |
1065 | impl<Tz: TimeZone> fmt::Debug for DateTime<Tz> { |
1066 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1067 | self.naive_local().fmt(f)?; |
1068 | self.offset.fmt(f) |
1069 | } |
1070 | } |
1071 | |
1072 | impl<Tz: TimeZone> fmt::Display for DateTime<Tz> |
1073 | where |
1074 | Tz::Offset: fmt::Display, |
1075 | { |
1076 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1077 | self.naive_local().fmt(f)?; |
1078 | f.write_char(' ' )?; |
1079 | self.offset.fmt(f) |
1080 | } |
1081 | } |
1082 | |
1083 | /// Accepts a relaxed form of RFC3339. |
1084 | /// A space or a 'T' are acepted as the separator between the date and time |
1085 | /// parts. |
1086 | /// |
1087 | /// All of these examples are equivalent: |
1088 | /// ``` |
1089 | /// # use chrono::{DateTime, Utc}; |
1090 | /// "2012-12-12T12:12:12Z" .parse::<DateTime<Utc>>()?; |
1091 | /// "2012-12-12 12:12:12Z" .parse::<DateTime<Utc>>()?; |
1092 | /// "2012-12-12 12:12:12+0000" .parse::<DateTime<Utc>>()?; |
1093 | /// "2012-12-12 12:12:12+00:00" .parse::<DateTime<Utc>>()?; |
1094 | /// # Ok::<(), chrono::ParseError>(()) |
1095 | /// ``` |
1096 | impl str::FromStr for DateTime<Utc> { |
1097 | type Err = ParseError; |
1098 | |
1099 | fn from_str(s: &str) -> ParseResult<DateTime<Utc>> { |
1100 | s.parse::<DateTime<FixedOffset>>().map(|dt: DateTime| dt.with_timezone(&Utc)) |
1101 | } |
1102 | } |
1103 | |
1104 | /// Accepts a relaxed form of RFC3339. |
1105 | /// A space or a 'T' are acepted as the separator between the date and time |
1106 | /// parts. |
1107 | /// |
1108 | /// All of these examples are equivalent: |
1109 | /// ``` |
1110 | /// # use chrono::{DateTime, Local}; |
1111 | /// "2012-12-12T12:12:12Z" .parse::<DateTime<Local>>()?; |
1112 | /// "2012-12-12 12:12:12Z" .parse::<DateTime<Local>>()?; |
1113 | /// "2012-12-12 12:12:12+0000" .parse::<DateTime<Local>>()?; |
1114 | /// "2012-12-12 12:12:12+00:00" .parse::<DateTime<Local>>()?; |
1115 | /// # Ok::<(), chrono::ParseError>(()) |
1116 | /// ``` |
1117 | #[cfg (feature = "clock" )] |
1118 | #[cfg_attr (docsrs, doc(cfg(feature = "clock" )))] |
1119 | impl str::FromStr for DateTime<Local> { |
1120 | type Err = ParseError; |
1121 | |
1122 | fn from_str(s: &str) -> ParseResult<DateTime<Local>> { |
1123 | s.parse::<DateTime<FixedOffset>>().map(|dt: DateTime| dt.with_timezone(&Local)) |
1124 | } |
1125 | } |
1126 | |
1127 | #[cfg (any(feature = "std" , test))] |
1128 | #[cfg_attr (docsrs, doc(cfg(feature = "std" )))] |
1129 | impl From<SystemTime> for DateTime<Utc> { |
1130 | fn from(t: SystemTime) -> DateTime<Utc> { |
1131 | let (sec: i64, nsec: u32) = match t.duration_since(UNIX_EPOCH) { |
1132 | Ok(dur: Duration) => (dur.as_secs() as i64, dur.subsec_nanos()), |
1133 | Err(e: SystemTimeError) => { |
1134 | // unlikely but should be handled |
1135 | let dur: Duration = e.duration(); |
1136 | let (sec: i64, nsec: u32) = (dur.as_secs() as i64, dur.subsec_nanos()); |
1137 | if nsec == 0 { |
1138 | (-sec, 0) |
1139 | } else { |
1140 | (-sec - 1, 1_000_000_000 - nsec) |
1141 | } |
1142 | } |
1143 | }; |
1144 | Utc.timestamp_opt(secs:sec, nsecs:nsec).unwrap() |
1145 | } |
1146 | } |
1147 | |
1148 | #[cfg (feature = "clock" )] |
1149 | #[cfg_attr (docsrs, doc(cfg(feature = "clock" )))] |
1150 | impl From<SystemTime> for DateTime<Local> { |
1151 | fn from(t: SystemTime) -> DateTime<Local> { |
1152 | DateTime::<Utc>::from(t).with_timezone(&Local) |
1153 | } |
1154 | } |
1155 | |
1156 | #[cfg (any(feature = "std" , test))] |
1157 | #[cfg_attr (docsrs, doc(cfg(feature = "std" )))] |
1158 | impl<Tz: TimeZone> From<DateTime<Tz>> for SystemTime { |
1159 | fn from(dt: DateTime<Tz>) -> SystemTime { |
1160 | use std::time::Duration; |
1161 | |
1162 | let sec: i64 = dt.timestamp(); |
1163 | let nsec: u32 = dt.timestamp_subsec_nanos(); |
1164 | if sec < 0 { |
1165 | // unlikely but should be handled |
1166 | UNIX_EPOCH - Duration::new(-sec as u64, nanos:0) + Duration::new(secs:0, nanos:nsec) |
1167 | } else { |
1168 | UNIX_EPOCH + Duration::new(secs:sec as u64, nanos:nsec) |
1169 | } |
1170 | } |
1171 | } |
1172 | |
1173 | #[cfg (all( |
1174 | target_arch = "wasm32" , |
1175 | feature = "wasmbind" , |
1176 | not(any(target_os = "emscripten" , target_os = "wasi" )) |
1177 | ))] |
1178 | #[cfg_attr ( |
1179 | docsrs, |
1180 | doc(cfg(all( |
1181 | target_arch = "wasm32" , |
1182 | feature = "wasmbind" , |
1183 | not(any(target_os = "emscripten" , target_os = "wasi" )) |
1184 | ))) |
1185 | )] |
1186 | impl From<js_sys::Date> for DateTime<Utc> { |
1187 | fn from(date: js_sys::Date) -> DateTime<Utc> { |
1188 | DateTime::<Utc>::from(&date) |
1189 | } |
1190 | } |
1191 | |
1192 | #[cfg (all( |
1193 | target_arch = "wasm32" , |
1194 | feature = "wasmbind" , |
1195 | not(any(target_os = "emscripten" , target_os = "wasi" )) |
1196 | ))] |
1197 | #[cfg_attr ( |
1198 | docsrs, |
1199 | doc(cfg(all( |
1200 | target_arch = "wasm32" , |
1201 | feature = "wasmbind" , |
1202 | not(any(target_os = "emscripten" , target_os = "wasi" )) |
1203 | ))) |
1204 | )] |
1205 | impl From<&js_sys::Date> for DateTime<Utc> { |
1206 | fn from(date: &js_sys::Date) -> DateTime<Utc> { |
1207 | Utc.timestamp_millis_opt(date.get_time() as i64).unwrap() |
1208 | } |
1209 | } |
1210 | |
1211 | #[cfg (all( |
1212 | target_arch = "wasm32" , |
1213 | feature = "wasmbind" , |
1214 | not(any(target_os = "emscripten" , target_os = "wasi" )) |
1215 | ))] |
1216 | #[cfg_attr ( |
1217 | docsrs, |
1218 | doc(cfg(all( |
1219 | target_arch = "wasm32" , |
1220 | feature = "wasmbind" , |
1221 | not(any(target_os = "emscripten" , target_os = "wasi" )) |
1222 | ))) |
1223 | )] |
1224 | impl From<DateTime<Utc>> for js_sys::Date { |
1225 | /// Converts a `DateTime<Utc>` to a JS `Date`. The resulting value may be lossy, |
1226 | /// any values that have a millisecond timestamp value greater/less than ±8,640,000,000,000,000 |
1227 | /// (April 20, 271821 BCE ~ September 13, 275760 CE) will become invalid dates in JS. |
1228 | fn from(date: DateTime<Utc>) -> js_sys::Date { |
1229 | let js_millis = wasm_bindgen::JsValue::from_f64(date.timestamp_millis() as f64); |
1230 | js_sys::Date::new(&js_millis) |
1231 | } |
1232 | } |
1233 | |
1234 | // Note that implementation of Arbitrary cannot be simply derived for DateTime<Tz>, due to |
1235 | // the nontrivial bound <Tz as TimeZone>::Offset: Arbitrary. |
1236 | #[cfg (feature = "arbitrary" )] |
1237 | impl<'a, Tz> arbitrary::Arbitrary<'a> for DateTime<Tz> |
1238 | where |
1239 | Tz: TimeZone, |
1240 | <Tz as TimeZone>::Offset: arbitrary::Arbitrary<'a>, |
1241 | { |
1242 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<DateTime<Tz>> { |
1243 | let datetime = NaiveDateTime::arbitrary(u)?; |
1244 | let offset = <Tz as TimeZone>::Offset::arbitrary(u)?; |
1245 | Ok(DateTime::from_utc(datetime, offset)) |
1246 | } |
1247 | } |
1248 | |
1249 | #[test ] |
1250 | fn test_add_sub_months() { |
1251 | let utc_dt: DateTime = Utc.with_ymd_and_hms(year:2018, month:9, day:5, hour:23, min:58, sec:0).unwrap(); |
1252 | assert_eq!(utc_dt + Months::new(15), Utc.with_ymd_and_hms(2019, 12, 5, 23, 58, 0).unwrap()); |
1253 | |
1254 | let utc_dt: DateTime = Utc.with_ymd_and_hms(year:2020, month:1, day:31, hour:23, min:58, sec:0).unwrap(); |
1255 | assert_eq!(utc_dt + Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap()); |
1256 | assert_eq!(utc_dt + Months::new(2), Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap()); |
1257 | |
1258 | let utc_dt: DateTime = Utc.with_ymd_and_hms(year:2018, month:9, day:5, hour:23, min:58, sec:0).unwrap(); |
1259 | assert_eq!(utc_dt - Months::new(15), Utc.with_ymd_and_hms(2017, 6, 5, 23, 58, 0).unwrap()); |
1260 | |
1261 | let utc_dt: DateTime = Utc.with_ymd_and_hms(year:2020, month:3, day:31, hour:23, min:58, sec:0).unwrap(); |
1262 | assert_eq!(utc_dt - Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap()); |
1263 | assert_eq!(utc_dt - Months::new(2), Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap()); |
1264 | } |
1265 | |
1266 | #[test ] |
1267 | fn test_auto_conversion() { |
1268 | let utc_dt: DateTime = Utc.with_ymd_and_hms(year:2018, month:9, day:5, hour:23, min:58, sec:0).unwrap(); |
1269 | let cdt_dt: DateTime = FixedOffsetLocalResult>::west_opt(5 * 60 * 60) |
1270 | .unwrap() |
1271 | .with_ymd_and_hms(year:2018, month:9, day:5, hour:18, min:58, sec:0) |
1272 | .unwrap(); |
1273 | let utc_dt2: DateTime<Utc> = cdt_dt.into(); |
1274 | assert_eq!(utc_dt, utc_dt2); |
1275 | } |
1276 | |
1277 | #[cfg (all(test, any(feature = "rustc-serialize" , feature = "serde" )))] |
1278 | fn test_encodable_json<FUtc, FFixed, E>(to_string_utc: FUtc, to_string_fixed: FFixed) |
1279 | where |
1280 | FUtc: Fn(&DateTime<Utc>) -> Result<String, E>, |
1281 | FFixed: Fn(&DateTime<FixedOffset>) -> Result<String, E>, |
1282 | E: ::core::fmt::Debug, |
1283 | { |
1284 | assert_eq!( |
1285 | to_string_utc(&Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()).ok(), |
1286 | Some(r#""2014-07-24T12:34:06Z""# .into()) |
1287 | ); |
1288 | |
1289 | assert_eq!( |
1290 | to_string_fixed( |
1291 | &FixedOffset::east_opt(3660).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() |
1292 | ) |
1293 | .ok(), |
1294 | Some(r#""2014-07-24T12:34:06+01:01""# .into()) |
1295 | ); |
1296 | assert_eq!( |
1297 | to_string_fixed( |
1298 | &FixedOffset::east_opt(3650).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() |
1299 | ) |
1300 | .ok(), |
1301 | Some(r#""2014-07-24T12:34:06+01:00:50""# .into()) |
1302 | ); |
1303 | } |
1304 | |
1305 | #[cfg (all(test, feature = "clock" , any(feature = "rustc-serialize" , feature = "serde" )))] |
1306 | fn test_decodable_json<FUtc, FFixed, FLocal, E>( |
1307 | utc_from_str: FUtc, |
1308 | fixed_from_str: FFixed, |
1309 | local_from_str: FLocal, |
1310 | ) where |
1311 | FUtc: Fn(&str) -> Result<DateTime<Utc>, E>, |
1312 | FFixed: Fn(&str) -> Result<DateTime<FixedOffset>, E>, |
1313 | FLocal: Fn(&str) -> Result<DateTime<Local>, E>, |
1314 | E: ::core::fmt::Debug, |
1315 | { |
1316 | // should check against the offset as well (the normal DateTime comparison will ignore them) |
1317 | fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> { |
1318 | dt.as_ref().map(|dt| (dt, dt.offset())) |
1319 | } |
1320 | |
1321 | assert_eq!( |
1322 | norm(&utc_from_str(r#""2014-07-24T12:34:06Z""# ).ok()), |
1323 | norm(&Some(Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap())) |
1324 | ); |
1325 | assert_eq!( |
1326 | norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""# ).ok()), |
1327 | norm(&Some(Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap())) |
1328 | ); |
1329 | |
1330 | assert_eq!( |
1331 | norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""# ).ok()), |
1332 | norm(&Some( |
1333 | FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() |
1334 | )) |
1335 | ); |
1336 | assert_eq!( |
1337 | norm(&fixed_from_str(r#""2014-07-24T13:57:06+01:23""# ).ok()), |
1338 | norm(&Some( |
1339 | FixedOffset::east_opt(60 * 60 + 23 * 60) |
1340 | .unwrap() |
1341 | .with_ymd_and_hms(2014, 7, 24, 13, 57, 6) |
1342 | .unwrap() |
1343 | )) |
1344 | ); |
1345 | |
1346 | // we don't know the exact local offset but we can check that |
1347 | // the conversion didn't change the instant itself |
1348 | assert_eq!( |
1349 | local_from_str(r#""2014-07-24T12:34:06Z""# ).expect("local shouuld parse" ), |
1350 | Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() |
1351 | ); |
1352 | assert_eq!( |
1353 | local_from_str(r#""2014-07-24T13:57:06+01:23""# ).expect("local should parse with offset" ), |
1354 | Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() |
1355 | ); |
1356 | |
1357 | assert!(utc_from_str(r#""2014-07-32T12:34:06Z""# ).is_err()); |
1358 | assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""# ).is_err()); |
1359 | } |
1360 | |
1361 | #[cfg (all(test, feature = "clock" , feature = "rustc-serialize" ))] |
1362 | fn test_decodable_json_timestamps<FUtc, FFixed, FLocal, E>( |
1363 | utc_from_str: FUtc, |
1364 | fixed_from_str: FFixed, |
1365 | local_from_str: FLocal, |
1366 | ) where |
1367 | FUtc: Fn(&str) -> Result<rustc_serialize::TsSeconds<Utc>, E>, |
1368 | FFixed: Fn(&str) -> Result<rustc_serialize::TsSeconds<FixedOffset>, E>, |
1369 | FLocal: Fn(&str) -> Result<rustc_serialize::TsSeconds<Local>, E>, |
1370 | E: ::core::fmt::Debug, |
1371 | { |
1372 | fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> { |
1373 | dt.as_ref().map(|dt| (dt, dt.offset())) |
1374 | } |
1375 | |
1376 | assert_eq!( |
1377 | norm(&utc_from_str("0" ).ok().map(DateTime::from)), |
1378 | norm(&Some(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap())) |
1379 | ); |
1380 | assert_eq!( |
1381 | norm(&utc_from_str("-1" ).ok().map(DateTime::from)), |
1382 | norm(&Some(Utc.with_ymd_and_hms(1969, 12, 31, 23, 59, 59).unwrap())) |
1383 | ); |
1384 | |
1385 | assert_eq!( |
1386 | norm(&fixed_from_str("0" ).ok().map(DateTime::from)), |
1387 | norm(&Some( |
1388 | FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() |
1389 | )) |
1390 | ); |
1391 | assert_eq!( |
1392 | norm(&fixed_from_str("-1" ).ok().map(DateTime::from)), |
1393 | norm(&Some( |
1394 | FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(1969, 12, 31, 23, 59, 59).unwrap() |
1395 | )) |
1396 | ); |
1397 | |
1398 | assert_eq!( |
1399 | *fixed_from_str("0" ).expect("0 timestamp should parse" ), |
1400 | Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() |
1401 | ); |
1402 | assert_eq!( |
1403 | *local_from_str("-1" ).expect("-1 timestamp should parse" ), |
1404 | Utc.with_ymd_and_hms(1969, 12, 31, 23, 59, 59).unwrap() |
1405 | ); |
1406 | } |
1407 | |