1extern crate alloc;
9use crate::epoch::{UnixTimestamp, UNIX_EPOCH};
10use crate::format::iso8601::{BASIC_DATE_TIME_OF_DAY, ISO8601_DATE_TIME};
11use crate::format::{Format, FormatError, FormatParser};
12use crate::gregorian::Date;
13use crate::julian::JulianDate;
14use crate::Time;
15pub use alloc::string::String;
16use core::fmt::{Display, Formatter};
17use core::ops::{Add, AddAssign, Sub};
18use irox_units::bounds::GreaterThanEqualToValueError;
19use irox_units::units::duration::Duration;
20
21#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
24pub struct UTCDateTime {
25 pub(crate) date: Date,
26 pub(crate) time: Time,
27}
28
29impl Display for UTCDateTime {
30 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
31 f.write_fmt(format_args!("{}", self.format(&BASIC_DATE_TIME_OF_DAY)))
32 }
33}
34
35impl UTCDateTime {
36 #[must_use]
39 pub fn new(date: Date, time: Time) -> UTCDateTime {
40 UTCDateTime { date, time }
41 }
42
43 pub fn try_from_values(
46 year: i32,
47 month: u8,
48 day: u8,
49 hour: u8,
50 minute: u8,
51 seconds: u8,
52 ) -> Result<UTCDateTime, GreaterThanEqualToValueError<u8>> {
53 let date = Date::try_from_values(year, month, day)?;
54 let time = Time::from_hms(hour, minute, seconds)?;
55 Ok(UTCDateTime::new(date, time))
56 }
57
58 pub fn try_from_values_f64(
61 year: i32,
62 month: u8,
63 day: u8,
64 hour: u8,
65 minute: u8,
66 seconds: f64,
67 ) -> Result<UTCDateTime, GreaterThanEqualToValueError<f64>> {
68 let date = Date::try_from_values(year, month, day)?;
69 let time = Time::from_hms_f64(hour, minute, seconds)?;
70 Ok(UTCDateTime::new(date, time))
71 }
72
73 #[must_use]
76 pub fn get_date(&self) -> Date {
77 self.date
78 }
79
80 #[must_use]
83 pub fn get_time(&self) -> Time {
84 self.time
85 }
86
87 #[must_use]
91 #[cfg(feature = "std")]
92 pub fn now() -> UTCDateTime {
93 UnixTimestamp::now().into()
94 }
95
96 #[must_use]
97 pub fn format<T: Format<UTCDateTime>>(&self, format: &T) -> String {
98 format.format(self)
99 }
100
101 #[must_use]
103 pub fn format_iso8601_extended(&self) -> String {
104 ISO8601_DATE_TIME.format(self)
105 }
106 #[must_use]
108 pub fn format_iso8601_basic(&self) -> String {
109 BASIC_DATE_TIME_OF_DAY.format(self)
110 }
111 pub fn try_from_iso8601(val: &str) -> Result<Self, FormatError> {
113 ISO8601_DATE_TIME.try_from(val)
114 }
115}
116
117impl From<&UnixTimestamp> for UTCDateTime {
118 fn from(value: &UnixTimestamp) -> Self {
119 let date = value.as_date();
120 let remaining_seconds = value.get_offset().as_seconds_f64()
121 - date.as_unix_timestamp().get_offset().as_seconds_f64();
122
123 let time = Time::from_seconds_f64(remaining_seconds).unwrap_or_default();
124
125 UTCDateTime { date, time }
126 }
127}
128impl From<UnixTimestamp> for UTCDateTime {
129 fn from(value: UnixTimestamp) -> Self {
130 let date = value.as_date();
131 let remaining_seconds = value.get_offset().as_seconds_f64()
132 - date.as_unix_timestamp().get_offset().as_seconds_f64();
133
134 let time = Time::from_seconds_f64(remaining_seconds).unwrap_or_default();
135
136 UTCDateTime { date, time }
137 }
138}
139
140impl From<UTCDateTime> for UnixTimestamp {
141 fn from(value: UTCDateTime) -> Self {
142 let mut date_dur = value.date - UNIX_EPOCH.get_gregorian_date();
143 date_dur += Into::<Duration>::into(value.time);
144 Self::from_offset(date_dur)
145 }
146}
147impl From<&UTCDateTime> for UnixTimestamp {
148 fn from(value: &UTCDateTime) -> Self {
149 let mut date_dur = value.date - UNIX_EPOCH.get_gregorian_date();
150 date_dur += Into::<Duration>::into(value.time);
151 Self::from_offset(date_dur)
152 }
153}
154
155impl From<UTCDateTime> for JulianDate {
156 fn from(value: UTCDateTime) -> Self {
157 let mut date: JulianDate = value.date.into();
158 let time: Duration = value.time.into();
159 date += time;
160 date
161 }
162}
163
164impl From<&UTCDateTime> for JulianDate {
165 fn from(value: &UTCDateTime) -> Self {
166 let mut date: JulianDate = value.date.into();
167 let time: Duration = value.time.into();
168 date += time;
169 date
170 }
171}
172
173impl Sub<Self> for UTCDateTime {
174 type Output = Duration;
175
176 fn sub(self, rhs: Self) -> Self::Output {
177 let ts1: JulianDate = self.into();
178 let ts2: JulianDate = rhs.into();
179
180 ts1 - ts2
181 }
182}
183impl Sub<&Self> for UTCDateTime {
184 type Output = Duration;
185
186 fn sub(self, rhs: &Self) -> Self::Output {
187 let ts1: JulianDate = self.into();
188 let ts2: JulianDate = rhs.into();
189
190 ts1 - ts2
191 }
192}
193
194impl Add<Duration> for UTCDateTime {
195 type Output = UTCDateTime;
196
197 fn add(self, rhs: Duration) -> Self::Output {
198 let (time, excess) = self.time.wrapping_add(rhs);
199 let date = self.date + excess;
200 UTCDateTime { date, time }
201 }
202}
203impl Add<&Duration> for UTCDateTime {
204 type Output = UTCDateTime;
205
206 fn add(self, rhs: &Duration) -> Self::Output {
207 let (time, excess) = self.time.wrapping_add(*rhs);
208 let date = self.date + excess;
209 UTCDateTime { date, time }
210 }
211}
212
213impl AddAssign<Duration> for UTCDateTime {
214 fn add_assign(&mut self, rhs: Duration) {
215 let (time, excess) = self.time.wrapping_add(rhs);
216 self.time = time;
217 self.date += excess;
218 }
219}
220impl AddAssign<&Duration> for UTCDateTime {
221 fn add_assign(&mut self, rhs: &Duration) {
222 let (time, excess) = self.time.wrapping_add(*rhs);
223 self.time = time;
224 self.date += excess;
225 }
226}