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::{carry, cascade, const_try, const_try_opt, div_floor, ensure_ranged};
23#[cfg(feature = "parsing")]
24use crate::parsing::Parsable;
25use crate::{
26 error, util, Date, Duration, Month, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
27};
28
29const UNIX_EPOCH_JULIAN_DAY: i32 = OffsetDateTime::UNIX_EPOCH.to_julian_day();
31
32#[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)]
34pub struct OffsetDateTime {
35 local_date_time: PrimitiveDateTime,
36 offset: UtcOffset,
37}
38
39impl PartialEq for OffsetDateTime {
40 #[inline]
41 fn eq(&self, other: &Self) -> bool {
42 raw_to_bits((self.year(), self.ordinal(), self.time()))
43 == raw_to_bits(other.to_offset_raw(self.offset()))
44 }
45}
46
47impl PartialOrd for OffsetDateTime {
48 #[inline]
49 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
50 Some(self.cmp(other))
51 }
52}
53
54impl Ord for OffsetDateTime {
55 #[inline]
56 fn cmp(&self, other: &Self) -> Ordering {
57 raw_to_bits((self.year(), self.ordinal(), self.time()))
58 .cmp(&raw_to_bits(other.to_offset_raw(self.offset())))
59 }
60}
61
62impl Hash for OffsetDateTime {
63 #[inline]
64 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
65 raw_to_bits(self.to_utc_raw()).hash(state);
66 }
67}
68
69#[inline]
73const fn raw_to_bits((year, ordinal, time): (i32, u16, Time)) -> i128 {
74 ((year as i128) << 74) | ((ordinal as i128) << 64) | (time.as_u64() as i128)
75}
76
77impl OffsetDateTime {
78 pub const UNIX_EPOCH: Self =
86 Self::new_in_offset(Date::UNIX_EPOCH, Time::MIDNIGHT, UtcOffset::UTC);
87
88 #[cfg(feature = "std")]
97 #[inline]
98 pub fn now_utc() -> Self {
99 #[cfg(all(
100 target_family = "wasm",
101 not(any(target_os = "emscripten", target_os = "wasi")),
102 feature = "wasm-bindgen"
103 ))]
104 {
105 js_sys::Date::new_0().into()
106 }
107
108 #[cfg(not(all(
109 target_family = "wasm",
110 not(any(target_os = "emscripten", target_os = "wasi")),
111 feature = "wasm-bindgen"
112 )))]
113 std::time::SystemTime::now().into()
114 }
115
116 #[cfg(feature = "local-offset")]
126 #[inline]
127 pub fn now_local() -> Result<Self, error::IndeterminateOffset> {
128 let t = Self::now_utc();
129 Ok(t.to_offset(UtcOffset::local_offset_at(t)?))
130 }
131
132 #[inline]
146 pub const fn new_in_offset(date: Date, time: Time, offset: UtcOffset) -> Self {
147 Self {
148 local_date_time: date.with_time(time),
149 offset,
150 }
151 }
152
153 #[inline]
166 pub const fn new_utc(date: Date, time: Time) -> Self {
167 PrimitiveDateTime::new(date, time).assume_utc()
168 }
169
170 #[inline]
196 #[track_caller]
197 pub const fn to_offset(self, offset: UtcOffset) -> Self {
198 self.checked_to_offset(offset)
199 .expect("local datetime out of valid range")
200 }
201
202 #[inline]
223 pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<Self> {
224 if self.offset.as_u32() == offset.as_u32() {
225 return Some(self);
226 }
227
228 let (year, ordinal, time) = self.to_offset_raw(offset);
229
230 if year > MAX_YEAR || year < MIN_YEAR {
231 return None;
232 }
233
234 Some(Self::new_in_offset(
235 unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) },
237 time,
238 offset,
239 ))
240 }
241
242 #[inline]
259 #[track_caller]
260 pub const fn to_utc(self) -> UtcDateTime {
261 self.checked_to_utc()
262 .expect("local datetime out of valid range")
263 }
264
265 #[cfg_attr(
279 feature = "large-dates",
280 doc = " datetime!(+999999-12-31 23:59:59 -1).checked_to_utc(),"
281 )]
282 #[cfg_attr(
283 not(feature = "large-dates"),
284 doc = " datetime!(9999-12-31 23:59:59 -1).checked_to_utc(),"
285 )]
286 #[inline]
290 pub const fn checked_to_utc(self) -> Option<UtcDateTime> {
291 if self.offset.is_utc() {
292 return Some(self.local_date_time.as_utc());
293 }
294
295 let (year, ordinal, time) = self.to_utc_raw();
296
297 if year > MAX_YEAR || year < MIN_YEAR {
298 return None;
299 }
300
301 Some(UtcDateTime::new(
302 unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) },
304 time,
305 ))
306 }
307
308 #[inline]
311 pub(crate) const fn to_utc_raw(self) -> (i32, u16, Time) {
312 let from = self.offset;
313
314 if from.is_utc() {
316 return (self.year(), self.ordinal(), self.time());
317 }
318
319 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
320 self.second() as i8 - from.seconds_past_minute(),
321 0..Second::per_t(Minute)
322 );
323 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
324 self.minute() as i8 - from.minutes_past_hour() + carry,
325 0..Minute::per_t(Hour)
326 );
327 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
328 self.hour() as i8 - from.whole_hours() + carry,
329 0..Hour::per_t(Day)
330 );
331 let (mut year, ordinal) = self.to_ordinal_date();
332 let mut ordinal = ordinal as i16 + carry;
333 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);
334
335 if true {
if !(ordinal > 0) {
::core::panicking::panic("assertion failed: ordinal > 0")
};
};debug_assert!(ordinal > 0);
336 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);
337
338 (
339 year,
340 ordinal as u16,
341 unsafe {
343 Time::__from_hms_nanos_unchecked(
344 hour as u8,
345 minute as u8,
346 second as u8,
347 self.nanosecond(),
348 )
349 },
350 )
351 }
352
353 #[inline]
356 pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) {
357 let from = self.offset;
358 let to = offset;
359
360 if from.as_u32() == to.as_u32() {
362 return (self.year(), self.ordinal(), self.time());
363 }
364
365 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
366 self.second() as i16 - from.seconds_past_minute() as i16
367 + to.seconds_past_minute() as i16,
368 0..Second::per_t(Minute)
369 );
370 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
371 self.minute() as i16 - from.minutes_past_hour() as i16
372 + to.minutes_past_hour() as i16
373 + carry,
374 0..Minute::per_t(Hour)
375 );
376 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
377 self.hour() as i8 - from.whole_hours() + to.whole_hours() + carry,
378 0..Hour::per_t(Day)
379 );
380 let (mut year, ordinal) = self.to_ordinal_date();
381 let mut ordinal = ordinal as i16 + carry;
382 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);
383
384 if true {
if !(ordinal > 0) {
::core::panicking::panic("assertion failed: ordinal > 0")
};
};debug_assert!(ordinal > 0);
385 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);
386
387 (
388 year,
389 ordinal as u16,
390 unsafe {
392 Time::__from_hms_nanos_unchecked(
393 hour as u8,
394 minute as u8,
395 second as u8,
396 self.nanosecond(),
397 )
398 },
399 )
400 }
401
402 #[inline]
431 pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange> {
432 type Timestamp = RangedI64<
433 {
434 OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
435 .unix_timestamp()
436 },
437 {
438 OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC).unix_timestamp()
439 },
440 >;
441 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);
442
443 let date = unsafe {
446 Date::from_julian_day_unchecked(
447 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,
448 )
449 };
450
451 let seconds_within_day = timestamp.rem_euclid(Second::per_t(Day));
452 let time = unsafe {
454 Time::__from_hms_nanos_unchecked(
455 (seconds_within_day / Second::per_t::<i64>(Hour)) as u8,
456 ((seconds_within_day % Second::per_t::<i64>(Hour)) / Minute::per_t::<i64>(Hour))
457 as u8,
458 (seconds_within_day % Second::per_t::<i64>(Minute)) as u8,
459 0,
460 )
461 };
462
463 Ok(Self::new_in_offset(date, time, UtcOffset::UTC))
464 }
465
466 #[inline]
482 pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange> {
483 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!(
484 timestamp,
485 Nanosecond::per_t::<i128>(Second)
486 ) as i64));
487
488 Ok(Self::new_in_offset(
489 datetime.date(),
490 unsafe {
492 Time::__from_hms_nanos_unchecked(
493 datetime.hour(),
494 datetime.minute(),
495 datetime.second(),
496 timestamp.rem_euclid(Nanosecond::per_t(Second)) as u32,
497 )
498 },
499 UtcOffset::UTC,
500 ))
501 }
502
503 #[inline]
511 pub const fn offset(self) -> UtcOffset {
512 self.offset
513 }
514
515 #[inline]
523 pub const fn unix_timestamp(self) -> i64 {
524 let days = (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64)
525 * Second::per_t::<i64>(Day);
526 let hours = self.hour() as i64 * Second::per_t::<i64>(Hour);
527 let minutes = self.minute() as i64 * Second::per_t::<i64>(Minute);
528 let seconds = self.second() as i64;
529 let offset_seconds = self.offset.whole_seconds() as i64;
530 days + hours + minutes + seconds - offset_seconds
531 }
532
533 #[inline]
544 pub const fn unix_timestamp_nanos(self) -> i128 {
545 self.unix_timestamp() as i128 * Nanosecond::per_t::<i128>(Second)
546 + self.nanosecond() as i128
547 }
548
549 #[inline]
551 pub(crate) const fn date_time(self) -> PrimitiveDateTime {
552 self.local_date_time
553 }
554
555 #[inline]
568 pub const fn date(self) -> Date {
569 self.date_time().date()
570 }
571
572 #[inline]
585 pub const fn time(self) -> Time {
586 self.date_time().time()
587 }
588
589 #[inline]
603 pub const fn year(self) -> i32 {
604 self.date().year()
605 }
606
607 #[inline]
621 pub const fn month(self) -> Month {
622 self.date().month()
623 }
624
625 #[inline]
640 pub const fn day(self) -> u8 {
641 self.date().day()
642 }
643
644 #[inline]
659 pub const fn ordinal(self) -> u16 {
660 self.date().ordinal()
661 }
662
663 #[inline]
675 pub const fn iso_week(self) -> u8 {
676 self.date().iso_week()
677 }
678
679 #[inline]
691 pub const fn sunday_based_week(self) -> u8 {
692 self.date().sunday_based_week()
693 }
694
695 #[inline]
707 pub const fn monday_based_week(self) -> u8 {
708 self.date().monday_based_week()
709 }
710
711 #[inline]
722 pub const fn to_calendar_date(self) -> (i32, Month, u8) {
723 self.date().to_calendar_date()
724 }
725
726 #[inline]
736 pub const fn to_ordinal_date(self) -> (i32, u16) {
737 self.date().to_ordinal_date()
738 }
739
740 #[inline]
767 pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
768 self.date().to_iso_week_date()
769 }
770
771 #[inline]
781 pub const fn weekday(self) -> Weekday {
782 self.date().weekday()
783 }
784
785 #[inline]
798 pub const fn to_julian_day(self) -> i32 {
799 self.date().to_julian_day()
800 }
801
802 #[inline]
810 pub const fn to_hms(self) -> (u8, u8, u8) {
811 self.time().as_hms()
812 }
813
814 #[inline]
828 pub const fn to_hms_milli(self) -> (u8, u8, u8, u16) {
829 self.time().as_hms_milli()
830 }
831
832 #[inline]
846 pub const fn to_hms_micro(self) -> (u8, u8, u8, u32) {
847 self.time().as_hms_micro()
848 }
849
850 #[inline]
864 pub const fn to_hms_nano(self) -> (u8, u8, u8, u32) {
865 self.time().as_hms_nano()
866 }
867
868 #[inline]
883 pub const fn hour(self) -> u8 {
884 self.time().hour()
885 }
886
887 #[inline]
902 pub const fn minute(self) -> u8 {
903 self.time().minute()
904 }
905
906 #[inline]
921 pub const fn second(self) -> u8 {
922 self.time().second()
923 }
924
925 #[inline]
938 pub const fn millisecond(self) -> u16 {
939 self.time().millisecond()
940 }
941
942 #[inline]
955 pub const fn microsecond(self) -> u32 {
956 self.time().microsecond()
957 }
958
959 #[inline]
972 pub const fn nanosecond(self) -> u32 {
973 self.time().nanosecond()
974 }
975
976 #[inline]
993 pub const fn checked_add(self, duration: Duration) -> Option<Self> {
994 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()))
995 }
996
997 #[inline]
1014 pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
1015 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()))
1016 }
1017
1018 #[cfg_attr(
1025 feature = "large-dates",
1026 doc = " datetime!(-999999-01-01 0:00 +10).saturating_add((-2).days()),"
1027 )]
1028 #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")]
1029 #[cfg_attr(
1030 not(feature = "large-dates"),
1031 doc = " datetime!(-9999-01-01 0:00 +10).saturating_add((-2).days()),"
1032 )]
1033 #[cfg_attr(
1034 not(feature = "large-dates"),
1035 doc = " datetime!(-9999-01-01 0:00 +10)"
1036 )]
1037 #[cfg_attr(
1041 feature = "large-dates",
1042 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days()),"
1043 )]
1044 #[cfg_attr(
1045 feature = "large-dates",
1046 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)"
1047 )]
1048 #[cfg_attr(
1049 not(feature = "large-dates"),
1050 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days()),"
1051 )]
1052 #[cfg_attr(
1053 not(feature = "large-dates"),
1054 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
1055 )]
1056 #[inline]
1064 pub const fn saturating_add(self, duration: Duration) -> Self {
1065 if let Some(datetime) = self.checked_add(duration) {
1066 datetime
1067 } else if duration.is_negative() {
1068 PrimitiveDateTime::MIN.assume_offset(self.offset())
1069 } else {
1070 PrimitiveDateTime::MAX.assume_offset(self.offset())
1071 }
1072 }
1073
1074 #[cfg_attr(
1081 feature = "large-dates",
1082 doc = " datetime!(-999999-01-01 0:00 +10).saturating_sub(2.days()),"
1083 )]
1084 #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")]
1085 #[cfg_attr(
1086 not(feature = "large-dates"),
1087 doc = " datetime!(-9999-01-01 0:00 +10).saturating_sub(2.days()),"
1088 )]
1089 #[cfg_attr(
1090 not(feature = "large-dates"),
1091 doc = " datetime!(-9999-01-01 0:00 +10)"
1092 )]
1093 #[cfg_attr(
1097 feature = "large-dates",
1098 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days()),"
1099 )]
1100 #[cfg_attr(
1101 feature = "large-dates",
1102 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)"
1103 )]
1104 #[cfg_attr(
1105 not(feature = "large-dates"),
1106 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days()),"
1107 )]
1108 #[cfg_attr(
1109 not(feature = "large-dates"),
1110 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
1111 )]
1112 #[inline]
1120 pub const fn saturating_sub(self, duration: Duration) -> Self {
1121 if let Some(datetime) = self.checked_sub(duration) {
1122 datetime
1123 } else if duration.is_negative() {
1124 PrimitiveDateTime::MAX.assume_offset(self.offset())
1125 } else {
1126 PrimitiveDateTime::MIN.assume_offset(self.offset())
1127 }
1128 }
1129}
1130
1131impl OffsetDateTime {
1133 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1152 #[inline]
1153 pub const fn replace_time(self, time: Time) -> Self {
1154 Self::new_in_offset(self.date(), time, self.offset())
1155 }
1156
1157 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1172 #[inline]
1173 pub const fn replace_date(self, date: Date) -> Self {
1174 Self::new_in_offset(date, self.time(), self.offset())
1175 }
1176
1177 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1192 #[inline]
1193 pub const fn replace_date_time(self, date_time: PrimitiveDateTime) -> Self {
1194 date_time.assume_offset(self.offset())
1195 }
1196
1197 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1207 #[inline]
1208 pub const fn replace_offset(self, offset: UtcOffset) -> Self {
1209 self.date_time().assume_offset(offset)
1210 }
1211
1212 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1224 #[inline]
1225 pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
1226 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()))
1227 }
1228
1229 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1241 #[inline]
1242 pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
1243 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()))
1244 }
1245
1246 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1258 #[inline]
1259 pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
1260 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()))
1261 }
1262
1263 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1272 #[inline]
1273 pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
1274 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()))
1275 }
1276
1277 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1288 #[inline]
1289 pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
1290 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()))
1291 }
1292
1293 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1304 #[inline]
1305 pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
1306 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()))
1307 }
1308
1309 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1320 #[inline]
1321 pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
1322 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()))
1323 }
1324
1325 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1336 #[inline]
1337 pub const fn replace_millisecond(
1338 self,
1339 millisecond: u16,
1340 ) -> Result<Self, error::ComponentRange> {
1341 Ok(
1342 match self.date_time().replace_millisecond(millisecond) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_millisecond(millisecond))
1343 .assume_offset(self.offset()),
1344 )
1345 }
1346
1347 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1358 #[inline]
1359 pub const fn replace_microsecond(
1360 self,
1361 microsecond: u32,
1362 ) -> Result<Self, error::ComponentRange> {
1363 Ok(
1364 match self.date_time().replace_microsecond(microsecond) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_microsecond(microsecond))
1365 .assume_offset(self.offset()),
1366 )
1367 }
1368
1369 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1380 #[inline]
1381 pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
1382 Ok(
1383 match self.date_time().replace_nanosecond(nanosecond) {
Ok(value) => value,
Err(error) => return Err(error),
}const_try!(self.date_time().replace_nanosecond(nanosecond))
1384 .assume_offset(self.offset()),
1385 )
1386 }
1387}
1388
1389#[cfg(feature = "formatting")]
1390impl OffsetDateTime {
1391 #[inline]
1394 pub fn format_into(
1395 self,
1396 output: &mut (impl io::Write + ?Sized),
1397 format: &(impl Formattable + ?Sized),
1398 ) -> Result<usize, error::Format> {
1399 format.format_into(
1400 output,
1401 Some(self.date()),
1402 Some(self.time()),
1403 Some(self.offset()),
1404 )
1405 }
1406
1407 #[inline]
1424 pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
1425 format.format(Some(self.date()), Some(self.time()), Some(self.offset()))
1426 }
1427}
1428
1429#[cfg(feature = "parsing")]
1430impl OffsetDateTime {
1431 #[inline]
1448 pub fn parse(
1449 input: &str,
1450 description: &(impl Parsable + ?Sized),
1451 ) -> Result<Self, error::Parse> {
1452 description.parse_offset_date_time(input.as_bytes())
1453 }
1454
1455 #[cfg(feature = "parsing")]
1459 #[inline]
1460 pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool {
1461 if self.nanosecond() != 999_999_999 {
1464 return false;
1465 }
1466
1467 let (year, ordinal, time) = self.to_utc_raw();
1468 let Ok(date) = Date::from_ordinal_date(year, ordinal) else {
1469 return false;
1470 };
1471
1472 time.hour() == 23
1473 && time.minute() == 59
1474 && time.second() == 59
1475 && date.day() == date.month().length(year)
1476 }
1477}
1478
1479impl SmartDisplay for OffsetDateTime {
1480 type Metadata = ();
1481
1482 #[inline]
1483 fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
1484 let width =
1485 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());
1486 Metadata::new(width, self, ())
1487 }
1488
1489 #[inline]
1490 fn fmt_with_metadata(
1491 &self,
1492 f: &mut fmt::Formatter<'_>,
1493 metadata: Metadata<Self>,
1494 ) -> fmt::Result {
1495 f.pad_with_width(
1496 metadata.unpadded_width(),
1497 format_args!("{0} {1} {2}", self.date(), self.time(), self.offset())format_args!("{} {} {}", self.date(), self.time(), self.offset()),
1498 )
1499 }
1500}
1501
1502impl fmt::Display for OffsetDateTime {
1503 #[inline]
1504 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1505 SmartDisplay::fmt(self, f)
1506 }
1507}
1508
1509impl fmt::Debug for OffsetDateTime {
1510 #[inline]
1511 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1512 fmt::Display::fmt(self, f)
1513 }
1514}
1515
1516impl Add<Duration> for OffsetDateTime {
1517 type Output = Self;
1518
1519 #[inline]
1523 #[track_caller]
1524 fn add(self, duration: Duration) -> Self::Output {
1525 self.checked_add(duration)
1526 .expect("resulting value is out of range")
1527 }
1528}
1529
1530impl Add<StdDuration> for OffsetDateTime {
1531 type Output = Self;
1532
1533 #[inline]
1537 #[track_caller]
1538 fn add(self, duration: StdDuration) -> Self::Output {
1539 let (is_next_day, time) = self.time().adjusting_add_std(duration);
1540
1541 Self::new_in_offset(
1542 if is_next_day {
1543 (self.date() + duration)
1544 .next_day()
1545 .expect("resulting value is out of range")
1546 } else {
1547 self.date() + duration
1548 },
1549 time,
1550 self.offset,
1551 )
1552 }
1553}
1554
1555impl AddAssign<Duration> for OffsetDateTime {
1556 #[inline]
1560 #[track_caller]
1561 fn add_assign(&mut self, rhs: Duration) {
1562 *self = *self + rhs;
1563 }
1564}
1565
1566impl AddAssign<StdDuration> for OffsetDateTime {
1567 #[inline]
1571 #[track_caller]
1572 fn add_assign(&mut self, rhs: StdDuration) {
1573 *self = *self + rhs;
1574 }
1575}
1576
1577impl Sub<Duration> for OffsetDateTime {
1578 type Output = Self;
1579
1580 #[inline]
1584 #[track_caller]
1585 fn sub(self, rhs: Duration) -> Self::Output {
1586 self.checked_sub(rhs)
1587 .expect("resulting value is out of range")
1588 }
1589}
1590
1591impl Sub<StdDuration> for OffsetDateTime {
1592 type Output = Self;
1593
1594 #[inline]
1598 #[track_caller]
1599 fn sub(self, duration: StdDuration) -> Self::Output {
1600 let (is_previous_day, time) = self.time().adjusting_sub_std(duration);
1601
1602 Self::new_in_offset(
1603 if is_previous_day {
1604 (self.date() - duration)
1605 .previous_day()
1606 .expect("resulting value is out of range")
1607 } else {
1608 self.date() - duration
1609 },
1610 time,
1611 self.offset,
1612 )
1613 }
1614}
1615
1616impl SubAssign<Duration> for OffsetDateTime {
1617 #[inline]
1621 #[track_caller]
1622 fn sub_assign(&mut self, rhs: Duration) {
1623 *self = *self - rhs;
1624 }
1625}
1626
1627impl SubAssign<StdDuration> for OffsetDateTime {
1628 #[inline]
1632 #[track_caller]
1633 fn sub_assign(&mut self, rhs: StdDuration) {
1634 *self = *self - rhs;
1635 }
1636}
1637
1638impl Sub for OffsetDateTime {
1639 type Output = Duration;
1640
1641 #[inline]
1645 #[track_caller]
1646 fn sub(self, rhs: Self) -> Self::Output {
1647 let base = self.date_time() - rhs.date_time();
1648 let adjustment = Duration::seconds(
1649 (self.offset.whole_seconds() - rhs.offset.whole_seconds()).extend::<i64>(),
1650 );
1651 base - adjustment
1652 }
1653}