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