1use core::cmp::Ordering;
30use core::marker::PhantomData;
31use core::ops::{Add, AddAssign, Sub, SubAssign};
32use irox_tools::cfg_docs;
33use irox_units::units::duration::Duration;
34
35use crate::gregorian::Date;
36use crate::julian::JulianDate;
37
38cfg_docs! {
39 use crate::julian::{JULIAN_EPOCH, RATA_DIE_EPOCH, LILIAN_EPOCH, REDUCED_JULIAN_EPOCH, MODIFIED_JULIAN_EPOCH, PRIME_JD_EPOCH, TRUNCATED_JULIAN_EPOCH, UNIX_JD_EPOCH, MJD2000_EPOCH, VICINTI_JD_EPOCH};
40}
41
42#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
45pub struct Epoch(pub Date);
46
47impl Epoch {
48 pub fn get_gregorian_date(&self) -> Date {
51 self.0
52 }
53}
54
55#[derive(Debug, Copy, Clone)]
58pub struct Timestamp<T> {
59 epoch: Epoch,
60 offset: Duration,
61
62 _phantom: PhantomData<T>,
63}
64
65impl<T> Timestamp<T> {
66 pub const fn new(epoch: Epoch, duration: Duration) -> Self {
67 Self {
68 epoch,
69 offset: duration,
70 _phantom: PhantomData,
71 }
72 }
73
74 #[must_use]
77 pub const fn get_epoch(&self) -> Epoch {
78 self.epoch
79 }
80
81 #[must_use]
84 pub const fn get_offset(&self) -> Duration {
85 self.offset
86 }
87}
88impl<T> PartialEq for Timestamp<T> {
89 fn eq(&self, other: &Self) -> bool {
90 if self.epoch != other.epoch {
91 return false;
92 }
93 self.offset.eq(&other.offset)
94 }
95}
96impl<T> Eq for Timestamp<T> {}
97
98impl<T> PartialOrd for Timestamp<T> {
99 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
100 Some(self.offset.cmp(&other.offset))
101 }
102}
103impl<T> Ord for Timestamp<T> {
104 fn cmp(&self, other: &Self) -> Ordering {
105 self.offset.cmp(&other.offset)
106 }
107}
108
109pub const UNIX_EPOCH: Epoch = Epoch(Date {
112 year: 1970,
113 day_of_year: 0,
114});
115
116pub type UnixTimestamp = Timestamp<UnixEpoch>;
119
120#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
122pub struct UnixEpoch;
123
124pub trait FromTimestamp<T> {
125 fn from_timestamp(other: &Timestamp<T>) -> Self;
126}
127
128macro_rules! derive_timestamp_impl {
129 ($epoch:ident,$name:ident) => {
130 impl $name {
131 pub const fn from_offset(offset: Duration) -> $name {
134 $name::new($epoch, offset)
135 }
136
137 pub const fn from_seconds(seconds: u32) -> $name {
140 $name::from_seconds_f64(seconds as f64)
141 }
142
143 pub const fn from_seconds_f64(seconds: f64) -> $name {
146 $name::from_offset(Duration::new_seconds(seconds))
147 }
148 }
149 impl Default for $name {
150 fn default() -> Self {
151 $name::from_offset(Duration::default())
152 }
153 }
154 impl From<Duration> for $name {
155 fn from(value: Duration) -> Self {
156 $name::from_offset(value)
157 }
158 }
159
160 impl<T> FromTimestamp<T> for $name {
161 fn from_timestamp(other: &Timestamp<T>) -> Self {
162 let epoch_offset = $epoch.0 - other.epoch.0;
163 let new_duration = other.offset - epoch_offset;
164 $name::from_offset(new_duration)
165 }
166 }
167 };
168}
169
170impl<T> AddAssign<Duration> for Timestamp<T> {
171 fn add_assign(&mut self, rhs: Duration) {
172 self.offset += rhs;
173 }
174}
175
176impl<T> SubAssign<Duration> for Timestamp<T> {
177 fn sub_assign(&mut self, rhs: Duration) {
178 self.offset -= rhs;
179 }
180}
181
182impl<T> AddAssign<&Duration> for Timestamp<T> {
183 fn add_assign(&mut self, rhs: &Duration) {
184 self.offset += *rhs;
185 }
186}
187
188impl<T> SubAssign<&Duration> for Timestamp<T> {
189 fn sub_assign(&mut self, rhs: &Duration) {
190 self.offset -= *rhs;
191 }
192}
193impl<T> SubAssign<&mut Duration> for Timestamp<T> {
194 fn sub_assign(&mut self, rhs: &mut Duration) {
195 self.offset -= *rhs;
196 }
197}
198macro_rules! impl_sub_timestamp {
199 ($($sub:ty)+, $($slf:ty)+) => {
200 impl<T> Sub<$($sub)+> for $($slf)+ {
201 type Output = Duration;
202
203 fn sub(self, rhs: $($sub)+) -> Self::Output {
204 self.offset - rhs.offset
205 }
206 }
207 };
208}
209macro_rules! impl_sub_duration {
210 ($($sub:ty)+, $($slf:ty)+) => {
211 impl<T> Sub<$($sub)+> for $($slf)+ {
212 type Output = Timestamp<T>;
213
214 fn sub(self, rhs: $($sub)+) -> Self::Output {
215 let offset = self.offset - rhs;
216 Timestamp::new(self.epoch, offset)
217 }
218 }
219 };
220}
221macro_rules! impl_add_timestamp {
222 ($($sub:ty)+, $($slf:ty)+) => {
223 impl<T> Add<$($sub)+> for $($slf)+ {
224 type Output = Timestamp<T>;
225
226 fn add(self, rhs: $($sub)+) -> Self::Output {
227 let offset = self.offset + rhs;
228 Timestamp::new(self.epoch, offset)
229 }
230 }
231 };
232}
233macro_rules! impl_op {
234 ($op:ident, $($operand:ty)+) => {
235 $op!($($operand)+, Timestamp<T>);
236 $op!($($operand)+, &Timestamp<T>);
237 $op!($($operand)+, &mut Timestamp<T>);
238 $op!(&$($operand)+, Timestamp<T>);
239 $op!(&$($operand)+, &Timestamp<T>);
240 $op!(&$($operand)+, &mut Timestamp<T>);
241 $op!(&mut $($operand)+, Timestamp<T>);
242 $op!(&mut $($operand)+, &Timestamp<T>);
243 $op!(&mut $($operand)+, &mut Timestamp<T>);
244 };
245}
246impl_op!(impl_sub_timestamp, Timestamp<T>);
247impl_op!(impl_add_timestamp, Duration);
248impl_op!(impl_sub_duration, Duration);
249
250impl UnixTimestamp {
251 #[must_use]
254 #[cfg(feature = "std")]
255 pub fn now() -> UnixTimestamp {
256 match std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
257 Ok(t) => UnixTimestamp::from_offset(t.into()),
258 Err(t) => {
259 UnixTimestamp::from_offset(Duration::new_seconds(-1.0 * t.duration().as_secs_f64()))
260 }
261 }
262 }
263
264 #[must_use]
268 #[cfg(feature = "std")]
269 pub fn elapsed(&self) -> Duration {
270 Self::now().offset - self.offset
271 }
272
273 #[must_use]
276 pub fn as_date(&self) -> Date {
277 self.into()
278 }
279
280 #[must_use]
281 pub fn as_julian(&self) -> JulianDate {
282 (*self).into()
283 }
284}
285derive_timestamp_impl!(UNIX_EPOCH, UnixTimestamp);
286
287pub const GPS_EPOCH: Epoch = Epoch(Date {
290 year: 1980,
291 day_of_year: 5,
292});
293
294pub type GPSTimestamp = Timestamp<GPSEpoch>;
297
298#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
300pub struct GPSEpoch;
301derive_timestamp_impl!(GPS_EPOCH, GPSTimestamp);
302
303pub const GREGORIAN_EPOCH: Epoch = Epoch(Date {
306 year: 1582,
307 day_of_year: 287,
308});
309
310pub type GregorianTimestamp = Timestamp<GregorianEpoch>;
313
314#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
316pub struct GregorianEpoch;
317derive_timestamp_impl!(GREGORIAN_EPOCH, GregorianTimestamp);
318
319pub const WINDOWS_NT_EPOCH: Epoch = Epoch(Date {
327 year: 1601,
328 day_of_year: 0,
329});
330
331pub type WindowsNTTimestamp = Timestamp<WindowsEpoch>;
337
338#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
340pub struct WindowsEpoch;
341derive_timestamp_impl!(WINDOWS_NT_EPOCH, WindowsNTTimestamp);
342
343pub const COMMON_ERA_EPOCH: Epoch = Epoch(Date {
346 year: 1,
347 day_of_year: 0,
348});
349
350pub const PRIME_EPOCH: Epoch = Epoch(Date {
353 year: 1900,
354 day_of_year: 0,
355});
356pub type PrimeTimestamp = Timestamp<PrimeEpoch>;
362#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
364pub struct PrimeEpoch;
365derive_timestamp_impl!(PRIME_EPOCH, PrimeTimestamp);
366pub const NTP_EPOCH: Epoch = PRIME_EPOCH;
369
370pub const LEAPOCH_TIMESTAMP: UnixTimestamp = UnixTimestamp::from_seconds(951868800);
372pub const LEAPOCH: Epoch = Epoch(Date {
374 year: 2000,
375 day_of_year: 60,
376});
377#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
379pub struct Leapoch;
380
381pub type LeapochTimestamp = Timestamp<Leapoch>;
384derive_timestamp_impl!(LEAPOCH, LeapochTimestamp);
385
386pub const JULIAN_DAY_1_JAN_YR0: f64 = 1721059.5;
387
388pub const Y2K_EPOCH: Epoch = Epoch(Date {
390 year: 2000,
391 day_of_year: 0,
392});
393
394pub const VICINTIPOCH: Epoch = Epoch(Date {
396 year: 2020,
397 day_of_year: 0,
398});
399#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
401pub struct Vicintipoch;
402pub type VicintiTimestamp = Timestamp<Vicintipoch>;
403derive_timestamp_impl!(VICINTIPOCH, VicintiTimestamp);