time/offset_date_time.rs
1//! The [`OffsetDateTime`] struct and its associated `impl`s.
2
3#[cfg(feature = "formatting")]
4use alloc::string::String;
5use core::cmp::Ordering;
6use core::fmt;
7use core::hash::Hash;
8use core::ops::{Add, AddAssign, Sub, SubAssign};
9use core::time::Duration as StdDuration;
10#[cfg(feature = "formatting")]
11use std::io;
12
13use deranged::RangedI64;
14use num_conv::prelude::*;
15use powerfmt::ext::FormatterExt as _;
16use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
17use time_core::convert::*;
18
19use crate::date::{MAX_YEAR, MIN_YEAR};
20#[cfg(feature = "formatting")]
21use crate::formatting::Formattable;
22use crate::internal_macros::{
23 carry, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt,
24};
25#[cfg(feature = "parsing")]
26use crate::parsing::Parsable;
27use crate::{
28 error, util, Date, Duration, Month, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
29};
30
31/// The Julian day of the Unix epoch.
32const UNIX_EPOCH_JULIAN_DAY: i32 = OffsetDateTime::UNIX_EPOCH.to_julian_day();
33
34/// A [`PrimitiveDateTime`] with a [`UtcOffset`].
35#[derive(Clone, Copy, Eq)]
36pub struct OffsetDateTime {
37 local_date_time: PrimitiveDateTime,
38 offset: UtcOffset,
39}
40
41impl PartialEq for OffsetDateTime {
42 #[inline]
43 fn eq(&self, other: &Self) -> bool {
44 raw_to_bits((self.year(), self.ordinal(), self.time()))
45 == raw_to_bits(other.to_offset_raw(self.offset()))
46 }
47}
48
49impl PartialOrd for OffsetDateTime {
50 #[inline]
51 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
52 Some(self.cmp(other))
53 }
54}
55
56impl Ord for OffsetDateTime {
57 #[inline]
58 fn cmp(&self, other: &Self) -> Ordering {
59 raw_to_bits((self.year(), self.ordinal(), self.time()))
60 .cmp(&raw_to_bits(other.to_offset_raw(self.offset())))
61 }
62}
63
64impl Hash for OffsetDateTime {
65 #[inline]
66 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
67 raw_to_bits(self.to_utc_raw()).hash(state);
68 }
69}
70
71/// **Note**: This value is explicitly signed, so do not cast this to or treat this as an
72/// unsigned integer. Doing so will lead to incorrect results for values with differing
73/// signs.
74#[inline]
75const fn raw_to_bits((year, ordinal, time): (i32, u16, Time)) -> i128 {
76 ((year as i128) << 74) | ((ordinal as i128) << 64) | (time.as_u64() as i128)
77}
78
79impl OffsetDateTime {
80 /// Midnight, 1 January, 1970 (UTC).
81 ///
82 /// ```rust
83 /// # use time::OffsetDateTime;
84 /// # use time_macros::datetime;
85 /// assert_eq!(OffsetDateTime::UNIX_EPOCH, datetime!(1970-01-01 0:00 UTC));
86 /// ```
87 pub const UNIX_EPOCH: Self =
88 Self::new_in_offset(Date::UNIX_EPOCH, Time::MIDNIGHT, UtcOffset::UTC);
89
90 /// Create a new `OffsetDateTime` with the current date and time in UTC.
91 ///
92 /// ```rust
93 /// # use time::OffsetDateTime;
94 /// # use time_macros::offset;
95 /// assert!(OffsetDateTime::now_utc().year() >= 2019);
96 /// assert_eq!(OffsetDateTime::now_utc().offset(), offset!(UTC));
97 /// ```
98 #[cfg(feature = "std")]
99 #[inline]
100 pub fn now_utc() -> Self {
101 #[cfg(all(
102 target_family = "wasm",
103 not(any(target_os = "emscripten", target_os = "wasi")),
104 feature = "wasm-bindgen"
105 ))]
106 {
107 js_sys::Date::new_0().into()
108 }
109
110 #[cfg(not(all(
111 target_family = "wasm",
112 not(any(target_os = "emscripten", target_os = "wasi")),
113 feature = "wasm-bindgen"
114 )))]
115 std::time::SystemTime::now().into()
116 }
117
118 /// Attempt to create a new `OffsetDateTime` with the current date and time in the local offset.
119 /// If the offset cannot be determined, an error is returned.
120 ///
121 /// ```rust
122 /// # use time::OffsetDateTime;
123 /// # if false {
124 /// assert!(OffsetDateTime::now_local().is_ok());
125 /// # }
126 /// ```
127 #[cfg(feature = "local-offset")]
128 #[inline]
129 pub fn now_local() -> Result<Self, error::IndeterminateOffset> {
130 let t = Self::now_utc();
131 Ok(t.to_offset(UtcOffset::local_offset_at(t)?))
132 }
133
134 /// Create a new `OffsetDateTime` with the given [`Date`], [`Time`], and [`UtcOffset`].
135 ///
136 /// ```
137 /// # use time::{Date, Month, OffsetDateTime, Time, UtcOffset};
138 /// # use time_macros::datetime;
139 /// let dt = OffsetDateTime::new_in_offset(
140 /// Date::from_calendar_date(2024, Month::January, 1)?,
141 /// Time::from_hms_nano(12, 59, 59, 500_000_000)?,
142 /// UtcOffset::from_hms(-5, 0, 0)?,
143 /// );
144 /// assert_eq!(dt, datetime!(2024-01-01 12:59:59.5 -5));
145 /// # Ok::<_, time::error::Error>(())
146 /// ```
147 #[inline]
148 pub const fn new_in_offset(date: Date, time: Time, offset: UtcOffset) -> Self {
149 Self {
150 local_date_time: date.with_time(time),
151 offset,
152 }
153 }
154
155 /// Create a new `OffsetDateTime` with the given [`Date`] and [`Time`] in the UTC timezone.
156 ///
157 /// ```
158 /// # use time::{Date, Month, OffsetDateTime, Time};
159 /// # use time_macros::datetime;
160 /// let dt = OffsetDateTime::new_utc(
161 /// Date::from_calendar_date(2024, Month::January, 1)?,
162 /// Time::from_hms_nano(12, 59, 59, 500_000_000)?,
163 /// );
164 /// assert_eq!(dt, datetime!(2024-01-01 12:59:59.5 UTC));
165 /// # Ok::<_, time::error::Error>(())
166 /// ```
167 #[inline]
168 pub const fn new_utc(date: Date, time: Time) -> Self {
169 PrimitiveDateTime::new(date, time).assume_utc()
170 }
171
172 /// Convert the `OffsetDateTime` from the current [`UtcOffset`] to the provided [`UtcOffset`].
173 ///
174 /// ```rust
175 /// # use time_macros::{datetime, offset};
176 /// assert_eq!(
177 /// datetime!(2000-01-01 0:00 UTC)
178 /// .to_offset(offset!(-1))
179 /// .year(),
180 /// 1999,
181 /// );
182 ///
183 /// // Let's see what time Sydney's new year's celebration is in New York and Los Angeles.
184 ///
185 /// // Construct midnight on new year's in Sydney.
186 /// let sydney = datetime!(2000-01-01 0:00 +11);
187 /// let new_york = sydney.to_offset(offset!(-5));
188 /// let los_angeles = sydney.to_offset(offset!(-8));
189 /// assert_eq!(sydney.hour(), 0);
190 /// assert_eq!(new_york.hour(), 8);
191 /// assert_eq!(los_angeles.hour(), 5);
192 /// ```
193 ///
194 /// # Panics
195 ///
196 /// This method panics if the local date-time in the new offset is outside the supported range.
197 #[inline]
198 #[track_caller]
199 pub const fn to_offset(self, offset: UtcOffset) -> Self {
200 expect_opt!(
201 self.checked_to_offset(offset),
202 "local datetime out of valid range"
203 )
204 }
205
206 /// Convert the `OffsetDateTime` from the current [`UtcOffset`] to the provided [`UtcOffset`],
207 /// returning `None` if the date-time in the resulting offset is invalid.
208 ///
209 /// ```rust
210 /// # use time::PrimitiveDateTime;
211 /// # use time_macros::{datetime, offset};
212 /// assert_eq!(
213 /// datetime!(2000-01-01 0:00 UTC)
214 /// .checked_to_offset(offset!(-1))
215 /// .unwrap()
216 /// .year(),
217 /// 1999,
218 /// );
219 /// assert_eq!(
220 /// PrimitiveDateTime::MAX
221 /// .assume_utc()
222 /// .checked_to_offset(offset!(+1)),
223 /// None,
224 /// );
225 /// ```
226 #[inline]
227 pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<Self> {
228 if self.offset.as_u32() == offset.as_u32() {
229 return Some(self);
230 }
231
232 let (year, ordinal, time) = self.to_offset_raw(offset);
233
234 if year > MAX_YEAR || year < MIN_YEAR {
235 return None;
236 }
237
238 Some(Self::new_in_offset(
239 // Safety: `ordinal` is not zero.
240 unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) },
241 time,
242 offset,
243 ))
244 }
245
246 /// Convert the `OffsetDateTime` from the current [`UtcOffset`] to UTC, returning a
247 /// [`UtcDateTime`].
248 ///
249 /// ```rust
250 /// # use time_macros::datetime;
251 /// assert_eq!(
252 /// datetime!(2000-01-01 0:00 +1)
253 /// .to_utc()
254 /// .year(),
255 /// 1999,
256 /// );
257 /// ```
258 ///
259 /// # Panics
260 ///
261 /// This method panics if the UTC date-time is outside the supported range.
262 #[inline]
263 #[track_caller]
264 pub const fn to_utc(self) -> UtcDateTime {
265 expect_opt!(self.checked_to_utc(), "local datetime out of valid range")
266 }
267
268 /// Convert the `OffsetDateTime` from the current [`UtcOffset`] to UTC, returning `None` if the
269 /// UTC date-time is invalid. Returns a [`UtcDateTime`].
270 ///
271 /// ```rust
272 /// # use time_macros::datetime;
273 /// assert_eq!(
274 /// datetime!(2000-01-01 0:00 +1)
275 /// .checked_to_utc()
276 /// .unwrap()
277 /// .year(),
278 /// 1999,
279 /// );
280 /// assert_eq!(
281 #[cfg_attr(
282 feature = "large-dates",
283 doc = " datetime!(+999999-12-31 23:59:59 -1).checked_to_utc(),"
284 )]
285 #[cfg_attr(
286 not(feature = "large-dates"),
287 doc = " datetime!(9999-12-31 23:59:59 -1).checked_to_utc(),"
288 )]
289 /// None,
290 /// );
291 /// ```
292 #[inline]
293 pub const fn checked_to_utc(self) -> Option<UtcDateTime> {
294 if self.offset.is_utc() {
295 return Some(self.local_date_time.as_utc());
296 }
297
298 let (year, ordinal, time) = self.to_utc_raw();
299
300 if year > MAX_YEAR || year < MIN_YEAR {
301 return None;
302 }
303
304 Some(UtcDateTime::new(
305 // Safety: `ordinal` is not zero.
306 unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) },
307 time,
308 ))
309 }
310
311 /// Equivalent to `.to_utc()`, but returning the year, ordinal, and time. This avoids
312 /// constructing an invalid [`Date`] if the new value is out of range.
313 #[inline]
314 pub(crate) const fn to_utc_raw(self) -> (i32, u16, Time) {
315 let from = self.offset;
316
317 // Fast path for when no conversion is necessary.
318 if from.is_utc() {
319 return (self.year(), self.ordinal(), self.time());
320 }
321
322 let (second, carry) = carry!(@most_once
323 self.second() as i8 - from.seconds_past_minute(),
324 0..Second::per_t(Minute)
325 );
326 let (minute, carry) = carry!(@most_once
327 self.minute() as i8 - from.minutes_past_hour() + carry,
328 0..Minute::per_t(Hour)
329 );
330 let (hour, carry) = carry!(@most_twice
331 self.hour() as i8 - from.whole_hours() + carry,
332 0..Hour::per_t(Day)
333 );
334 let (mut year, ordinal) = self.to_ordinal_date();
335 let mut ordinal = ordinal as i16 + carry;
336 cascade!(ordinal => year);
337
338 debug_assert!(ordinal > 0);
339 debug_assert!(ordinal <= util::days_in_year(year) as i16);
340
341 (
342 year,
343 ordinal as u16,
344 // Safety: The cascades above ensure the values are in range.
345 unsafe {
346 Time::__from_hms_nanos_unchecked(
347 hour as u8,
348 minute as u8,
349 second as u8,
350 self.nanosecond(),
351 )
352 },
353 )
354 }
355
356 /// Equivalent to `.to_offset(offset)`, but returning the year, ordinal, and time. This avoids
357 /// constructing an invalid [`Date`] if the new value is out of range.
358 #[inline]
359 pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) {
360 let from = self.offset;
361 let to = offset;
362
363 // Fast path for when no conversion is necessary.
364 if from.as_u32() == to.as_u32() {
365 return (self.year(), self.ordinal(), self.time());
366 }
367
368 let (second, carry) = carry!(@most_twice
369 self.second() as i16 - from.seconds_past_minute() as i16
370 + to.seconds_past_minute() as i16,
371 0..Second::per_t(Minute)
372 );
373 let (minute, carry) = carry!(@most_twice
374 self.minute() as i16 - from.minutes_past_hour() as i16
375 + to.minutes_past_hour() as i16
376 + carry,
377 0..Minute::per_t(Hour)
378 );
379 let (hour, carry) = carry!(@most_thrice
380 self.hour() as i8 - from.whole_hours() + to.whole_hours() + carry,
381 0..Hour::per_t(Day)
382 );
383 let (mut year, ordinal) = self.to_ordinal_date();
384 let mut ordinal = ordinal as i16 + carry;
385 cascade!(ordinal => year);
386
387 debug_assert!(ordinal > 0);
388 debug_assert!(ordinal <= util::days_in_year(year) as i16);
389
390 (
391 year,
392 ordinal as u16,
393 // Safety: The cascades above ensure the values are in range.
394 unsafe {
395 Time::__from_hms_nanos_unchecked(
396 hour as u8,
397 minute as u8,
398 second as u8,
399 self.nanosecond(),
400 )
401 },
402 )
403 }
404
405 /// Create an `OffsetDateTime` from the provided Unix timestamp. Calling `.offset()` on the
406 /// resulting value is guaranteed to return UTC.
407 ///
408 /// ```rust
409 /// # use time::OffsetDateTime;
410 /// # use time_macros::datetime;
411 /// assert_eq!(
412 /// OffsetDateTime::from_unix_timestamp(0),
413 /// Ok(OffsetDateTime::UNIX_EPOCH),
414 /// );
415 /// assert_eq!(
416 /// OffsetDateTime::from_unix_timestamp(1_546_300_800),
417 /// Ok(datetime!(2019-01-01 0:00 UTC)),
418 /// );
419 /// ```
420 ///
421 /// If you have a timestamp-nanosecond pair, you can use something along the lines of the
422 /// following:
423 ///
424 /// ```rust
425 /// # use time::{Duration, OffsetDateTime, ext::NumericalDuration};
426 /// let (timestamp, nanos) = (1, 500_000_000);
427 /// assert_eq!(
428 /// OffsetDateTime::from_unix_timestamp(timestamp)? + Duration::nanoseconds(nanos),
429 /// OffsetDateTime::UNIX_EPOCH + 1.5.seconds()
430 /// );
431 /// # Ok::<_, time::Error>(())
432 /// ```
433 #[inline]
434 pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange> {
435 type Timestamp = RangedI64<
436 {
437 OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
438 .unix_timestamp()
439 },
440 {
441 OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC).unix_timestamp()
442 },
443 >;
444 ensure_ranged!(Timestamp: timestamp);
445
446 // Use the unchecked method here, as the input validity has already been verified.
447 // Safety: The Julian day number is in range.
448 let date = unsafe {
449 Date::from_julian_day_unchecked(
450 UNIX_EPOCH_JULIAN_DAY + div_floor!(timestamp, Second::per_t::<i64>(Day)) as i32,
451 )
452 };
453
454 let seconds_within_day = timestamp.rem_euclid(Second::per_t(Day));
455 // Safety: All values are in range.
456 let time = unsafe {
457 Time::__from_hms_nanos_unchecked(
458 (seconds_within_day / Second::per_t::<i64>(Hour)) as u8,
459 ((seconds_within_day % Second::per_t::<i64>(Hour)) / Minute::per_t::<i64>(Hour))
460 as u8,
461 (seconds_within_day % Second::per_t::<i64>(Minute)) as u8,
462 0,
463 )
464 };
465
466 Ok(Self::new_in_offset(date, time, UtcOffset::UTC))
467 }
468
469 /// Construct an `OffsetDateTime` from the provided Unix timestamp (in nanoseconds). Calling
470 /// `.offset()` on the resulting value is guaranteed to return UTC.
471 ///
472 /// ```rust
473 /// # use time::OffsetDateTime;
474 /// # use time_macros::datetime;
475 /// assert_eq!(
476 /// OffsetDateTime::from_unix_timestamp_nanos(0),
477 /// Ok(OffsetDateTime::UNIX_EPOCH),
478 /// );
479 /// assert_eq!(
480 /// OffsetDateTime::from_unix_timestamp_nanos(1_546_300_800_000_000_000),
481 /// Ok(datetime!(2019-01-01 0:00 UTC)),
482 /// );
483 /// ```
484 #[inline]
485 pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange> {
486 let datetime = const_try!(Self::from_unix_timestamp(div_floor!(
487 timestamp,
488 Nanosecond::per_t::<i128>(Second)
489 ) as i64));
490
491 Ok(Self::new_in_offset(
492 datetime.date(),
493 // Safety: `nanosecond` is in range due to `rem_euclid`.
494 unsafe {
495 Time::__from_hms_nanos_unchecked(
496 datetime.hour(),
497 datetime.minute(),
498 datetime.second(),
499 timestamp.rem_euclid(Nanosecond::per_t(Second)) as u32,
500 )
501 },
502 UtcOffset::UTC,
503 ))
504 }
505
506 /// Get the [`UtcOffset`].
507 ///
508 /// ```rust
509 /// # use time_macros::{datetime, offset};
510 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).offset(), offset!(UTC));
511 /// assert_eq!(datetime!(2019-01-01 0:00 +1).offset(), offset!(+1));
512 /// ```
513 #[inline]
514 pub const fn offset(self) -> UtcOffset {
515 self.offset
516 }
517
518 /// Get the [Unix timestamp](https://en.wikipedia.org/wiki/Unix_time).
519 ///
520 /// ```rust
521 /// # use time_macros::datetime;
522 /// assert_eq!(datetime!(1970-01-01 0:00 UTC).unix_timestamp(), 0);
523 /// assert_eq!(datetime!(1970-01-01 0:00 -1).unix_timestamp(), 3_600);
524 /// ```
525 #[inline]
526 pub const fn unix_timestamp(self) -> i64 {
527 let days = (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64)
528 * Second::per_t::<i64>(Day);
529 let hours = self.hour() as i64 * Second::per_t::<i64>(Hour);
530 let minutes = self.minute() as i64 * Second::per_t::<i64>(Minute);
531 let seconds = self.second() as i64;
532 let offset_seconds = self.offset.whole_seconds() as i64;
533 days + hours + minutes + seconds - offset_seconds
534 }
535
536 /// Get the Unix timestamp in nanoseconds.
537 ///
538 /// ```rust
539 /// use time_macros::datetime;
540 /// assert_eq!(datetime!(1970-01-01 0:00 UTC).unix_timestamp_nanos(), 0);
541 /// assert_eq!(
542 /// datetime!(1970-01-01 0:00 -1).unix_timestamp_nanos(),
543 /// 3_600_000_000_000,
544 /// );
545 /// ```
546 #[inline]
547 pub const fn unix_timestamp_nanos(self) -> i128 {
548 self.unix_timestamp() as i128 * Nanosecond::per_t::<i128>(Second)
549 + self.nanosecond() as i128
550 }
551
552 /// Get the [`PrimitiveDateTime`] in the stored offset.
553 #[inline]
554 pub(crate) const fn date_time(self) -> PrimitiveDateTime {
555 self.local_date_time
556 }
557
558 /// Get the [`Date`] in the stored offset.
559 ///
560 /// ```rust
561 /// # use time_macros::{date, datetime, offset};
562 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).date(), date!(2019-01-01));
563 /// assert_eq!(
564 /// datetime!(2019-01-01 0:00 UTC)
565 /// .to_offset(offset!(-1))
566 /// .date(),
567 /// date!(2018-12-31),
568 /// );
569 /// ```
570 #[inline]
571 pub const fn date(self) -> Date {
572 self.date_time().date()
573 }
574
575 /// Get the [`Time`] in the stored offset.
576 ///
577 /// ```rust
578 /// # use time_macros::{datetime, offset, time};
579 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).time(), time!(0:00));
580 /// assert_eq!(
581 /// datetime!(2019-01-01 0:00 UTC)
582 /// .to_offset(offset!(-1))
583 /// .time(),
584 /// time!(23:00)
585 /// );
586 /// ```
587 #[inline]
588 pub const fn time(self) -> Time {
589 self.date_time().time()
590 }
591
592 /// Get the year of the date in the stored offset.
593 ///
594 /// ```rust
595 /// # use time_macros::{datetime, offset};
596 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).year(), 2019);
597 /// assert_eq!(
598 /// datetime!(2019-12-31 23:00 UTC)
599 /// .to_offset(offset!(+1))
600 /// .year(),
601 /// 2020,
602 /// );
603 /// assert_eq!(datetime!(2020-01-01 0:00 UTC).year(), 2020);
604 /// ```
605 #[inline]
606 pub const fn year(self) -> i32 {
607 self.date().year()
608 }
609
610 /// Get the month of the date in the stored offset.
611 ///
612 /// ```rust
613 /// # use time::Month;
614 /// # use time_macros::{datetime, offset};
615 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).month(), Month::January);
616 /// assert_eq!(
617 /// datetime!(2019-12-31 23:00 UTC)
618 /// .to_offset(offset!(+1))
619 /// .month(),
620 /// Month::January,
621 /// );
622 /// ```
623 #[inline]
624 pub const fn month(self) -> Month {
625 self.date().month()
626 }
627
628 /// Get the day of the date in the stored offset.
629 ///
630 /// The returned value will always be in the range `1..=31`.
631 ///
632 /// ```rust
633 /// # use time_macros::{datetime, offset};
634 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).day(), 1);
635 /// assert_eq!(
636 /// datetime!(2019-12-31 23:00 UTC)
637 /// .to_offset(offset!(+1))
638 /// .day(),
639 /// 1,
640 /// );
641 /// ```
642 #[inline]
643 pub const fn day(self) -> u8 {
644 self.date().day()
645 }
646
647 /// Get the day of the year of the date in the stored offset.
648 ///
649 /// The returned value will always be in the range `1..=366`.
650 ///
651 /// ```rust
652 /// # use time_macros::{datetime, offset};
653 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).ordinal(), 1);
654 /// assert_eq!(
655 /// datetime!(2019-12-31 23:00 UTC)
656 /// .to_offset(offset!(+1))
657 /// .ordinal(),
658 /// 1,
659 /// );
660 /// ```
661 #[inline]
662 pub const fn ordinal(self) -> u16 {
663 self.date().ordinal()
664 }
665
666 /// Get the ISO week number of the date in the stored offset.
667 ///
668 /// The returned value will always be in the range `1..=53`.
669 ///
670 /// ```rust
671 /// # use time_macros::datetime;
672 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).iso_week(), 1);
673 /// assert_eq!(datetime!(2020-01-01 0:00 UTC).iso_week(), 1);
674 /// assert_eq!(datetime!(2020-12-31 0:00 UTC).iso_week(), 53);
675 /// assert_eq!(datetime!(2021-01-01 0:00 UTC).iso_week(), 53);
676 /// ```
677 #[inline]
678 pub const fn iso_week(self) -> u8 {
679 self.date().iso_week()
680 }
681
682 /// Get the week number where week 1 begins on the first Sunday.
683 ///
684 /// The returned value will always be in the range `0..=53`.
685 ///
686 /// ```rust
687 /// # use time_macros::datetime;
688 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).sunday_based_week(), 0);
689 /// assert_eq!(datetime!(2020-01-01 0:00 UTC).sunday_based_week(), 0);
690 /// assert_eq!(datetime!(2020-12-31 0:00 UTC).sunday_based_week(), 52);
691 /// assert_eq!(datetime!(2021-01-01 0:00 UTC).sunday_based_week(), 0);
692 /// ```
693 #[inline]
694 pub const fn sunday_based_week(self) -> u8 {
695 self.date().sunday_based_week()
696 }
697
698 /// Get the week number where week 1 begins on the first Monday.
699 ///
700 /// The returned value will always be in the range `0..=53`.
701 ///
702 /// ```rust
703 /// # use time_macros::datetime;
704 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).monday_based_week(), 0);
705 /// assert_eq!(datetime!(2020-01-01 0:00 UTC).monday_based_week(), 0);
706 /// assert_eq!(datetime!(2020-12-31 0:00 UTC).monday_based_week(), 52);
707 /// assert_eq!(datetime!(2021-01-01 0:00 UTC).monday_based_week(), 0);
708 /// ```
709 #[inline]
710 pub const fn monday_based_week(self) -> u8 {
711 self.date().monday_based_week()
712 }
713
714 /// Get the year, month, and day.
715 ///
716 /// ```rust
717 /// # use time::Month;
718 /// # use time_macros::datetime;
719 /// assert_eq!(
720 /// datetime!(2019-01-01 0:00 UTC).to_calendar_date(),
721 /// (2019, Month::January, 1)
722 /// );
723 /// ```
724 #[inline]
725 pub const fn to_calendar_date(self) -> (i32, Month, u8) {
726 self.date().to_calendar_date()
727 }
728
729 /// Get the year and ordinal day number.
730 ///
731 /// ```rust
732 /// # use time_macros::datetime;
733 /// assert_eq!(
734 /// datetime!(2019-01-01 0:00 UTC).to_ordinal_date(),
735 /// (2019, 1)
736 /// );
737 /// ```
738 #[inline]
739 pub const fn to_ordinal_date(self) -> (i32, u16) {
740 self.date().to_ordinal_date()
741 }
742
743 /// Get the ISO 8601 year, week number, and weekday.
744 ///
745 /// ```rust
746 /// # use time::Weekday::*;
747 /// # use time_macros::datetime;
748 /// assert_eq!(
749 /// datetime!(2019-01-01 0:00 UTC).to_iso_week_date(),
750 /// (2019, 1, Tuesday)
751 /// );
752 /// assert_eq!(
753 /// datetime!(2019-10-04 0:00 UTC).to_iso_week_date(),
754 /// (2019, 40, Friday)
755 /// );
756 /// assert_eq!(
757 /// datetime!(2020-01-01 0:00 UTC).to_iso_week_date(),
758 /// (2020, 1, Wednesday)
759 /// );
760 /// assert_eq!(
761 /// datetime!(2020-12-31 0:00 UTC).to_iso_week_date(),
762 /// (2020, 53, Thursday)
763 /// );
764 /// assert_eq!(
765 /// datetime!(2021-01-01 0:00 UTC).to_iso_week_date(),
766 /// (2020, 53, Friday)
767 /// );
768 /// ```
769 #[inline]
770 pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
771 self.date().to_iso_week_date()
772 }
773
774 /// Get the weekday of the date in the stored offset.
775 ///
776 /// ```rust
777 /// # use time::Weekday::*;
778 /// # use time_macros::datetime;
779 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).weekday(), Tuesday);
780 /// assert_eq!(datetime!(2019-02-01 0:00 UTC).weekday(), Friday);
781 /// assert_eq!(datetime!(2019-03-01 0:00 UTC).weekday(), Friday);
782 /// ```
783 #[inline]
784 pub const fn weekday(self) -> Weekday {
785 self.date().weekday()
786 }
787
788 /// Get the Julian day for the date. The time is not taken into account for this calculation.
789 ///
790 /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
791 /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
792 ///
793 /// ```rust
794 /// # use time_macros::datetime;
795 /// assert_eq!(datetime!(-4713-11-24 0:00 UTC).to_julian_day(), 0);
796 /// assert_eq!(datetime!(2000-01-01 0:00 UTC).to_julian_day(), 2_451_545);
797 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).to_julian_day(), 2_458_485);
798 /// assert_eq!(datetime!(2019-12-31 0:00 UTC).to_julian_day(), 2_458_849);
799 /// ```
800 #[inline]
801 pub const fn to_julian_day(self) -> i32 {
802 self.date().to_julian_day()
803 }
804
805 /// Get the clock hour, minute, and second.
806 ///
807 /// ```rust
808 /// # use time_macros::datetime;
809 /// assert_eq!(datetime!(2020-01-01 0:00:00 UTC).to_hms(), (0, 0, 0));
810 /// assert_eq!(datetime!(2020-01-01 23:59:59 UTC).to_hms(), (23, 59, 59));
811 /// ```
812 #[inline]
813 pub const fn to_hms(self) -> (u8, u8, u8) {
814 self.time().as_hms()
815 }
816
817 /// Get the clock hour, minute, second, and millisecond.
818 ///
819 /// ```rust
820 /// # use time_macros::datetime;
821 /// assert_eq!(
822 /// datetime!(2020-01-01 0:00:00 UTC).to_hms_milli(),
823 /// (0, 0, 0, 0)
824 /// );
825 /// assert_eq!(
826 /// datetime!(2020-01-01 23:59:59.999 UTC).to_hms_milli(),
827 /// (23, 59, 59, 999)
828 /// );
829 /// ```
830 #[inline]
831 pub const fn to_hms_milli(self) -> (u8, u8, u8, u16) {
832 self.time().as_hms_milli()
833 }
834
835 /// Get the clock hour, minute, second, and microsecond.
836 ///
837 /// ```rust
838 /// # use time_macros::datetime;
839 /// assert_eq!(
840 /// datetime!(2020-01-01 0:00:00 UTC).to_hms_micro(),
841 /// (0, 0, 0, 0)
842 /// );
843 /// assert_eq!(
844 /// datetime!(2020-01-01 23:59:59.999_999 UTC).to_hms_micro(),
845 /// (23, 59, 59, 999_999)
846 /// );
847 /// ```
848 #[inline]
849 pub const fn to_hms_micro(self) -> (u8, u8, u8, u32) {
850 self.time().as_hms_micro()
851 }
852
853 /// Get the clock hour, minute, second, and nanosecond.
854 ///
855 /// ```rust
856 /// # use time_macros::datetime;
857 /// assert_eq!(
858 /// datetime!(2020-01-01 0:00:00 UTC).to_hms_nano(),
859 /// (0, 0, 0, 0)
860 /// );
861 /// assert_eq!(
862 /// datetime!(2020-01-01 23:59:59.999_999_999 UTC).to_hms_nano(),
863 /// (23, 59, 59, 999_999_999)
864 /// );
865 /// ```
866 #[inline]
867 pub const fn to_hms_nano(self) -> (u8, u8, u8, u32) {
868 self.time().as_hms_nano()
869 }
870
871 /// Get the clock hour in the stored offset.
872 ///
873 /// The returned value will always be in the range `0..24`.
874 ///
875 /// ```rust
876 /// # use time_macros::{datetime, offset};
877 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).hour(), 0);
878 /// assert_eq!(
879 /// datetime!(2019-01-01 23:59:59 UTC)
880 /// .to_offset(offset!(-2))
881 /// .hour(),
882 /// 21,
883 /// );
884 /// ```
885 #[inline]
886 pub const fn hour(self) -> u8 {
887 self.time().hour()
888 }
889
890 /// Get the minute within the hour in the stored offset.
891 ///
892 /// The returned value will always be in the range `0..60`.
893 ///
894 /// ```rust
895 /// # use time_macros::{datetime, offset};
896 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).minute(), 0);
897 /// assert_eq!(
898 /// datetime!(2019-01-01 23:59:59 UTC)
899 /// .to_offset(offset!(+0:30))
900 /// .minute(),
901 /// 29,
902 /// );
903 /// ```
904 #[inline]
905 pub const fn minute(self) -> u8 {
906 self.time().minute()
907 }
908
909 /// Get the second within the minute in the stored offset.
910 ///
911 /// The returned value will always be in the range `0..60`.
912 ///
913 /// ```rust
914 /// # use time_macros::{datetime, offset};
915 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).second(), 0);
916 /// assert_eq!(
917 /// datetime!(2019-01-01 23:59:59 UTC)
918 /// .to_offset(offset!(+0:00:30))
919 /// .second(),
920 /// 29,
921 /// );
922 /// ```
923 #[inline]
924 pub const fn second(self) -> u8 {
925 self.time().second()
926 }
927
928 // Because a `UtcOffset` is limited in resolution to one second, any subsecond value will not
929 // change when adjusting for the offset.
930
931 /// Get the milliseconds within the second in the stored offset.
932 ///
933 /// The returned value will always be in the range `0..1_000`.
934 ///
935 /// ```rust
936 /// # use time_macros::datetime;
937 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).millisecond(), 0);
938 /// assert_eq!(datetime!(2019-01-01 23:59:59.999 UTC).millisecond(), 999);
939 /// ```
940 #[inline]
941 pub const fn millisecond(self) -> u16 {
942 self.time().millisecond()
943 }
944
945 /// Get the microseconds within the second in the stored offset.
946 ///
947 /// The returned value will always be in the range `0..1_000_000`.
948 ///
949 /// ```rust
950 /// # use time_macros::datetime;
951 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).microsecond(), 0);
952 /// assert_eq!(
953 /// datetime!(2019-01-01 23:59:59.999_999 UTC).microsecond(),
954 /// 999_999,
955 /// );
956 /// ```
957 #[inline]
958 pub const fn microsecond(self) -> u32 {
959 self.time().microsecond()
960 }
961
962 /// Get the nanoseconds within the second in the stored offset.
963 ///
964 /// The returned value will always be in the range `0..1_000_000_000`.
965 ///
966 /// ```rust
967 /// # use time_macros::datetime;
968 /// assert_eq!(datetime!(2019-01-01 0:00 UTC).nanosecond(), 0);
969 /// assert_eq!(
970 /// datetime!(2019-01-01 23:59:59.999_999_999 UTC).nanosecond(),
971 /// 999_999_999,
972 /// );
973 /// ```
974 #[inline]
975 pub const fn nanosecond(self) -> u32 {
976 self.time().nanosecond()
977 }
978
979 /// Computes `self + duration`, returning `None` if an overflow occurred.
980 ///
981 /// ```
982 /// # use time::{Date, ext::NumericalDuration};
983 /// # use time_macros::{datetime, offset};
984 /// let datetime = Date::MIN.midnight().assume_offset(offset!(+10));
985 /// assert_eq!(datetime.checked_add((-2).days()), None);
986 ///
987 /// let datetime = Date::MAX.midnight().assume_offset(offset!(+10));
988 /// assert_eq!(datetime.checked_add(2.days()), None);
989 ///
990 /// assert_eq!(
991 /// datetime!(2019-11-25 15:30 +10).checked_add(27.hours()),
992 /// Some(datetime!(2019-11-26 18:30 +10))
993 /// );
994 /// ```
995 #[inline]
996 pub const fn checked_add(self, duration: Duration) -> Option<Self> {
997 Some(const_try_opt!(self.date_time().checked_add(duration)).assume_offset(self.offset()))
998 }
999
1000 /// Computes `self - duration`, returning `None` if an overflow occurred.
1001 ///
1002 /// ```
1003 /// # use time::{Date, ext::NumericalDuration};
1004 /// # use time_macros::{datetime, offset};
1005 /// let datetime = Date::MIN.midnight().assume_offset(offset!(+10));
1006 /// assert_eq!(datetime.checked_sub(2.days()), None);
1007 ///
1008 /// let datetime = Date::MAX.midnight().assume_offset(offset!(+10));
1009 /// assert_eq!(datetime.checked_sub((-2).days()), None);
1010 ///
1011 /// assert_eq!(
1012 /// datetime!(2019-11-25 15:30 +10).checked_sub(27.hours()),
1013 /// Some(datetime!(2019-11-24 12:30 +10))
1014 /// );
1015 /// ```
1016 #[inline]
1017 pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
1018 Some(const_try_opt!(self.date_time().checked_sub(duration)).assume_offset(self.offset()))
1019 }
1020
1021 /// Computes `self + duration`, saturating value on overflow.
1022 ///
1023 /// ```
1024 /// # use time::ext::NumericalDuration;
1025 /// # use time_macros::datetime;
1026 /// assert_eq!(
1027 #[cfg_attr(
1028 feature = "large-dates",
1029 doc = " datetime!(-999999-01-01 0:00 +10).saturating_add((-2).days()),"
1030 )]
1031 #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")]
1032 #[cfg_attr(
1033 not(feature = "large-dates"),
1034 doc = " datetime!(-9999-01-01 0:00 +10).saturating_add((-2).days()),"
1035 )]
1036 #[cfg_attr(
1037 not(feature = "large-dates"),
1038 doc = " datetime!(-9999-01-01 0:00 +10)"
1039 )]
1040 /// );
1041 ///
1042 /// assert_eq!(
1043 #[cfg_attr(
1044 feature = "large-dates",
1045 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days()),"
1046 )]
1047 #[cfg_attr(
1048 feature = "large-dates",
1049 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)"
1050 )]
1051 #[cfg_attr(
1052 not(feature = "large-dates"),
1053 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days()),"
1054 )]
1055 #[cfg_attr(
1056 not(feature = "large-dates"),
1057 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
1058 )]
1059 /// );
1060 ///
1061 /// assert_eq!(
1062 /// datetime!(2019-11-25 15:30 +10).saturating_add(27.hours()),
1063 /// datetime!(2019-11-26 18:30 +10)
1064 /// );
1065 /// ```
1066 #[inline]
1067 pub const fn saturating_add(self, duration: Duration) -> Self {
1068 if let Some(datetime) = self.checked_add(duration) {
1069 datetime
1070 } else if duration.is_negative() {
1071 PrimitiveDateTime::MIN.assume_offset(self.offset())
1072 } else {
1073 PrimitiveDateTime::MAX.assume_offset(self.offset())
1074 }
1075 }
1076
1077 /// Computes `self - duration`, saturating value on overflow.
1078 ///
1079 /// ```
1080 /// # use time::ext::NumericalDuration;
1081 /// # use time_macros::datetime;
1082 /// assert_eq!(
1083 #[cfg_attr(
1084 feature = "large-dates",
1085 doc = " datetime!(-999999-01-01 0:00 +10).saturating_sub(2.days()),"
1086 )]
1087 #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")]
1088 #[cfg_attr(
1089 not(feature = "large-dates"),
1090 doc = " datetime!(-9999-01-01 0:00 +10).saturating_sub(2.days()),"
1091 )]
1092 #[cfg_attr(
1093 not(feature = "large-dates"),
1094 doc = " datetime!(-9999-01-01 0:00 +10)"
1095 )]
1096 /// );
1097 ///
1098 /// assert_eq!(
1099 #[cfg_attr(
1100 feature = "large-dates",
1101 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days()),"
1102 )]
1103 #[cfg_attr(
1104 feature = "large-dates",
1105 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)"
1106 )]
1107 #[cfg_attr(
1108 not(feature = "large-dates"),
1109 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days()),"
1110 )]
1111 #[cfg_attr(
1112 not(feature = "large-dates"),
1113 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
1114 )]
1115 /// );
1116 ///
1117 /// assert_eq!(
1118 /// datetime!(2019-11-25 15:30 +10).saturating_sub(27.hours()),
1119 /// datetime!(2019-11-24 12:30 +10)
1120 /// );
1121 /// ```
1122 #[inline]
1123 pub const fn saturating_sub(self, duration: Duration) -> Self {
1124 if let Some(datetime) = self.checked_sub(duration) {
1125 datetime
1126 } else if duration.is_negative() {
1127 PrimitiveDateTime::MAX.assume_offset(self.offset())
1128 } else {
1129 PrimitiveDateTime::MIN.assume_offset(self.offset())
1130 }
1131 }
1132}
1133
1134/// Methods that replace part of the `OffsetDateTime`.
1135impl OffsetDateTime {
1136 /// Replace the time, which is assumed to be in the stored offset. The date and offset
1137 /// components are unchanged.
1138 ///
1139 /// ```rust
1140 /// # use time_macros::{datetime, time};
1141 /// assert_eq!(
1142 /// datetime!(2020-01-01 5:00 UTC).replace_time(time!(12:00)),
1143 /// datetime!(2020-01-01 12:00 UTC)
1144 /// );
1145 /// assert_eq!(
1146 /// datetime!(2020-01-01 12:00 -5).replace_time(time!(7:00)),
1147 /// datetime!(2020-01-01 7:00 -5)
1148 /// );
1149 /// assert_eq!(
1150 /// datetime!(2020-01-01 0:00 +1).replace_time(time!(12:00)),
1151 /// datetime!(2020-01-01 12:00 +1)
1152 /// );
1153 /// ```
1154 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1155 #[inline]
1156 pub const fn replace_time(self, time: Time) -> Self {
1157 Self::new_in_offset(self.date(), time, self.offset())
1158 }
1159
1160 /// Replace the date, which is assumed to be in the stored offset. The time and offset
1161 /// components are unchanged.
1162 ///
1163 /// ```rust
1164 /// # use time_macros::{datetime, date};
1165 /// assert_eq!(
1166 /// datetime!(2020-01-01 12:00 UTC).replace_date(date!(2020-01-30)),
1167 /// datetime!(2020-01-30 12:00 UTC)
1168 /// );
1169 /// assert_eq!(
1170 /// datetime!(2020-01-01 0:00 +1).replace_date(date!(2020-01-30)),
1171 /// datetime!(2020-01-30 0:00 +1)
1172 /// );
1173 /// ```
1174 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1175 #[inline]
1176 pub const fn replace_date(self, date: Date) -> Self {
1177 Self::new_in_offset(date, self.time(), self.offset())
1178 }
1179
1180 /// Replace the date and time, which are assumed to be in the stored offset. The offset
1181 /// component remains unchanged.
1182 ///
1183 /// ```rust
1184 /// # use time_macros::datetime;
1185 /// assert_eq!(
1186 /// datetime!(2020-01-01 12:00 UTC).replace_date_time(datetime!(2020-01-30 16:00)),
1187 /// datetime!(2020-01-30 16:00 UTC)
1188 /// );
1189 /// assert_eq!(
1190 /// datetime!(2020-01-01 12:00 +1).replace_date_time(datetime!(2020-01-30 0:00)),
1191 /// datetime!(2020-01-30 0:00 +1)
1192 /// );
1193 /// ```
1194 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1195 #[inline]
1196 pub const fn replace_date_time(self, date_time: PrimitiveDateTime) -> Self {
1197 date_time.assume_offset(self.offset())
1198 }
1199
1200 /// Replace the offset. The date and time components remain unchanged.
1201 ///
1202 /// ```rust
1203 /// # use time_macros::{datetime, offset};
1204 /// assert_eq!(
1205 /// datetime!(2020-01-01 0:00 UTC).replace_offset(offset!(-5)),
1206 /// datetime!(2020-01-01 0:00 -5)
1207 /// );
1208 /// ```
1209 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1210 #[inline]
1211 pub const fn replace_offset(self, offset: UtcOffset) -> Self {
1212 self.date_time().assume_offset(offset)
1213 }
1214
1215 /// Replace the year. The month and day will be unchanged.
1216 ///
1217 /// ```rust
1218 /// # use time_macros::datetime;
1219 /// assert_eq!(
1220 /// datetime!(2022-02-18 12:00 +01).replace_year(2019),
1221 /// Ok(datetime!(2019-02-18 12:00 +01))
1222 /// );
1223 /// assert!(datetime!(2022-02-18 12:00 +01).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year
1224 /// assert!(datetime!(2022-02-18 12:00 +01).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year
1225 /// ```
1226 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1227 #[inline]
1228 pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
1229 Ok(const_try!(self.date_time().replace_year(year)).assume_offset(self.offset()))
1230 }
1231
1232 /// Replace the month of the year.
1233 ///
1234 /// ```rust
1235 /// # use time_macros::datetime;
1236 /// # use time::Month;
1237 /// assert_eq!(
1238 /// datetime!(2022-02-18 12:00 +01).replace_month(Month::January),
1239 /// Ok(datetime!(2022-01-18 12:00 +01))
1240 /// );
1241 /// assert!(datetime!(2022-01-30 12:00 +01).replace_month(Month::February).is_err()); // 30 isn't a valid day in February
1242 /// ```
1243 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1244 #[inline]
1245 pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
1246 Ok(const_try!(self.date_time().replace_month(month)).assume_offset(self.offset()))
1247 }
1248
1249 /// Replace the day of the month.
1250 ///
1251 /// ```rust
1252 /// # use time_macros::datetime;
1253 /// assert_eq!(
1254 /// datetime!(2022-02-18 12:00 +01).replace_day(1),
1255 /// Ok(datetime!(2022-02-01 12:00 +01))
1256 /// );
1257 /// assert!(datetime!(2022-02-18 12:00 +01).replace_day(0).is_err()); // 00 isn't a valid day
1258 /// assert!(datetime!(2022-02-18 12:00 +01).replace_day(30).is_err()); // 30 isn't a valid day in February
1259 /// ```
1260 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1261 #[inline]
1262 pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
1263 Ok(const_try!(self.date_time().replace_day(day)).assume_offset(self.offset()))
1264 }
1265
1266 /// Replace the day of the year.
1267 ///
1268 /// ```rust
1269 /// # use time_macros::datetime;
1270 /// assert_eq!(datetime!(2022-049 12:00 +01).replace_ordinal(1), Ok(datetime!(2022-001 12:00 +01)));
1271 /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal
1272 /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(366).is_err()); // 2022 isn't a leap year
1273 /// ```
1274 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1275 #[inline]
1276 pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
1277 Ok(const_try!(self.date_time().replace_ordinal(ordinal)).assume_offset(self.offset()))
1278 }
1279
1280 /// Replace the clock hour.
1281 ///
1282 /// ```rust
1283 /// # use time_macros::datetime;
1284 /// assert_eq!(
1285 /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_hour(7),
1286 /// Ok(datetime!(2022-02-18 07:02:03.004_005_006 +01))
1287 /// );
1288 /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_hour(24).is_err()); // 24 isn't a valid hour
1289 /// ```
1290 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1291 #[inline]
1292 pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
1293 Ok(const_try!(self.date_time().replace_hour(hour)).assume_offset(self.offset()))
1294 }
1295
1296 /// Replace the minutes within the hour.
1297 ///
1298 /// ```rust
1299 /// # use time_macros::datetime;
1300 /// assert_eq!(
1301 /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_minute(7),
1302 /// Ok(datetime!(2022-02-18 01:07:03.004_005_006 +01))
1303 /// );
1304 /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_minute(60).is_err()); // 60 isn't a valid minute
1305 /// ```
1306 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1307 #[inline]
1308 pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
1309 Ok(const_try!(self.date_time().replace_minute(minute)).assume_offset(self.offset()))
1310 }
1311
1312 /// Replace the seconds within the minute.
1313 ///
1314 /// ```rust
1315 /// # use time_macros::datetime;
1316 /// assert_eq!(
1317 /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_second(7),
1318 /// Ok(datetime!(2022-02-18 01:02:07.004_005_006 +01))
1319 /// );
1320 /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_second(60).is_err()); // 60 isn't a valid second
1321 /// ```
1322 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1323 #[inline]
1324 pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
1325 Ok(const_try!(self.date_time().replace_second(second)).assume_offset(self.offset()))
1326 }
1327
1328 /// Replace the milliseconds within the second.
1329 ///
1330 /// ```rust
1331 /// # use time_macros::datetime;
1332 /// assert_eq!(
1333 /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_millisecond(7),
1334 /// Ok(datetime!(2022-02-18 01:02:03.007 +01))
1335 /// );
1336 /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond
1337 /// ```
1338 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1339 #[inline]
1340 pub const fn replace_millisecond(
1341 self,
1342 millisecond: u16,
1343 ) -> Result<Self, error::ComponentRange> {
1344 Ok(
1345 const_try!(self.date_time().replace_millisecond(millisecond))
1346 .assume_offset(self.offset()),
1347 )
1348 }
1349
1350 /// Replace the microseconds within the second.
1351 ///
1352 /// ```rust
1353 /// # use time_macros::datetime;
1354 /// assert_eq!(
1355 /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_microsecond(7_008),
1356 /// Ok(datetime!(2022-02-18 01:02:03.007_008 +01))
1357 /// );
1358 /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond
1359 /// ```
1360 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1361 #[inline]
1362 pub const fn replace_microsecond(
1363 self,
1364 microsecond: u32,
1365 ) -> Result<Self, error::ComponentRange> {
1366 Ok(
1367 const_try!(self.date_time().replace_microsecond(microsecond))
1368 .assume_offset(self.offset()),
1369 )
1370 }
1371
1372 /// Replace the nanoseconds within the second.
1373 ///
1374 /// ```rust
1375 /// # use time_macros::datetime;
1376 /// assert_eq!(
1377 /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_nanosecond(7_008_009),
1378 /// Ok(datetime!(2022-02-18 01:02:03.007_008_009 +01))
1379 /// );
1380 /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond
1381 /// ```
1382 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1383 #[inline]
1384 pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
1385 Ok(
1386 const_try!(self.date_time().replace_nanosecond(nanosecond))
1387 .assume_offset(self.offset()),
1388 )
1389 }
1390}
1391
1392#[cfg(feature = "formatting")]
1393impl OffsetDateTime {
1394 /// Format the `OffsetDateTime` using the provided [format
1395 /// description](crate::format_description).
1396 #[inline]
1397 pub fn format_into(
1398 self,
1399 output: &mut (impl io::Write + ?Sized),
1400 format: &(impl Formattable + ?Sized),
1401 ) -> Result<usize, error::Format> {
1402 format.format_into(
1403 output,
1404 Some(self.date()),
1405 Some(self.time()),
1406 Some(self.offset()),
1407 )
1408 }
1409
1410 /// Format the `OffsetDateTime` using the provided [format
1411 /// description](crate::format_description).
1412 ///
1413 /// ```rust
1414 /// # use time::format_description;
1415 /// # use time_macros::datetime;
1416 /// let format = format_description::parse(
1417 /// "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \
1418 /// sign:mandatory]:[offset_minute]:[offset_second]",
1419 /// )?;
1420 /// assert_eq!(
1421 /// datetime!(2020-01-02 03:04:05 +06:07:08).format(&format)?,
1422 /// "2020-01-02 03:04:05 +06:07:08"
1423 /// );
1424 /// # Ok::<_, time::Error>(())
1425 /// ```
1426 #[inline]
1427 pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
1428 format.format(Some(self.date()), Some(self.time()), Some(self.offset()))
1429 }
1430}
1431
1432#[cfg(feature = "parsing")]
1433impl OffsetDateTime {
1434 /// Parse an `OffsetDateTime` from the input using the provided [format
1435 /// description](crate::format_description).
1436 ///
1437 /// ```rust
1438 /// # use time::OffsetDateTime;
1439 /// # use time_macros::{datetime, format_description};
1440 /// let format = format_description!(
1441 /// "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \
1442 /// sign:mandatory]:[offset_minute]:[offset_second]"
1443 /// );
1444 /// assert_eq!(
1445 /// OffsetDateTime::parse("2020-01-02 03:04:05 +06:07:08", &format)?,
1446 /// datetime!(2020-01-02 03:04:05 +06:07:08)
1447 /// );
1448 /// # Ok::<_, time::Error>(())
1449 /// ```
1450 #[inline]
1451 pub fn parse(
1452 input: &str,
1453 description: &(impl Parsable + ?Sized),
1454 ) -> Result<Self, error::Parse> {
1455 description.parse_offset_date_time(input.as_bytes())
1456 }
1457
1458 /// A helper method to check if the `OffsetDateTime` is a valid representation of a leap second.
1459 /// Leap seconds, when parsed, are represented as the preceding nanosecond. However, leap
1460 /// seconds can only occur as the last second of a month UTC.
1461 #[cfg(feature = "parsing")]
1462 #[inline]
1463 pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool {
1464 // This comparison doesn't need to be adjusted for the stored offset, so check it first for
1465 // speed.
1466 if self.nanosecond() != 999_999_999 {
1467 return false;
1468 }
1469
1470 let (year, ordinal, time) = self.to_utc_raw();
1471 let Ok(date) = Date::from_ordinal_date(year, ordinal) else {
1472 return false;
1473 };
1474
1475 time.hour() == 23
1476 && time.minute() == 59
1477 && time.second() == 59
1478 && date.day() == date.month().length(year)
1479 }
1480}
1481
1482impl SmartDisplay for OffsetDateTime {
1483 type Metadata = ();
1484
1485 #[inline]
1486 fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
1487 let width =
1488 smart_display::padded_width_of!(self.date(), " ", self.time(), " ", self.offset());
1489 Metadata::new(width, self, ())
1490 }
1491
1492 #[inline]
1493 fn fmt_with_metadata(
1494 &self,
1495 f: &mut fmt::Formatter<'_>,
1496 metadata: Metadata<Self>,
1497 ) -> fmt::Result {
1498 f.pad_with_width(
1499 metadata.unpadded_width(),
1500 format_args!("{} {} {}", self.date(), self.time(), self.offset()),
1501 )
1502 }
1503}
1504
1505impl fmt::Display for OffsetDateTime {
1506 #[inline]
1507 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1508 SmartDisplay::fmt(self, f)
1509 }
1510}
1511
1512impl fmt::Debug for OffsetDateTime {
1513 #[inline]
1514 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1515 fmt::Display::fmt(self, f)
1516 }
1517}
1518
1519impl Add<Duration> for OffsetDateTime {
1520 type Output = Self;
1521
1522 /// # Panics
1523 ///
1524 /// This may panic if an overflow occurs.
1525 #[inline]
1526 #[track_caller]
1527 fn add(self, duration: Duration) -> Self::Output {
1528 self.checked_add(duration)
1529 .expect("resulting value is out of range")
1530 }
1531}
1532
1533impl Add<StdDuration> for OffsetDateTime {
1534 type Output = Self;
1535
1536 /// # Panics
1537 ///
1538 /// This may panic if an overflow occurs.
1539 #[inline]
1540 #[track_caller]
1541 fn add(self, duration: StdDuration) -> Self::Output {
1542 let (is_next_day, time) = self.time().adjusting_add_std(duration);
1543
1544 Self::new_in_offset(
1545 if is_next_day {
1546 (self.date() + duration)
1547 .next_day()
1548 .expect("resulting value is out of range")
1549 } else {
1550 self.date() + duration
1551 },
1552 time,
1553 self.offset,
1554 )
1555 }
1556}
1557
1558impl AddAssign<Duration> for OffsetDateTime {
1559 /// # Panics
1560 ///
1561 /// This may panic if an overflow occurs.
1562 #[inline]
1563 #[track_caller]
1564 fn add_assign(&mut self, rhs: Duration) {
1565 *self = *self + rhs;
1566 }
1567}
1568
1569impl AddAssign<StdDuration> for OffsetDateTime {
1570 /// # Panics
1571 ///
1572 /// This may panic if an overflow occurs.
1573 #[inline]
1574 #[track_caller]
1575 fn add_assign(&mut self, rhs: StdDuration) {
1576 *self = *self + rhs;
1577 }
1578}
1579
1580impl Sub<Duration> for OffsetDateTime {
1581 type Output = Self;
1582
1583 /// # Panics
1584 ///
1585 /// This may panic if an overflow occurs.
1586 #[inline]
1587 #[track_caller]
1588 fn sub(self, rhs: Duration) -> Self::Output {
1589 self.checked_sub(rhs)
1590 .expect("resulting value is out of range")
1591 }
1592}
1593
1594impl Sub<StdDuration> for OffsetDateTime {
1595 type Output = Self;
1596
1597 /// # Panics
1598 ///
1599 /// This may panic if an overflow occurs.
1600 #[inline]
1601 #[track_caller]
1602 fn sub(self, duration: StdDuration) -> Self::Output {
1603 let (is_previous_day, time) = self.time().adjusting_sub_std(duration);
1604
1605 Self::new_in_offset(
1606 if is_previous_day {
1607 (self.date() - duration)
1608 .previous_day()
1609 .expect("resulting value is out of range")
1610 } else {
1611 self.date() - duration
1612 },
1613 time,
1614 self.offset,
1615 )
1616 }
1617}
1618
1619impl SubAssign<Duration> for OffsetDateTime {
1620 /// # Panics
1621 ///
1622 /// This may panic if an overflow occurs.
1623 #[inline]
1624 #[track_caller]
1625 fn sub_assign(&mut self, rhs: Duration) {
1626 *self = *self - rhs;
1627 }
1628}
1629
1630impl SubAssign<StdDuration> for OffsetDateTime {
1631 /// # Panics
1632 ///
1633 /// This may panic if an overflow occurs.
1634 #[inline]
1635 #[track_caller]
1636 fn sub_assign(&mut self, rhs: StdDuration) {
1637 *self = *self - rhs;
1638 }
1639}
1640
1641impl Sub for OffsetDateTime {
1642 type Output = Duration;
1643
1644 /// # Panics
1645 ///
1646 /// This may panic if an overflow occurs.
1647 #[inline]
1648 #[track_caller]
1649 fn sub(self, rhs: Self) -> Self::Output {
1650 let base = self.date_time() - rhs.date_time();
1651 let adjustment = Duration::seconds(
1652 (self.offset.whole_seconds() - rhs.offset.whole_seconds()).extend::<i64>(),
1653 );
1654 base - adjustment
1655 }
1656}