1extern crate alloc;
9use crate::epoch::{UnixTimestamp, UNIX_EPOCH};
10use crate::format::iso8601::{ISO8601Format, 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_tools::cfg_feature_serde;
19use irox_units::bounds::GreaterThanEqualToValueError;
20use irox_units::units::duration::Duration;
21
22#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
25pub struct UTCDateTime {
26 pub(crate) date: Date,
27 pub(crate) time: Time,
28}
29
30impl Display for UTCDateTime {
31 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
32 f.write_fmt(format_args!("{}", self.format(&BASIC_DATE_TIME_OF_DAY)))
33 }
34}
35
36impl UTCDateTime {
37 #[must_use]
40 pub fn new(date: Date, time: Time) -> UTCDateTime {
41 UTCDateTime { date, time }
42 }
43
44 pub fn try_from_values(
47 year: i32,
48 month: u8,
49 day: u8,
50 hour: u8,
51 minute: u8,
52 seconds: u8,
53 ) -> Result<UTCDateTime, GreaterThanEqualToValueError<u8>> {
54 let date = Date::try_from_values(year, month, day)?;
55 let time = Time::from_hms(hour, minute, seconds)?;
56 Ok(UTCDateTime::new(date, time))
57 }
58
59 pub fn try_from_values_f64(
62 year: i32,
63 month: u8,
64 day: u8,
65 hour: u8,
66 minute: u8,
67 seconds: f64,
68 ) -> Result<UTCDateTime, GreaterThanEqualToValueError<f64>> {
69 let date = Date::try_from_values(year, month, day)?;
70 let time = Time::from_hms_f64(hour, minute, seconds)?;
71 Ok(UTCDateTime::new(date, time))
72 }
73
74 #[must_use]
77 pub fn get_date(&self) -> Date {
78 self.date
79 }
80
81 #[must_use]
84 pub fn get_time(&self) -> Time {
85 self.time
86 }
87
88 #[must_use]
92 #[cfg(feature = "std")]
93 pub fn now() -> UTCDateTime {
94 UnixTimestamp::now().into()
95 }
96
97 #[must_use]
98 pub fn format<T: Format<UTCDateTime>>(&self, format: &T) -> String {
99 format.format(self)
100 }
101
102 #[must_use]
104 pub fn format_iso8601_extended(&self) -> String {
105 ISO8601_DATE_TIME.format(self)
106 }
107 #[must_use]
109 pub fn format_iso8601_basic(&self) -> String {
110 BASIC_DATE_TIME_OF_DAY.format(self)
111 }
112 pub fn try_from_iso8601(val: &str) -> Result<Self, FormatError> {
114 ISO8601_DATE_TIME.try_from(val)
115 }
116}
117
118impl ISO8601Format for UTCDateTime {
119 fn format_iso8601_extended(&self) -> String {
120 UTCDateTime::format_iso8601_extended(self)
121 }
122
123 fn format_iso8601_basic(&self) -> String {
124 UTCDateTime::format_iso8601_basic(self)
125 }
126
127 fn try_from_iso8601(val: &str) -> Result<Self, FormatError>
128 where
129 Self: Sized,
130 {
131 UTCDateTime::try_from_iso8601(val)
132 }
133}
134
135impl From<&UnixTimestamp> for UTCDateTime {
136 fn from(value: &UnixTimestamp) -> Self {
137 let date = value.as_date();
138 let remaining_seconds = value.get_offset().as_seconds_f64()
139 - date.as_unix_timestamp().get_offset().as_seconds_f64();
140
141 let time = Time::from_seconds_f64(remaining_seconds).unwrap_or_default();
142
143 UTCDateTime { date, time }
144 }
145}
146impl From<UnixTimestamp> for UTCDateTime {
147 fn from(value: UnixTimestamp) -> Self {
148 let date = value.as_date();
149 let remaining_seconds = value.get_offset().as_seconds_f64()
150 - date.as_unix_timestamp().get_offset().as_seconds_f64();
151
152 let time = Time::from_seconds_f64(remaining_seconds).unwrap_or_default();
153
154 UTCDateTime { date, time }
155 }
156}
157
158impl From<UTCDateTime> for UnixTimestamp {
159 fn from(value: UTCDateTime) -> Self {
160 let mut date_dur = value.date - UNIX_EPOCH.get_gregorian_date();
161 date_dur += Into::<Duration>::into(value.time);
162 Self::from_offset(date_dur)
163 }
164}
165impl From<&UTCDateTime> for UnixTimestamp {
166 fn from(value: &UTCDateTime) -> Self {
167 let mut date_dur = value.date - UNIX_EPOCH.get_gregorian_date();
168 date_dur += Into::<Duration>::into(value.time);
169 Self::from_offset(date_dur)
170 }
171}
172
173impl From<UTCDateTime> for JulianDate {
174 fn from(value: UTCDateTime) -> Self {
175 let mut date: JulianDate = value.date.into();
176 let time: Duration = value.time.into();
177 date += time;
178 date
179 }
180}
181
182impl From<&UTCDateTime> for JulianDate {
183 fn from(value: &UTCDateTime) -> Self {
184 let mut date: JulianDate = value.date.into();
185 let time: Duration = value.time.into();
186 date += time;
187 date
188 }
189}
190
191impl Sub<Self> for UTCDateTime {
192 type Output = Duration;
193
194 fn sub(self, rhs: Self) -> Self::Output {
195 let ts1: JulianDate = self.into();
196 let ts2: JulianDate = rhs.into();
197
198 ts1 - ts2
199 }
200}
201impl Sub<&Self> for UTCDateTime {
202 type Output = Duration;
203
204 fn sub(self, rhs: &Self) -> Self::Output {
205 let ts1: JulianDate = self.into();
206 let ts2: JulianDate = rhs.into();
207
208 ts1 - ts2
209 }
210}
211
212impl Add<Duration> for UTCDateTime {
213 type Output = UTCDateTime;
214
215 fn add(self, rhs: Duration) -> Self::Output {
216 let (time, excess) = self.time.wrapping_add(rhs);
217 let date = self.date + excess;
218 UTCDateTime { date, time }
219 }
220}
221impl Add<&Duration> for UTCDateTime {
222 type Output = UTCDateTime;
223
224 fn add(self, rhs: &Duration) -> Self::Output {
225 let (time, excess) = self.time.wrapping_add(*rhs);
226 let date = self.date + excess;
227 UTCDateTime { date, time }
228 }
229}
230
231impl AddAssign<Duration> for UTCDateTime {
232 fn add_assign(&mut self, rhs: Duration) {
233 let (time, excess) = self.time.wrapping_add(rhs);
234 self.time = time;
235 self.date += excess;
236 }
237}
238impl AddAssign<&Duration> for UTCDateTime {
239 fn add_assign(&mut self, rhs: &Duration) {
240 let (time, excess) = self.time.wrapping_add(*rhs);
241 self.time = time;
242 self.date += excess;
243 }
244}
245
246cfg_feature_serde! {
247 struct UTCDateTimeVisitor;
248 impl serde::de::Visitor<'_> for UTCDateTimeVisitor {
249 type Value = UTCDateTime;
250
251 fn expecting(&self, fmt: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
252 write!(fmt, "The visitor expects to receive a string formatted as a ISO 8601 DateTime")
253 }
254 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: serde::de::Error {
255 UTCDateTime::try_from_iso8601(v).map_err(serde::de::Error::custom)
256 }
257 }
258 impl<'de> serde::Deserialize<'de> for UTCDateTime {
259 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
260 deserializer.deserialize_str(UTCDateTimeVisitor)
261 }
262 }
263 impl serde::Serialize for UTCDateTime {
264 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
265 serializer.serialize_str(&self.format_iso8601_extended())
266 }
267 }
268}
269
270#[cfg(all(test, feature = "serde", feature = "std"))]
271mod tests {
272 use crate::datetime::UTCDateTime;
273 use crate::epoch::UnixTimestamp;
274 use irox_units::units::duration::Duration;
275
276 #[test]
277 #[cfg(all(feature = "serde", feature = "std"))]
278 pub fn serde_test() -> Result<(), crate::FormatError> {
279 #[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Debug)]
280 struct Test {
281 a: UTCDateTime,
282 }
283 impl Default for Test {
284 fn default() -> Self {
285 Self {
286 a: UTCDateTime::default(),
287 }
288 }
289 }
290 let a = Test {
291 a: UnixTimestamp::from_offset(Duration::from_days(1)).into(),
292 };
293 let s = serde_json::to_string(&a).unwrap_or_default();
294 assert_eq!(s, "{\"a\":\"1970-01-02T00:00:00Z\"}");
295 let b: Test = serde_json::from_str(&s).unwrap();
296 assert_eq!(a, b);
297 Ok(())
298 }
299}