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