1#[cfg(feature = "formatting")]
4use alloc::string::String;
5use core::cmp::Ordering;
6use core::fmt;
7use core::hash::Hash;
8use core::ops::{Add, AddAssign, Sub, SubAssign};
9use core::time::Duration as StdDuration;
10#[cfg(feature = "formatting")]
11use std::io;
12
13use deranged::RangedI64;
14use num_conv::prelude::*;
15use powerfmt::ext::FormatterExt as _;
16use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
17use time_core::convert::*;
18
19use crate::date::{MAX_YEAR, MIN_YEAR};
20#[cfg(feature = "formatting")]
21use crate::formatting::Formattable;
22use crate::internal_macros::{
23 carry, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt,
24};
25#[cfg(feature = "parsing")]
26use crate::parsing::Parsable;
27use crate::{
28 error, util, Date, Duration, Month, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
29};
30
31const UNIX_EPOCH_JULIAN_DAY: i32 = OffsetDateTime::UNIX_EPOCH.to_julian_day();
33
34#[derive(#[automatically_derived]
impl ::core::clone::Clone for OffsetDateTime {
#[inline]
fn clone(&self) -> OffsetDateTime {
let _: ::core::clone::AssertParamIsClone<PrimitiveDateTime>;
let _: ::core::clone::AssertParamIsClone<UtcOffset>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for OffsetDateTime { }Copy, #[automatically_derived]
impl ::core::cmp::Eq for OffsetDateTime {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<PrimitiveDateTime>;
let _: ::core::cmp::AssertParamIsEq<UtcOffset>;
}
}Eq)]
36pub struct OffsetDateTime {
37 local_date_time: PrimitiveDateTime,
38 offset: UtcOffset,
39}
40
41impl PartialEq for OffsetDateTime {
42 #[inline]
43 fn eq(&self, other: &Self) -> bool {
44 raw_to_bits((self.year(), self.ordinal(), self.time()))
45 == raw_to_bits(other.to_offset_raw(self.offset()))
46 }
47}
48
49impl PartialOrd for OffsetDateTime {
50 #[inline]
51 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
52 Some(self.cmp(other))
53 }
54}
55
56impl Ord for OffsetDateTime {
57 #[inline]
58 fn cmp(&self, other: &Self) -> Ordering {
59 raw_to_bits((self.year(), self.ordinal(), self.time()))
60 .cmp(&raw_to_bits(other.to_offset_raw(self.offset())))
61 }
62}
63
64impl Hash for OffsetDateTime {
65 #[inline]
66 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
67 raw_to_bits(self.to_utc_raw()).hash(state);
68 }
69}
70
71#[inline]
75const fn raw_to_bits((year, ordinal, time): (i32, u16, Time)) -> i128 {
76 ((year as i128) << 74) | ((ordinal as i128) << 64) | (time.as_u64() as i128)
77}
78
79impl OffsetDateTime {
80 pub const UNIX_EPOCH: Self =
88 Self::new_in_offset(Date::UNIX_EPOCH, Time::MIDNIGHT, UtcOffset::UTC);
89
90 #[cfg(feature = "std")]
99 #[inline]
100 pub fn now_utc() -> Self {
101 #[cfg(all(
102 target_family = "wasm",
103 not(any(target_os = "emscripten", target_os = "wasi")),
104 feature = "wasm-bindgen"
105 ))]
106 {
107 js_sys::Date::new_0().into()
108 }
109
110 #[cfg(not(all(
111 target_family = "wasm",
112 not(any(target_os = "emscripten", target_os = "wasi")),
113 feature = "wasm-bindgen"
114 )))]
115 std::time::SystemTime::now().into()
116 }
117
118 #[cfg(feature = "local-offset")]
128 #[inline]
129 pub fn now_local() -> Result<Self, error::IndeterminateOffset> {
130 let t = Self::now_utc();
131 Ok(t.to_offset(UtcOffset::local_offset_at(t)?))
132 }
133
134 #[inline]
148 pub const fn new_in_offset(date: Date, time: Time, offset: UtcOffset) -> Self {
149 Self {
150 local_date_time: date.with_time(time),
151 offset,
152 }
153 }
154
155 #[inline]
168 pub const fn new_utc(date: Date, time: Time) -> Self {
169 PrimitiveDateTime::new(date, time).assume_utc()
170 }
171
172 #[inline]
198 #[track_caller]
199 pub const fn to_offset(self, offset: UtcOffset) -> Self {
200 match self.checked_to_offset(offset) {
Some(value) => value,
None => crate::expect_failed("local datetime out of valid range"),
}expect_opt!(
201 self.checked_to_offset(offset),
202 "local datetime out of valid range"
203 )
204 }
205
206 #[inline]
227 pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<Self> {
228 if self.offset.as_u32() == offset.as_u32() {
229 return Some(self);
230 }
231
232 let (year, ordinal, time) = self.to_offset_raw(offset);
233
234 if year > MAX_YEAR || year < MIN_YEAR {
235 return None;
236 }
237
238 Some(Self::new_in_offset(
239 unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) },
241 time,
242 offset,
243 ))
244 }
245
246 #[inline]
263 #[track_caller]
264 pub const fn to_utc(self) -> UtcDateTime {
265 match self.checked_to_utc() {
Some(value) => value,
None => crate::expect_failed("local datetime out of valid range"),
}expect_opt!(self.checked_to_utc(), "local datetime out of valid range")
266 }
267
268 #[cfg_attr(
282 feature = "large-dates",
283 doc = " datetime!(+999999-12-31 23:59:59 -1).checked_to_utc(),"
284 )]
285 #[cfg_attr(
286 not(feature = "large-dates"),
287 doc = " datetime!(9999-12-31 23:59:59 -1).checked_to_utc(),"
288 )]
289 #[inline]
293 pub const fn checked_to_utc(self) -> Option<UtcDateTime> {
294 if self.offset.is_utc() {
295 return Some(self.local_date_time.as_utc());
296 }
297
298 let (year, ordinal, time) = self.to_utc_raw();
299
300 if year > MAX_YEAR || year < MIN_YEAR {
301 return None;
302 }
303
304 Some(UtcDateTime::new(
305 unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) },
307 time,
308 ))
309 }
310
311 #[inline]
314 pub(crate) const fn to_utc_raw(self) -> (i32, u16, Time) {
315 let from = self.offset;
316
317 if from.is_utc() {
319 return (self.year(), self.ordinal(), self.time());
320 }
321
322 let (second, carry) = match (self.second() as i8 - from.seconds_past_minute(), 0,
Second::per_t(Minute)) {
(value, min, max) => {
if crate::hint::likely(value >= min) {
if crate::hint::likely(value < max) {
(value, 0)
} else { (value - (max - min), 1) }
} else { (value + (max - min), -1) }
}
}carry!(@most_once
323 self.second() as i8 - from.seconds_past_minute(),
324 0..Second::per_t(Minute)
325 );
326 let (minute, carry) = match (self.minute() as i8 - from.minutes_past_hour() + carry, 0,
Minute::per_t(Hour)) {
(value, min, max) => {
if crate::hint::likely(value >= min) {
if crate::hint::likely(value < max) {
(value, 0)
} else { (value - (max - min), 1) }
} else { (value + (max - min), -1) }
}
}carry!(@most_once
327 self.minute() as i8 - from.minutes_past_hour() + carry,
328 0..Minute::per_t(Hour)
329 );
330 let (hour, carry) = match (self.hour() as i8 - from.whole_hours() + carry, 0, Hour::per_t(Day)) {
(value, min, max) => {
if crate::hint::likely(value >= min) {
if crate::hint::likely(value < max) {
(value, 0)
} else if value < 2 * max - min {
(value - (max - min), 1)
} else { (value - 2 * (max - min), 2) }
} else {
if value >= min - max {
(value + (max - min), -1)
} else { (value + 2 * (max - min), -2) }
}
}
}carry!(@most_twice
331 self.hour() as i8 - from.whole_hours() + carry,
332 0..Hour::per_t(Day)
333 );
334 let (mut year, ordinal) = self.to_ordinal_date();
335 let mut ordinal = ordinal as i16 + carry;
336 let days_in_year = crate::util::days_in_year(year) as i16;
#[allow(unused_assignments)]
if crate::hint::unlikely(ordinal > days_in_year) {
ordinal -= days_in_year;
year += 1;
} else if crate::hint::unlikely(ordinal < 1) {
year -= 1;
ordinal += crate::util::days_in_year(year) as i16;
};cascade!(ordinal => year);
337
338 if true {
if !(ordinal > 0) {
::core::panicking::panic("assertion failed: ordinal > 0")
};
};debug_assert!(ordinal > 0);
339 if true {
if !(ordinal <= util::days_in_year(year) as i16) {
::core::panicking::panic("assertion failed: ordinal <= util::days_in_year(year) as i16")
};
};debug_assert!(ordinal <= util::days_in_year(year) as i16);
340
341 (
342 year,
343 ordinal as u16,
344 unsafe {
346 Time::__from_hms_nanos_unchecked(
347 hour as u8,
348 minute as u8,
349 second as u8,
350 self.nanosecond(),
351 )
352 },
353 )
354 }
355
356 #[inline]
359 pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) {
360 let from = self.offset;
361 let to = offset;
362
363 if from.as_u32() == to.as_u32() {
365 return (self.year(), self.ordinal(), self.time());
366 }
367
368 let (second, carry) = match (self.second() as i16 - from.seconds_past_minute() as i16 +
to.seconds_past_minute() as i16, 0, Second::per_t(Minute)) {
(value, min, max) => {
if crate::hint::likely(value >= min) {
if crate::hint::likely(value < max) {
(value, 0)
} else if value < 2 * max - min {
(value - (max - min), 1)
} else { (value - 2 * (max - min), 2) }
} else {
if value >= min - max {
(value + (max - min), -1)
} else { (value + 2 * (max - min), -2) }
}
}
}carry!(@most_twice
369 self.second() as i16 - from.seconds_past_minute() as i16
370 + to.seconds_past_minute() as i16,
371 0..Second::per_t(Minute)
372 );
373 let (minute, carry) = match (self.minute() as i16 - from.minutes_past_hour() as i16 +
to.minutes_past_hour() as i16 + carry, 0, Minute::per_t(Hour))
{
(value, min, max) => {
if crate::hint::likely(value >= min) {
if crate::hint::likely(value < max) {
(value, 0)
} else if value < 2 * max - min {
(value - (max - min), 1)
} else { (value - 2 * (max - min), 2) }
} else {
if value >= min - max {
(value + (max - min), -1)
} else { (value + 2 * (max - min), -2) }
}
}
}carry!(@most_twice
374 self.minute() as i16 - from.minutes_past_hour() as i16
375 + to.minutes_past_hour() as i16
376 + carry,
377 0..Minute::per_t(Hour)
378 );
379 let (hour, carry) = match (self.hour() as i8 - from.whole_hours() + to.whole_hours() + carry, 0,
Hour::per_t(Day)) {
(value, min, max) => {
if crate::hint::likely(value >= min) {
if crate::hint::likely(value < max) {
(value, 0)
} else if value < 2 * max - min {
(value - (max - min), 1)
} else if value < 3 * max - 2 * min {
(value - 2 * (max - min), 2)
} else { (value - 3 * (max - min), 3) }
} else {
if value >= min - max {
(value + (max - min), -1)
} else if value >= 2 * (min - max) {
(value + 2 * (max - min), -2)
} else { (value + 3 * (max - min), -3) }
}
}
}carry!(@most_thrice
380 self.hour() as i8 - from.whole_hours() + to.whole_hours() + carry,
381 0..Hour::per_t(Day)
382 );
383 let (mut year, ordinal) = self.to_ordinal_date();
384 let mut ordinal = ordinal as i16 + carry;
385 let days_in_year = crate::util::days_in_year(year) as i16;
#[allow(unused_assignments)]
if crate::hint::unlikely(ordinal > days_in_year) {
ordinal -= days_in_year;
year += 1;
} else if crate::hint::unlikely(ordinal < 1) {
year -= 1;
ordinal += crate::util::days_in_year(year) as i16;
};cascade!(ordinal => year);
386
387 if true {
if !(ordinal > 0) {
::core::panicking::panic("assertion failed: ordinal > 0")
};
};debug_assert!(ordinal > 0);
388 if true {
if !(ordinal <= util::days_in_year(year) as i16) {
::core::panicking::panic("assertion failed: ordinal <= util::days_in_year(year) as i16")
};
};debug_assert!(ordinal <= util::days_in_year(year) as i16);
389
390 (
391 year,
392 ordinal as u16,
393 unsafe {
395 Time::__from_hms_nanos_unchecked(
396 hour as u8,
397 minute as u8,
398 second as u8,
399 self.nanosecond(),
400 )
401 },
402 )
403 }
404
405 #[inline]
434 pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange> {
435 type Timestamp = RangedI64<
436 {
437 OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
438 .unix_timestamp()
439 },
440 {
441 OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC).unix_timestamp()
442 },
443 >;
444 match <Timestamp>::new(timestamp) {
Some(val) => val,
None => {
crate::hint::cold_path();
#[allow(trivial_numeric_casts)]
return Err(crate::error::ComponentRange {
name: "timestamp",
minimum: <Timestamp>::MIN.get() as i64,
maximum: <Timestamp>::MAX.get() as i64,
value: timestamp as i64,
conditional_message: None,
});
}
};ensure_ranged!(Timestamp: timestamp);
445
446 let date = unsafe {
449 Date::from_julian_day_unchecked(
450 UNIX_EPOCH_JULIAN_DAY + match (timestamp, Second::per_t::<i64>(Day)) {
(this, rhs) => {
let d = this / rhs;
let r = this % rhs;
let correction = (this ^ rhs) >> (crate::size_of_val(&this) * 8 - 1);
if r != 0 { d + correction } else { d }
}
}div_floor!(timestamp, Second::per_t::<i64>(Day)) as i32,
451 )
452 };
453
454 let seconds_within_day = timestamp.rem_euclid(Second::per_t(Day));
455 let time = unsafe {
457 Time::__from_hms_nanos_unchecked(
458 (seconds_within_day / Second::per_t::<i64>(Hour)) as u8,
459 ((seconds_within_day % Second::per_t::<i64>(Hour)) / Minute::per_t::<i64>(Hour))
460 as u8,
461 (seconds_within_day % Second::per_t::<i64>(Minute)) as u8,
462 0,
463 )
464 };
465
466 Ok(Self::new_in_offset(date, time, UtcOffset::UTC))
467 }
468
469 #[inline]
485 pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange> {
486 let datetime = match Self::from_unix_timestamp(match (timestamp,
Nanosecond::per_t::<i128>(Second)) {
(this, rhs) => {
let d = this / rhs;
let r = this % rhs;
let correction =
(this ^ rhs) >> (crate::size_of_val(&this) * 8 - 1);
if r != 0 { d + correction } else { d }
}
} as i64) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(Self::from_unix_timestamp(div_floor!(
487 timestamp,
488 Nanosecond::per_t::<i128>(Second)
489 ) as i64));
490
491 Ok(Self::new_in_offset(
492 datetime.date(),
493 unsafe {
495 Time::__from_hms_nanos_unchecked(
496 datetime.hour(),
497 datetime.minute(),
498 datetime.second(),
499 timestamp.rem_euclid(Nanosecond::per_t(Second)) as u32,
500 )
501 },
502 UtcOffset::UTC,
503 ))
504 }
505
506 #[inline]
514 pub const fn offset(self) -> UtcOffset {
515 self.offset
516 }
517
518 #[inline]
526 pub const fn unix_timestamp(self) -> i64 {
527 let days = (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64)
528 * Second::per_t::<i64>(Day);
529 let hours = self.hour() as i64 * Second::per_t::<i64>(Hour);
530 let minutes = self.minute() as i64 * Second::per_t::<i64>(Minute);
531 let seconds = self.second() as i64;
532 let offset_seconds = self.offset.whole_seconds() as i64;
533 days + hours + minutes + seconds - offset_seconds
534 }
535
536 #[inline]
547 pub const fn unix_timestamp_nanos(self) -> i128 {
548 self.unix_timestamp() as i128 * Nanosecond::per_t::<i128>(Second)
549 + self.nanosecond() as i128
550 }
551
552 #[inline]
554 pub(crate) const fn date_time(self) -> PrimitiveDateTime {
555 self.local_date_time
556 }
557
558 #[inline]
571 pub const fn date(self) -> Date {
572 self.date_time().date()
573 }
574
575 #[inline]
588 pub const fn time(self) -> Time {
589 self.date_time().time()
590 }
591
592 #[inline]
606 pub const fn year(self) -> i32 {
607 self.date().year()
608 }
609
610 #[inline]
624 pub const fn month(self) -> Month {
625 self.date().month()
626 }
627
628 #[inline]
643 pub const fn day(self) -> u8 {
644 self.date().day()
645 }
646
647 #[inline]
662 pub const fn ordinal(self) -> u16 {
663 self.date().ordinal()
664 }
665
666 #[inline]
678 pub const fn iso_week(self) -> u8 {
679 self.date().iso_week()
680 }
681
682 #[inline]
694 pub const fn sunday_based_week(self) -> u8 {
695 self.date().sunday_based_week()
696 }
697
698 #[inline]
710 pub const fn monday_based_week(self) -> u8 {
711 self.date().monday_based_week()
712 }
713
714 #[inline]
725 pub const fn to_calendar_date(self) -> (i32, Month, u8) {
726 self.date().to_calendar_date()
727 }
728
729 #[inline]
739 pub const fn to_ordinal_date(self) -> (i32, u16) {
740 self.date().to_ordinal_date()
741 }
742
743 #[inline]
770 pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
771 self.date().to_iso_week_date()
772 }
773
774 #[inline]
784 pub const fn weekday(self) -> Weekday {
785 self.date().weekday()
786 }
787
788 #[inline]
801 pub const fn to_julian_day(self) -> i32 {
802 self.date().to_julian_day()
803 }
804
805 #[inline]
813 pub const fn to_hms(self) -> (u8, u8, u8) {
814 self.time().as_hms()
815 }
816
817 #[inline]
831 pub const fn to_hms_milli(self) -> (u8, u8, u8, u16) {
832 self.time().as_hms_milli()
833 }
834
835 #[inline]
849 pub const fn to_hms_micro(self) -> (u8, u8, u8, u32) {
850 self.time().as_hms_micro()
851 }
852
853 #[inline]
867 pub const fn to_hms_nano(self) -> (u8, u8, u8, u32) {
868 self.time().as_hms_nano()
869 }
870
871 #[inline]
886 pub const fn hour(self) -> u8 {
887 self.time().hour()
888 }
889
890 #[inline]
905 pub const fn minute(self) -> u8 {
906 self.time().minute()
907 }
908
909 #[inline]
924 pub const fn second(self) -> u8 {
925 self.time().second()
926 }
927
928 #[inline]
941 pub const fn millisecond(self) -> u16 {
942 self.time().millisecond()
943 }
944
945 #[inline]
958 pub const fn microsecond(self) -> u32 {
959 self.time().microsecond()
960 }
961
962 #[inline]
975 pub const fn nanosecond(self) -> u32 {
976 self.time().nanosecond()
977 }
978
979 #[inline]
996 pub const fn checked_add(self, duration: Duration) -> Option<Self> {
997 Some(match self.date_time().checked_add(duration) {
Some(value) => value,
None => return None,
}const_try_opt!(self.date_time().checked_add(duration)).assume_offset(self.offset()))
998 }
999
1000 #[inline]
1017 pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
1018 Some(match self.date_time().checked_sub(duration) {
Some(value) => value,
None => return None,
}const_try_opt!(self.date_time().checked_sub(duration)).assume_offset(self.offset()))
1019 }
1020
1021 #[cfg_attr(
1028 feature = "large-dates",
1029 doc = " datetime!(-999999-01-01 0:00 +10).saturating_add((-2).days()),"
1030 )]
1031 #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")]
1032 #[cfg_attr(
1033 not(feature = "large-dates"),
1034 doc = " datetime!(-9999-01-01 0:00 +10).saturating_add((-2).days()),"
1035 )]
1036 #[cfg_attr(
1037 not(feature = "large-dates"),
1038 doc = " datetime!(-9999-01-01 0:00 +10)"
1039 )]
1040 #[cfg_attr(
1044 feature = "large-dates",
1045 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days()),"
1046 )]
1047 #[cfg_attr(
1048 feature = "large-dates",
1049 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)"
1050 )]
1051 #[cfg_attr(
1052 not(feature = "large-dates"),
1053 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days()),"
1054 )]
1055 #[cfg_attr(
1056 not(feature = "large-dates"),
1057 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
1058 )]
1059 #[inline]
1067 pub const fn saturating_add(self, duration: Duration) -> Self {
1068 if let Some(datetime) = self.checked_add(duration) {
1069 datetime
1070 } else if duration.is_negative() {
1071 PrimitiveDateTime::MIN.assume_offset(self.offset())
1072 } else {
1073 PrimitiveDateTime::MAX.assume_offset(self.offset())
1074 }
1075 }
1076
1077 #[cfg_attr(
1084 feature = "large-dates",
1085 doc = " datetime!(-999999-01-01 0:00 +10).saturating_sub(2.days()),"
1086 )]
1087 #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")]
1088 #[cfg_attr(
1089 not(feature = "large-dates"),
1090 doc = " datetime!(-9999-01-01 0:00 +10).saturating_sub(2.days()),"
1091 )]
1092 #[cfg_attr(
1093 not(feature = "large-dates"),
1094 doc = " datetime!(-9999-01-01 0:00 +10)"
1095 )]
1096 #[cfg_attr(
1100 feature = "large-dates",
1101 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days()),"
1102 )]
1103 #[cfg_attr(
1104 feature = "large-dates",
1105 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)"
1106 )]
1107 #[cfg_attr(
1108 not(feature = "large-dates"),
1109 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days()),"
1110 )]
1111 #[cfg_attr(
1112 not(feature = "large-dates"),
1113 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
1114 )]
1115 #[inline]
1123 pub const fn saturating_sub(self, duration: Duration) -> Self {
1124 if let Some(datetime) = self.checked_sub(duration) {
1125 datetime
1126 } else if duration.is_negative() {
1127 PrimitiveDateTime::MAX.assume_offset(self.offset())
1128 } else {
1129 PrimitiveDateTime::MIN.assume_offset(self.offset())
1130 }
1131 }
1132}
1133
1134impl OffsetDateTime {
1136 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1155 #[inline]
1156 pub const fn replace_time(self, time: Time) -> Self {
1157 Self::new_in_offset(self.date(), time, self.offset())
1158 }
1159
1160 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1175 #[inline]
1176 pub const fn replace_date(self, date: Date) -> Self {
1177 Self::new_in_offset(date, self.time(), self.offset())
1178 }
1179
1180 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1195 #[inline]
1196 pub const fn replace_date_time(self, date_time: PrimitiveDateTime) -> Self {
1197 date_time.assume_offset(self.offset())
1198 }
1199
1200 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1210 #[inline]
1211 pub const fn replace_offset(self, offset: UtcOffset) -> Self {
1212 self.date_time().assume_offset(offset)
1213 }
1214
1215 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1227 #[inline]
1228 pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
1229 Ok(match self.date_time().replace_year(year) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_year(year)).assume_offset(self.offset()))
1230 }
1231
1232 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1244 #[inline]
1245 pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
1246 Ok(match self.date_time().replace_month(month) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_month(month)).assume_offset(self.offset()))
1247 }
1248
1249 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1261 #[inline]
1262 pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
1263 Ok(match self.date_time().replace_day(day) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_day(day)).assume_offset(self.offset()))
1264 }
1265
1266 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1275 #[inline]
1276 pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
1277 Ok(match self.date_time().replace_ordinal(ordinal) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_ordinal(ordinal)).assume_offset(self.offset()))
1278 }
1279
1280 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1291 #[inline]
1292 pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
1293 Ok(match self.date_time().replace_hour(hour) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_hour(hour)).assume_offset(self.offset()))
1294 }
1295
1296 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1307 #[inline]
1308 pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
1309 Ok(match self.date_time().replace_minute(minute) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_minute(minute)).assume_offset(self.offset()))
1310 }
1311
1312 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1323 #[inline]
1324 pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
1325 Ok(match self.date_time().replace_second(second) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_second(second)).assume_offset(self.offset()))
1326 }
1327
1328 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1339 #[inline]
1340 pub const fn replace_millisecond(
1341 self,
1342 millisecond: u16,
1343 ) -> Result<Self, error::ComponentRange> {
1344 Ok(
1345 match self.date_time().replace_millisecond(millisecond) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_millisecond(millisecond))
1346 .assume_offset(self.offset()),
1347 )
1348 }
1349
1350 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1361 #[inline]
1362 pub const fn replace_microsecond(
1363 self,
1364 microsecond: u32,
1365 ) -> Result<Self, error::ComponentRange> {
1366 Ok(
1367 match self.date_time().replace_microsecond(microsecond) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_microsecond(microsecond))
1368 .assume_offset(self.offset()),
1369 )
1370 }
1371
1372 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1383 #[inline]
1384 pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
1385 Ok(
1386 match self.date_time().replace_nanosecond(nanosecond) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_nanosecond(nanosecond))
1387 .assume_offset(self.offset()),
1388 )
1389 }
1390}
1391
1392#[cfg(feature = "formatting")]
1393impl OffsetDateTime {
1394 #[inline]
1397 pub fn format_into(
1398 self,
1399 output: &mut (impl io::Write + ?Sized),
1400 format: &(impl Formattable + ?Sized),
1401 ) -> Result<usize, error::Format> {
1402 format.format_into(
1403 output,
1404 Some(self.date()),
1405 Some(self.time()),
1406 Some(self.offset()),
1407 )
1408 }
1409
1410 #[inline]
1427 pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
1428 format.format(Some(self.date()), Some(self.time()), Some(self.offset()))
1429 }
1430}
1431
1432#[cfg(feature = "parsing")]
1433impl OffsetDateTime {
1434 #[inline]
1451 pub fn parse(
1452 input: &str,
1453 description: &(impl Parsable + ?Sized),
1454 ) -> Result<Self, error::Parse> {
1455 description.parse_offset_date_time(input.as_bytes())
1456 }
1457
1458 #[cfg(feature = "parsing")]
1462 #[inline]
1463 pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool {
1464 if self.nanosecond() != 999_999_999 {
1467 return false;
1468 }
1469
1470 let (year, ordinal, time) = self.to_utc_raw();
1471 let Ok(date) = Date::from_ordinal_date(year, ordinal) else {
1472 return false;
1473 };
1474
1475 time.hour() == 23
1476 && time.minute() == 59
1477 && time.second() == 59
1478 && date.day() == date.month().length(year)
1479 }
1480}
1481
1482impl SmartDisplay for OffsetDateTime {
1483 type Metadata = ();
1484
1485 #[inline]
1486 fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
1487 let width =
1488 0 +
::powerfmt::smart_display::Metadata::padded_width_of(&self.date(),
::powerfmt::smart_display::FormatterOptions::default()) +
::powerfmt::smart_display::Metadata::padded_width_of(&" ",
::powerfmt::smart_display::FormatterOptions::default()) +
::powerfmt::smart_display::Metadata::padded_width_of(&self.time(),
::powerfmt::smart_display::FormatterOptions::default()) +
::powerfmt::smart_display::Metadata::padded_width_of(&" ",
::powerfmt::smart_display::FormatterOptions::default()) +
::powerfmt::smart_display::Metadata::padded_width_of(&self.offset(),
::powerfmt::smart_display::FormatterOptions::default())smart_display::padded_width_of!(self.date(), " ", self.time(), " ", self.offset());
1489 Metadata::new(width, self, ())
1490 }
1491
1492 #[inline]
1493 fn fmt_with_metadata(
1494 &self,
1495 f: &mut fmt::Formatter<'_>,
1496 metadata: Metadata<Self>,
1497 ) -> fmt::Result {
1498 f.pad_with_width(
1499 metadata.unpadded_width(),
1500 format_args!("{0} {1} {2}", self.date(), self.time(), self.offset())format_args!("{} {} {}", self.date(), self.time(), self.offset()),
1501 )
1502 }
1503}
1504
1505impl fmt::Display for OffsetDateTime {
1506 #[inline]
1507 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1508 SmartDisplay::fmt(self, f)
1509 }
1510}
1511
1512impl fmt::Debug for OffsetDateTime {
1513 #[inline]
1514 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1515 fmt::Display::fmt(self, f)
1516 }
1517}
1518
1519impl Add<Duration> for OffsetDateTime {
1520 type Output = Self;
1521
1522 #[inline]
1526 #[track_caller]
1527 fn add(self, duration: Duration) -> Self::Output {
1528 self.checked_add(duration)
1529 .expect("resulting value is out of range")
1530 }
1531}
1532
1533impl Add<StdDuration> for OffsetDateTime {
1534 type Output = Self;
1535
1536 #[inline]
1540 #[track_caller]
1541 fn add(self, duration: StdDuration) -> Self::Output {
1542 let (is_next_day, time) = self.time().adjusting_add_std(duration);
1543
1544 Self::new_in_offset(
1545 if is_next_day {
1546 (self.date() + duration)
1547 .next_day()
1548 .expect("resulting value is out of range")
1549 } else {
1550 self.date() + duration
1551 },
1552 time,
1553 self.offset,
1554 )
1555 }
1556}
1557
1558impl AddAssign<Duration> for OffsetDateTime {
1559 #[inline]
1563 #[track_caller]
1564 fn add_assign(&mut self, rhs: Duration) {
1565 *self = *self + rhs;
1566 }
1567}
1568
1569impl AddAssign<StdDuration> for OffsetDateTime {
1570 #[inline]
1574 #[track_caller]
1575 fn add_assign(&mut self, rhs: StdDuration) {
1576 *self = *self + rhs;
1577 }
1578}
1579
1580impl Sub<Duration> for OffsetDateTime {
1581 type Output = Self;
1582
1583 #[inline]
1587 #[track_caller]
1588 fn sub(self, rhs: Duration) -> Self::Output {
1589 self.checked_sub(rhs)
1590 .expect("resulting value is out of range")
1591 }
1592}
1593
1594impl Sub<StdDuration> for OffsetDateTime {
1595 type Output = Self;
1596
1597 #[inline]
1601 #[track_caller]
1602 fn sub(self, duration: StdDuration) -> Self::Output {
1603 let (is_previous_day, time) = self.time().adjusting_sub_std(duration);
1604
1605 Self::new_in_offset(
1606 if is_previous_day {
1607 (self.date() - duration)
1608 .previous_day()
1609 .expect("resulting value is out of range")
1610 } else {
1611 self.date() - duration
1612 },
1613 time,
1614 self.offset,
1615 )
1616 }
1617}
1618
1619impl SubAssign<Duration> for OffsetDateTime {
1620 #[inline]
1624 #[track_caller]
1625 fn sub_assign(&mut self, rhs: Duration) {
1626 *self = *self - rhs;
1627 }
1628}
1629
1630impl SubAssign<StdDuration> for OffsetDateTime {
1631 #[inline]
1635 #[track_caller]
1636 fn sub_assign(&mut self, rhs: StdDuration) {
1637 *self = *self - rhs;
1638 }
1639}
1640
1641impl Sub for OffsetDateTime {
1642 type Output = Duration;
1643
1644 #[inline]
1648 #[track_caller]
1649 fn sub(self, rhs: Self) -> Self::Output {
1650 let base = self.date_time() - rhs.date_time();
1651 let adjustment = Duration::seconds(
1652 (self.offset.whole_seconds() - rhs.offset.whole_seconds()).extend::<i64>(),
1653 );
1654 base - adjustment
1655 }
1656}