1#[cfg(feature = "formatting")]
4use alloc::string::String;
5use core::cmp::Ordering;
6use core::fmt;
7use core::hash::{Hash, Hasher};
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::util::days_in_year;
26use crate::{
27 Date, Duration, Month, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday, error,
28};
29
30const UNIX_EPOCH_JULIAN_DAY: i32 = OffsetDateTime::UNIX_EPOCH.to_julian_day();
32
33#[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)]
35pub struct OffsetDateTime {
36 local_date_time: PrimitiveDateTime,
37 offset: UtcOffset,
38}
39
40impl PartialEq for OffsetDateTime {
41 #[inline]
42 fn eq(&self, other: &Self) -> bool {
43 raw_to_bits((self.year(), self.ordinal(), self.time()))
44 == raw_to_bits(other.to_offset_raw(self.offset()))
45 }
46}
47
48impl PartialOrd for OffsetDateTime {
49 #[inline]
50 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
51 Some(self.cmp(other))
52 }
53}
54
55impl Ord for OffsetDateTime {
56 #[inline]
57 fn cmp(&self, other: &Self) -> Ordering {
58 raw_to_bits((self.year(), self.ordinal(), self.time()))
59 .cmp(&raw_to_bits(other.to_offset_raw(self.offset())))
60 }
61}
62
63impl Hash for OffsetDateTime {
64 #[inline]
65 fn hash<H>(&self, state: &mut H)
66 where
67 H: Hasher,
68 {
69 raw_to_bits(self.to_utc_raw()).hash(state);
70 }
71}
72
73#[inline]
77const fn raw_to_bits((year, ordinal, time): (i32, u16, Time)) -> i128 {
78 ((year as i128) << 74) | ((ordinal as i128) << 64) | (time.as_u64() as i128)
79}
80
81impl OffsetDateTime {
82 pub const UNIX_EPOCH: Self =
90 Self::new_in_offset(Date::UNIX_EPOCH, Time::MIDNIGHT, UtcOffset::UTC);
91
92 #[cfg(feature = "std")]
101 #[inline]
102 pub fn now_utc() -> Self {
103 #[cfg(all(
104 target_family = "wasm",
105 not(any(target_os = "emscripten", target_os = "wasi")),
106 feature = "wasm-bindgen"
107 ))]
108 {
109 js_sys::Date::new_0().into()
110 }
111
112 #[cfg(not(all(
113 target_family = "wasm",
114 not(any(target_os = "emscripten", target_os = "wasi")),
115 feature = "wasm-bindgen"
116 )))]
117 std::time::SystemTime::now().into()
118 }
119
120 #[cfg(feature = "local-offset")]
130 #[inline]
131 pub fn now_local() -> Result<Self, error::IndeterminateOffset> {
132 let t = Self::now_utc();
133 Ok(t.to_offset(UtcOffset::local_offset_at(t)?))
134 }
135
136 #[inline]
150 pub const fn new_in_offset(date: Date, time: Time, offset: UtcOffset) -> Self {
151 Self {
152 local_date_time: date.with_time(time),
153 offset,
154 }
155 }
156
157 #[inline]
170 pub const fn new_utc(date: Date, time: Time) -> Self {
171 PrimitiveDateTime::new(date, time).assume_utc()
172 }
173
174 #[inline]
200 #[track_caller]
201 pub const fn to_offset(self, offset: UtcOffset) -> Self {
202 self.checked_to_offset(offset)
203 .expect("local datetime out of valid range")
204 }
205
206 #[inline]
227 pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<Self> {
228 if self.offset.as_u32_for_equality() == offset.as_u32_for_equality() {
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 self.checked_to_utc()
266 .expect("local datetime out of valid range")
267 }
268
269 #[cfg_attr(
283 feature = "large-dates",
284 doc = " datetime!(+999999-12-31 23:59:59 -1).checked_to_utc(),"
285 )]
286 #[cfg_attr(
287 not(feature = "large-dates"),
288 doc = " datetime!(9999-12-31 23:59:59 -1).checked_to_utc(),"
289 )]
290 #[inline]
294 pub const fn checked_to_utc(self) -> Option<UtcDateTime> {
295 if self.offset.is_utc() {
296 return Some(self.local_date_time.as_utc());
297 }
298
299 let (year, ordinal, time) = self.to_utc_raw();
300
301 if year > MAX_YEAR || year < MIN_YEAR {
302 return None;
303 }
304
305 Some(UtcDateTime::new(
306 unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) },
308 time,
309 ))
310 }
311
312 #[inline]
315 pub(crate) const fn to_utc_raw(self) -> (i32, u16, Time) {
316 let from = self.offset;
317
318 if from.is_utc() {
320 return (self.year(), self.ordinal(), self.time());
321 }
322
323 let (second, carry) = match (self.second().cast_signed() - 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
324 self.second().cast_signed() - from.seconds_past_minute(),
325 0..Second::per_t(Minute)
326 );
327 let (minute, carry) = match (self.minute().cast_signed() - 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
328 self.minute().cast_signed() - from.minutes_past_hour() + carry,
329 0..Minute::per_t(Hour)
330 );
331 let (hour, carry) = match (self.hour().cast_signed() - 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
332 self.hour().cast_signed() - from.whole_hours() + carry,
333 0..Hour::per_t(Day)
334 );
335 let (mut year, ordinal) = self.to_ordinal_date();
336 let mut ordinal = ordinal.cast_signed() + carry;
337 let days_in_year =
crate::util::range_validated::days_in_year(year).cast_signed();
#[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::range_validated::days_in_year(year).cast_signed();
};cascade!(ordinal => year);
338
339 if true {
if !(ordinal > 0) {
::core::panicking::panic("assertion failed: ordinal > 0")
};
};debug_assert!(ordinal > 0);
340 if true {
if !(ordinal <= days_in_year(year).cast_signed()) {
::core::panicking::panic("assertion failed: ordinal <= days_in_year(year).cast_signed()")
};
};debug_assert!(ordinal <= days_in_year(year).cast_signed());
341
342 (
343 year,
344 ordinal.cast_unsigned(),
345 unsafe {
347 Time::__from_hms_nanos_unchecked(
348 hour.cast_unsigned(),
349 minute.cast_unsigned(),
350 second.cast_unsigned(),
351 self.nanosecond(),
352 )
353 },
354 )
355 }
356
357 #[inline]
360 pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) {
361 let from = self.offset;
362 let to = offset;
363
364 if from.as_u32_for_equality() == to.as_u32_for_equality() {
366 return (self.year(), self.ordinal(), self.time());
367 }
368
369 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
370 self.second() as i16 - from.seconds_past_minute() as i16
371 + to.seconds_past_minute() as i16,
372 0..Second::per_t(Minute)
373 );
374 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
375 self.minute() as i16 - from.minutes_past_hour() as i16
376 + to.minutes_past_hour() as i16
377 + carry,
378 0..Minute::per_t(Hour)
379 );
380 let (hour, carry) = match (self.hour().cast_signed() - 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
381 self.hour().cast_signed() - from.whole_hours() + to.whole_hours() + carry,
382 0..Hour::per_t(Day)
383 );
384 let (mut year, ordinal) = self.to_ordinal_date();
385 let mut ordinal = ordinal.cast_signed() + carry;
386 let days_in_year =
crate::util::range_validated::days_in_year(year).cast_signed();
#[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::range_validated::days_in_year(year).cast_signed();
};cascade!(ordinal => year);
387
388 if true {
if !(ordinal > 0) {
::core::panicking::panic("assertion failed: ordinal > 0")
};
};debug_assert!(ordinal > 0);
389 if true {
if !(ordinal <= days_in_year(year).cast_signed()) {
::core::panicking::panic("assertion failed: ordinal <= days_in_year(year).cast_signed()")
};
};debug_assert!(ordinal <= days_in_year(year).cast_signed());
390
391 (
392 year,
393 ordinal.cast_unsigned(),
394 unsafe {
396 Time::__from_hms_nanos_unchecked(
397 hour.cast_unsigned(),
398 minute as u8,
399 second as u8,
400 self.nanosecond(),
401 )
402 },
403 )
404 }
405
406 #[inline]
435 pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange> {
436 type Timestamp = RangedI64<
437 {
438 OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
439 .unix_timestamp()
440 },
441 {
442 OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC).unix_timestamp()
443 },
444 >;
445 match <Timestamp>::new(timestamp) {
Some(val) => val,
None => {
crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional("timestamp"));
}
};ensure_ranged!(Timestamp: timestamp);
446
447 let date = unsafe {
450 Date::from_julian_day_unchecked(
451 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) >> (size_of_val(&this) * 8 - 1);
if r != 0 { d + correction } else { d }
}
}div_floor!(timestamp, Second::per_t::<i64>(Day)) as i32,
452 )
453 };
454
455 let seconds_within_day = timestamp.rem_euclid(Second::per_t(Day));
456 let time = unsafe {
458 Time::__from_hms_nanos_unchecked(
459 (seconds_within_day / Second::per_t::<i64>(Hour)) as u8,
460 ((seconds_within_day % Second::per_t::<i64>(Hour)) / Minute::per_t::<i64>(Hour))
461 as u8,
462 (seconds_within_day % Second::per_t::<i64>(Minute)) as u8,
463 0,
464 )
465 };
466
467 Ok(Self::new_in_offset(date, time, UtcOffset::UTC))
468 }
469
470 #[inline]
486 pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange> {
487 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) >> (size_of_val(&this) * 8 - 1);
if r != 0 { d + correction } else { d }
}
} as i64) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(Self::from_unix_timestamp(div_floor!(
488 timestamp,
489 Nanosecond::per_t::<i128>(Second)
490 ) as i64));
491
492 Ok(Self::new_in_offset(
493 datetime.date(),
494 unsafe {
496 Time::__from_hms_nanos_unchecked(
497 datetime.hour(),
498 datetime.minute(),
499 datetime.second(),
500 timestamp.rem_euclid(Nanosecond::per_t(Second)) as u32,
501 )
502 },
503 UtcOffset::UTC,
504 ))
505 }
506
507 #[inline]
515 pub const fn offset(self) -> UtcOffset {
516 self.offset
517 }
518
519 #[inline]
527 pub const fn unix_timestamp(self) -> i64 {
528 let days = (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64)
529 * Second::per_t::<i64>(Day);
530 let hours = self.hour() as i64 * Second::per_t::<i64>(Hour);
531 let minutes = self.minute() as i64 * Second::per_t::<i64>(Minute);
532 let seconds = self.second() as i64;
533 let offset_seconds = self.offset.whole_seconds() as i64;
534 days + hours + minutes + seconds - offset_seconds
535 }
536
537 #[inline]
548 pub const fn unix_timestamp_nanos(self) -> i128 {
549 self.unix_timestamp() as i128 * Nanosecond::per_t::<i128>(Second)
550 + self.nanosecond() as i128
551 }
552
553 #[inline]
555 pub(crate) const fn date_time(self) -> PrimitiveDateTime {
556 self.local_date_time
557 }
558
559 #[inline]
572 pub const fn date(self) -> Date {
573 self.date_time().date()
574 }
575
576 #[inline]
589 pub const fn time(self) -> Time {
590 self.date_time().time()
591 }
592
593 #[inline]
607 pub const fn year(self) -> i32 {
608 self.date().year()
609 }
610
611 #[inline]
625 pub const fn month(self) -> Month {
626 self.date().month()
627 }
628
629 #[inline]
644 pub const fn day(self) -> u8 {
645 self.date().day()
646 }
647
648 #[inline]
663 pub const fn ordinal(self) -> u16 {
664 self.date().ordinal()
665 }
666
667 #[inline]
679 pub const fn iso_week(self) -> u8 {
680 self.date().iso_week()
681 }
682
683 #[inline]
695 pub const fn sunday_based_week(self) -> u8 {
696 self.date().sunday_based_week()
697 }
698
699 #[inline]
711 pub const fn monday_based_week(self) -> u8 {
712 self.date().monday_based_week()
713 }
714
715 #[inline]
726 pub const fn to_calendar_date(self) -> (i32, Month, u8) {
727 self.date().to_calendar_date()
728 }
729
730 #[inline]
740 pub const fn to_ordinal_date(self) -> (i32, u16) {
741 self.date().to_ordinal_date()
742 }
743
744 #[inline]
771 pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
772 self.date().to_iso_week_date()
773 }
774
775 #[inline]
785 pub const fn weekday(self) -> Weekday {
786 self.date().weekday()
787 }
788
789 #[inline]
799 pub const fn to_julian_day(self) -> i32 {
800 self.date().to_julian_day()
801 }
802
803 #[inline]
811 pub const fn to_hms(self) -> (u8, u8, u8) {
812 self.time().as_hms()
813 }
814
815 #[inline]
829 pub const fn to_hms_milli(self) -> (u8, u8, u8, u16) {
830 self.time().as_hms_milli()
831 }
832
833 #[inline]
847 pub const fn to_hms_micro(self) -> (u8, u8, u8, u32) {
848 self.time().as_hms_micro()
849 }
850
851 #[inline]
865 pub const fn to_hms_nano(self) -> (u8, u8, u8, u32) {
866 self.time().as_hms_nano()
867 }
868
869 #[inline]
884 pub const fn hour(self) -> u8 {
885 self.time().hour()
886 }
887
888 #[inline]
903 pub const fn minute(self) -> u8 {
904 self.time().minute()
905 }
906
907 #[inline]
922 pub const fn second(self) -> u8 {
923 self.time().second()
924 }
925
926 #[inline]
939 pub const fn millisecond(self) -> u16 {
940 self.time().millisecond()
941 }
942
943 #[inline]
956 pub const fn microsecond(self) -> u32 {
957 self.time().microsecond()
958 }
959
960 #[inline]
973 pub const fn nanosecond(self) -> u32 {
974 self.time().nanosecond()
975 }
976
977 #[inline]
994 pub const fn checked_add(self, duration: Duration) -> Option<Self> {
995 Some(match self.date_time().checked_add(duration) {
Some(value) => value,
None => { crate::hint::cold_path(); return None; }
}const_try_opt!(self.date_time().checked_add(duration)).assume_offset(self.offset()))
996 }
997
998 #[inline]
1015 pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
1016 Some(match self.date_time().checked_sub(duration) {
Some(value) => value,
None => { crate::hint::cold_path(); return None; }
}const_try_opt!(self.date_time().checked_sub(duration)).assume_offset(self.offset()))
1017 }
1018
1019 #[cfg_attr(
1026 feature = "large-dates",
1027 doc = " datetime!(-999999-01-01 0:00 +10).saturating_add((-2).days()),"
1028 )]
1029 #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")]
1030 #[cfg_attr(
1031 not(feature = "large-dates"),
1032 doc = " datetime!(-9999-01-01 0:00 +10).saturating_add((-2).days()),"
1033 )]
1034 #[cfg_attr(
1035 not(feature = "large-dates"),
1036 doc = " datetime!(-9999-01-01 0:00 +10)"
1037 )]
1038 #[cfg_attr(
1042 feature = "large-dates",
1043 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days()),"
1044 )]
1045 #[cfg_attr(
1046 feature = "large-dates",
1047 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)"
1048 )]
1049 #[cfg_attr(
1050 not(feature = "large-dates"),
1051 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days()),"
1052 )]
1053 #[cfg_attr(
1054 not(feature = "large-dates"),
1055 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
1056 )]
1057 #[inline]
1065 pub const fn saturating_add(self, duration: Duration) -> Self {
1066 if let Some(datetime) = self.checked_add(duration) {
1067 datetime
1068 } else if duration.is_negative() {
1069 PrimitiveDateTime::MIN.assume_offset(self.offset())
1070 } else {
1071 PrimitiveDateTime::MAX.assume_offset(self.offset())
1072 }
1073 }
1074
1075 #[cfg_attr(
1082 feature = "large-dates",
1083 doc = " datetime!(-999999-01-01 0:00 +10).saturating_sub(2.days()),"
1084 )]
1085 #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")]
1086 #[cfg_attr(
1087 not(feature = "large-dates"),
1088 doc = " datetime!(-9999-01-01 0:00 +10).saturating_sub(2.days()),"
1089 )]
1090 #[cfg_attr(
1091 not(feature = "large-dates"),
1092 doc = " datetime!(-9999-01-01 0:00 +10)"
1093 )]
1094 #[cfg_attr(
1098 feature = "large-dates",
1099 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days()),"
1100 )]
1101 #[cfg_attr(
1102 feature = "large-dates",
1103 doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)"
1104 )]
1105 #[cfg_attr(
1106 not(feature = "large-dates"),
1107 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days()),"
1108 )]
1109 #[cfg_attr(
1110 not(feature = "large-dates"),
1111 doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
1112 )]
1113 #[inline]
1121 pub const fn saturating_sub(self, duration: Duration) -> Self {
1122 if let Some(datetime) = self.checked_sub(duration) {
1123 datetime
1124 } else if duration.is_negative() {
1125 PrimitiveDateTime::MAX.assume_offset(self.offset())
1126 } else {
1127 PrimitiveDateTime::MIN.assume_offset(self.offset())
1128 }
1129 }
1130}
1131
1132impl OffsetDateTime {
1134 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1153 #[inline]
1154 pub const fn replace_time(self, time: Time) -> Self {
1155 Self::new_in_offset(self.date(), time, self.offset())
1156 }
1157
1158 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1173 #[inline]
1174 pub const fn replace_date(self, date: Date) -> Self {
1175 Self::new_in_offset(date, self.time(), self.offset())
1176 }
1177
1178 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1193 #[inline]
1194 pub const fn replace_date_time(self, date_time: PrimitiveDateTime) -> Self {
1195 date_time.assume_offset(self.offset())
1196 }
1197
1198 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1208 #[inline]
1209 pub const fn replace_offset(self, offset: UtcOffset) -> Self {
1210 self.date_time().assume_offset(offset)
1211 }
1212
1213 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1225 #[inline]
1226 pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
1227 Ok(match self.date_time().replace_year(year) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_year(year)).assume_offset(self.offset()))
1228 }
1229
1230 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1242 #[inline]
1243 pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
1244 Ok(match self.date_time().replace_month(month) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_month(month)).assume_offset(self.offset()))
1245 }
1246
1247 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1259 #[inline]
1260 pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
1261 Ok(match self.date_time().replace_day(day) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_day(day)).assume_offset(self.offset()))
1262 }
1263
1264 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1273 #[inline]
1274 pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
1275 Ok(match self.date_time().replace_ordinal(ordinal) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_ordinal(ordinal)).assume_offset(self.offset()))
1276 }
1277
1278 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1288 #[inline]
1289 pub const fn truncate_to_day(mut self) -> Self {
1290 self.local_date_time = self.local_date_time.truncate_to_day();
1291 self
1292 }
1293
1294 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1305 #[inline]
1306 pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
1307 Ok(match self.date_time().replace_hour(hour) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_hour(hour)).assume_offset(self.offset()))
1308 }
1309
1310 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1320 #[inline]
1321 pub const fn truncate_to_hour(mut self) -> Self {
1322 self.local_date_time = self.local_date_time.truncate_to_hour();
1323 self
1324 }
1325
1326 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1337 #[inline]
1338 pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
1339 Ok(match self.date_time().replace_minute(minute) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_minute(minute)).assume_offset(self.offset()))
1340 }
1341
1342 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1352 #[inline]
1353 pub const fn truncate_to_minute(mut self) -> Self {
1354 self.local_date_time = self.local_date_time.truncate_to_minute();
1355 self
1356 }
1357
1358 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1369 #[inline]
1370 pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
1371 Ok(match self.date_time().replace_second(second) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_second(second)).assume_offset(self.offset()))
1372 }
1373
1374 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1384 #[inline]
1385 pub const fn truncate_to_second(mut self) -> Self {
1386 self.local_date_time = self.local_date_time.truncate_to_second();
1387 self
1388 }
1389
1390 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1401 #[inline]
1402 pub const fn replace_millisecond(
1403 self,
1404 millisecond: u16,
1405 ) -> Result<Self, error::ComponentRange> {
1406 Ok(
1407 match self.date_time().replace_millisecond(millisecond) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_millisecond(millisecond))
1408 .assume_offset(self.offset()),
1409 )
1410 }
1411
1412 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1422 #[inline]
1423 pub const fn truncate_to_millisecond(mut self) -> Self {
1424 self.local_date_time = self.local_date_time.truncate_to_millisecond();
1425 self
1426 }
1427
1428 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1439 #[inline]
1440 pub const fn replace_microsecond(
1441 self,
1442 microsecond: u32,
1443 ) -> Result<Self, error::ComponentRange> {
1444 Ok(
1445 match self.date_time().replace_microsecond(microsecond) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_microsecond(microsecond))
1446 .assume_offset(self.offset()),
1447 )
1448 }
1449
1450 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1460 #[inline]
1461 pub const fn truncate_to_microsecond(mut self) -> Self {
1462 self.local_date_time = self.local_date_time.truncate_to_microsecond();
1463 self
1464 }
1465
1466 #[must_use = "This method does not mutate the original `OffsetDateTime`."]
1477 #[inline]
1478 pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
1479 Ok(
1480 match self.date_time().replace_nanosecond(nanosecond) {
Ok(value) => value,
Err(error) => { crate::hint::cold_path(); return Err(error); }
}const_try!(self.date_time().replace_nanosecond(nanosecond))
1481 .assume_offset(self.offset()),
1482 )
1483 }
1484}
1485
1486#[cfg(feature = "formatting")]
1487impl OffsetDateTime {
1488 #[inline]
1491 pub fn format_into(
1492 self,
1493 output: &mut (impl io::Write + ?Sized),
1494 format: &(impl Formattable + ?Sized),
1495 ) -> Result<usize, error::Format> {
1496 format.format_into(
1497 output,
1498 Some(self.date()),
1499 Some(self.time()),
1500 Some(self.offset()),
1501 )
1502 }
1503
1504 #[inline]
1521 pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
1522 format.format(Some(self.date()), Some(self.time()), Some(self.offset()))
1523 }
1524}
1525
1526#[cfg(feature = "parsing")]
1527impl OffsetDateTime {
1528 #[inline]
1545 pub fn parse(
1546 input: &str,
1547 description: &(impl Parsable + ?Sized),
1548 ) -> Result<Self, error::Parse> {
1549 description.parse_offset_date_time(input.as_bytes())
1550 }
1551
1552 #[cfg(feature = "parsing")]
1556 #[inline]
1557 pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool {
1558 if self.nanosecond() != 999_999_999 {
1561 return false;
1562 }
1563
1564 let (year, ordinal, time) = self.to_utc_raw();
1565 let Ok(date) = Date::from_ordinal_date(year, ordinal) else {
1566 return false;
1567 };
1568
1569 time.hour() == 23
1570 && time.minute() == 59
1571 && time.second() == 59
1572 && date.day() == date.month().length(year)
1573 }
1574}
1575
1576impl SmartDisplay for OffsetDateTime {
1577 type Metadata = ();
1578
1579 #[inline]
1580 fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
1581 let width =
1582 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());
1583 Metadata::new(width, self, ())
1584 }
1585
1586 #[inline]
1587 fn fmt_with_metadata(
1588 &self,
1589 f: &mut fmt::Formatter<'_>,
1590 metadata: Metadata<Self>,
1591 ) -> fmt::Result {
1592 f.pad_with_width(
1593 metadata.unpadded_width(),
1594 format_args!("{0} {1} {2}", self.date(), self.time(), self.offset())format_args!("{} {} {}", self.date(), self.time(), self.offset()),
1595 )
1596 }
1597}
1598
1599impl fmt::Display for OffsetDateTime {
1600 #[inline]
1601 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1602 SmartDisplay::fmt(self, f)
1603 }
1604}
1605
1606impl fmt::Debug for OffsetDateTime {
1607 #[inline]
1608 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1609 fmt::Display::fmt(self, f)
1610 }
1611}
1612
1613impl Add<Duration> for OffsetDateTime {
1614 type Output = Self;
1615
1616 #[inline]
1620 #[track_caller]
1621 fn add(self, duration: Duration) -> Self::Output {
1622 self.checked_add(duration)
1623 .expect("resulting value is out of range")
1624 }
1625}
1626
1627impl Add<StdDuration> for OffsetDateTime {
1628 type Output = Self;
1629
1630 #[inline]
1634 #[track_caller]
1635 fn add(self, duration: StdDuration) -> Self::Output {
1636 let (is_next_day, time) = self.time().adjusting_add_std(duration);
1637
1638 Self::new_in_offset(
1639 if is_next_day {
1640 (self.date() + duration)
1641 .next_day()
1642 .expect("resulting value is out of range")
1643 } else {
1644 self.date() + duration
1645 },
1646 time,
1647 self.offset,
1648 )
1649 }
1650}
1651
1652impl AddAssign<Duration> for OffsetDateTime {
1653 #[inline]
1657 #[track_caller]
1658 fn add_assign(&mut self, rhs: Duration) {
1659 *self = *self + rhs;
1660 }
1661}
1662
1663impl AddAssign<StdDuration> for OffsetDateTime {
1664 #[inline]
1668 #[track_caller]
1669 fn add_assign(&mut self, rhs: StdDuration) {
1670 *self = *self + rhs;
1671 }
1672}
1673
1674impl Sub<Duration> for OffsetDateTime {
1675 type Output = Self;
1676
1677 #[inline]
1681 #[track_caller]
1682 fn sub(self, rhs: Duration) -> Self::Output {
1683 self.checked_sub(rhs)
1684 .expect("resulting value is out of range")
1685 }
1686}
1687
1688impl Sub<StdDuration> for OffsetDateTime {
1689 type Output = Self;
1690
1691 #[inline]
1695 #[track_caller]
1696 fn sub(self, duration: StdDuration) -> Self::Output {
1697 let (is_previous_day, time) = self.time().adjusting_sub_std(duration);
1698
1699 Self::new_in_offset(
1700 if is_previous_day {
1701 (self.date() - duration)
1702 .previous_day()
1703 .expect("resulting value is out of range")
1704 } else {
1705 self.date() - duration
1706 },
1707 time,
1708 self.offset,
1709 )
1710 }
1711}
1712
1713impl SubAssign<Duration> for OffsetDateTime {
1714 #[inline]
1718 #[track_caller]
1719 fn sub_assign(&mut self, rhs: Duration) {
1720 *self = *self - rhs;
1721 }
1722}
1723
1724impl SubAssign<StdDuration> for OffsetDateTime {
1725 #[inline]
1729 #[track_caller]
1730 fn sub_assign(&mut self, rhs: StdDuration) {
1731 *self = *self - rhs;
1732 }
1733}
1734
1735impl Sub for OffsetDateTime {
1736 type Output = Duration;
1737
1738 #[inline]
1742 #[track_caller]
1743 fn sub(self, rhs: Self) -> Self::Output {
1744 let base = self.date_time() - rhs.date_time();
1745 let adjustment = Duration::seconds(
1746 (self.offset.whole_seconds() - rhs.offset.whole_seconds()).extend::<i64>(),
1747 );
1748 base - adjustment
1749 }
1750}