1use core::marker::PhantomData;
11use core::ops::{Add, AddAssign, Sub, SubAssign};
12use irox_units::units::duration::{Duration, DurationUnit};
13
14use crate::epoch::{PrimeEpoch, PRIME_EPOCH, UNIX_EPOCH, VICINTIPOCH, Y2K_EPOCH};
15use crate::{
16 epoch::{Epoch, UnixTimestamp, COMMON_ERA_EPOCH, GREGORIAN_EPOCH},
17 gregorian::Date,
18 SECONDS_IN_DAY,
19};
20
21pub const JULIAN_EPOCH: JulianEpoch = JulianEpoch::new(
24 JULIAN_JD_OFFSET,
25 Epoch(Date {
26 year: -4712,
27 day_of_year: 0,
28 }),
29);
30pub type JulianDate = JulianDayNumber<JulianEpoch>;
35impl JulianDate {
36 pub const fn new(day_number: f64) -> Self {
37 Self {
38 epoch: JULIAN_EPOCH,
39 day_number,
40 _phantom: PhantomData,
41 }
42 }
43}
44
45pub const JULIAN_JD_OFFSET: f64 = 0.0_f64;
46
47pub trait AsJulianEpoch {
48 fn get_julian_epoch() -> JulianEpoch;
49}
50
51#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
55pub struct JulianDayNumber<T: AsJulianEpoch> {
56 epoch: JulianEpoch,
57 day_number: f64,
58
59 _phantom: PhantomData<T>,
60}
61
62impl<T: AsJulianEpoch> JulianDayNumber<T> {
63 pub(crate) const fn new_internal(epoch: JulianEpoch, day_number: f64) -> Self {
64 JulianDayNumber {
65 epoch,
66 day_number,
67 _phantom: PhantomData,
68 }
69 }
70 pub const fn get_day_number(&self) -> f64 {
72 self.day_number
73 }
74 pub const fn get_julian_epoch(&self) -> JulianEpoch {
76 self.epoch
77 }
78
79 pub fn from_jdn<O: AsJulianEpoch>(other: &JulianDayNumber<O>) -> Self {
81 let jdn = other.as_julian_date();
82 let je = T::get_julian_epoch();
83 Self {
84 epoch: je,
85 day_number: jdn.day_number - je.day_offset_from_julian_epoch,
86 _phantom: PhantomData,
87 }
88 }
89
90 pub const fn as_julian_date(&self) -> JulianDate {
92 JulianDate::new_internal(
93 JULIAN_EPOCH,
94 self.day_number + self.epoch.day_offset_from_julian_epoch,
95 )
96 }
97 pub fn as_reduced_date(&self) -> ReducedJulianDate {
99 ReducedJulianDate::from_jdn(self)
100 }
101 pub fn as_modified_date(&self) -> ModifiedJulianDate {
102 ModifiedJulianDate::from_jdn(self)
103 }
104 pub fn as_truncated_date(&self) -> TruncatedJulianDate {
105 TruncatedJulianDate::from_jdn(self)
106 }
107 pub fn as_lilian_date(&self) -> LilianDate {
108 LilianDate::from_jdn(self)
109 }
110 pub fn as_rata_die_date(&self) -> RataDieDate {
111 RataDieDate::from_jdn(self)
112 }
113 pub fn as_prime_date(&self) -> PrimeDate {
114 PrimeDate::from_jdn(self)
115 }
116}
117
118#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
120pub struct JulianEpoch {
121 day_offset_from_julian_epoch: f64,
122 epoch: Epoch,
123}
124impl AsJulianEpoch for JulianEpoch {
125 fn get_julian_epoch() -> JulianEpoch {
126 JULIAN_EPOCH
127 }
128}
129impl Eq for JulianEpoch {}
130impl JulianEpoch {
131 pub const fn new(day_offset_from_julian_epoch: f64, epoch: Epoch) -> Self {
132 JulianEpoch {
133 day_offset_from_julian_epoch,
134 epoch,
135 }
136 }
137 pub fn get_day_offset_from_julian_epoch(&self) -> f64 {
138 self.day_offset_from_julian_epoch
139 }
140 pub fn get_epoch(&self) -> Epoch {
141 self.epoch
142 }
143}
144
145macro_rules! impl_julian {
146 ($date:ident,$epoch:ident) => {
147 impl $date {
148 pub const fn new(day_number: f64) -> Self {
149 Self {
150 epoch: $epoch,
151 day_number,
152 _phantom: PhantomData,
153 }
154 }
155 }
156 impl From<$date> for JulianDate {
162 fn from(value: $date) -> Self {
163 JulianDate::new(value.day_number + $epoch.day_offset_from_julian_epoch)
164 }
165 }
166 impl From<&JulianDate> for $date {
167 fn from(value: &JulianDate) -> Self {
168 $date::new(value.day_number - $epoch.day_offset_from_julian_epoch)
169 }
170 }
171 impl From<&$date> for JulianDate {
172 fn from(value: &$date) -> Self {
173 JulianDate::new(value.day_number + $epoch.day_offset_from_julian_epoch)
174 }
175 }
176 };
177}
178
179#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
181pub struct ReducedJulianEpoch;
182impl AsJulianEpoch for ReducedJulianEpoch {
183 fn get_julian_epoch() -> JulianEpoch {
184 REDUCED_JULIAN_EPOCH
185 }
186}
187pub const REDUCED_JULIAN_EPOCH: JulianEpoch = JulianEpoch::new(
192 REDUCED_JD_OFFSET,
193 Epoch(Date {
194 year: 1858,
195 day_of_year: 320,
196 }),
197);
198
199pub type ReducedJulianDate = JulianDayNumber<ReducedJulianEpoch>;
205
206pub const REDUCED_JD_OFFSET: f64 = 2400000_f64;
208impl_julian!(ReducedJulianDate, REDUCED_JULIAN_EPOCH);
209
210#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
212pub struct ModifiedJulianEpoch;
213impl AsJulianEpoch for ModifiedJulianEpoch {
214 fn get_julian_epoch() -> JulianEpoch {
215 MODIFIED_JULIAN_EPOCH
216 }
217}
218
219pub const MODIFIED_JULIAN_EPOCH: JulianEpoch = JulianEpoch::new(
220 MODIFIED_JD_OFFSET,
221 Epoch(Date {
222 year: 1858,
223 day_of_year: 320,
224 }),
225);
226
227pub type ModifiedJulianDate = JulianDayNumber<ModifiedJulianEpoch>;
233
234pub const MODIFIED_JD_OFFSET: f64 = 2400000.5_f64;
236impl_julian!(ModifiedJulianDate, MODIFIED_JULIAN_EPOCH);
237
238#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
240pub struct TruncatedJulianEpoch;
241impl AsJulianEpoch for TruncatedJulianEpoch {
242 fn get_julian_epoch() -> JulianEpoch {
243 TRUNCATED_JULIAN_EPOCH
244 }
245}
246pub const TRUNCATED_JULIAN_EPOCH: JulianEpoch = JulianEpoch::new(
251 TRUNCATED_JD_OFFSET,
252 Epoch(Date {
253 year: 1968,
254 day_of_year: 145,
255 }),
256);
257
258pub type TruncatedJulianDate = JulianDayNumber<TruncatedJulianEpoch>;
264
265pub const TRUNCATED_JD_OFFSET: f64 = 2440000.5_f64;
267impl_julian!(TruncatedJulianDate, TRUNCATED_JULIAN_EPOCH);
268
269#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
271pub struct LilianEpoch;
272impl AsJulianEpoch for LilianEpoch {
273 fn get_julian_epoch() -> JulianEpoch {
274 LILIAN_EPOCH
275 }
276}
277
278pub type LilianDate = JulianDayNumber<LilianEpoch>;
284
285pub const LILIAN_JD_OFFSET: f64 = 2299159.5_f64;
287
288pub const LILIAN_EPOCH: JulianEpoch = JulianEpoch::new(LILIAN_JD_OFFSET, GREGORIAN_EPOCH);
289impl_julian!(LilianDate, LILIAN_EPOCH);
290
291#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
293pub struct RataDieEpoch;
294impl AsJulianEpoch for RataDieEpoch {
295 fn get_julian_epoch() -> JulianEpoch {
296 RATA_DIE_EPOCH
297 }
298}
299
300pub type RataDieDate = JulianDayNumber<RataDieEpoch>;
304
305pub const RATA_DIE_JD_OFFSET: f64 = 1721424.5_f64;
307pub const RATA_DIE_EPOCH: JulianEpoch = JulianEpoch::new(RATA_DIE_JD_OFFSET, COMMON_ERA_EPOCH);
308impl_julian!(RataDieDate, RATA_DIE_EPOCH);
309
310pub const UNIX_TS_JD_OFFSET: f64 = 2240587.5_f64;
312pub const UNIX_JD_EPOCH: JulianEpoch = JulianEpoch::new(UNIX_TS_JD_OFFSET, UNIX_EPOCH);
313
314pub type PrimeDate = JulianDayNumber<PrimeEpoch>;
317pub const PRIME_JD_OFFSET: f64 = 2415020.5_f64;
318pub const PRIME_JD_EPOCH: JulianEpoch = JulianEpoch::new(PRIME_JD_OFFSET, PRIME_EPOCH);
319impl AsJulianEpoch for PrimeEpoch {
320 fn get_julian_epoch() -> JulianEpoch {
321 PRIME_JD_EPOCH
322 }
323}
324impl_julian!(PrimeDate, PRIME_JD_EPOCH);
325
326pub const MJD2000_OFFSET: f64 = 2451544.5;
327pub const MJD2000_EPOCH: JulianEpoch = JulianEpoch::new(MJD2000_OFFSET, Y2K_EPOCH);
328pub struct MJD2000Epoch;
329impl AsJulianEpoch for MJD2000Epoch {
330 fn get_julian_epoch() -> JulianEpoch {
331 MJD2000_EPOCH
332 }
333}
334pub type MJD2000 = JulianDayNumber<MJD2000Epoch>;
335impl_julian!(MJD2000, MJD2000_EPOCH);
336
337pub const VICINTI_OFFSET: f64 = 2458849.5;
338pub const VICINTI_JD_EPOCH: JulianEpoch = JulianEpoch::new(VICINTI_OFFSET, VICINTIPOCH);
339
340impl<T: AsJulianEpoch> Add<Duration> for JulianDayNumber<T> {
341 type Output = JulianDayNumber<T>;
342
343 fn add(self, rhs: Duration) -> Self::Output {
344 let day_number = self.day_number + rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
345 Self::new_internal(self.epoch, day_number)
346 }
347}
348
349impl<T: AsJulianEpoch> Add<&Duration> for JulianDayNumber<T> {
350 type Output = JulianDayNumber<T>;
351
352 fn add(self, rhs: &Duration) -> Self::Output {
353 let day_number = self.day_number + rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
354 Self::new_internal(self.epoch, day_number)
355 }
356}
357
358impl<T: AsJulianEpoch> Sub<Duration> for JulianDayNumber<T> {
359 type Output = JulianDayNumber<T>;
360
361 fn sub(self, rhs: Duration) -> Self::Output {
362 let day_number = self.day_number - rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
363 Self::new_internal(self.epoch, day_number)
364 }
365}
366
367impl<T: AsJulianEpoch> Sub<&Duration> for JulianDayNumber<T> {
368 type Output = JulianDayNumber<T>;
369
370 fn sub(self, rhs: &Duration) -> Self::Output {
371 let day_number = self.day_number - rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
372 Self::new_internal(self.epoch, day_number)
373 }
374}
375
376impl<T: AsJulianEpoch> AddAssign<Duration> for JulianDayNumber<T> {
377 fn add_assign(&mut self, rhs: Duration) {
378 self.day_number += rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
379 }
380}
381
382impl<T: AsJulianEpoch> AddAssign<&Duration> for JulianDayNumber<T> {
383 fn add_assign(&mut self, rhs: &Duration) {
384 self.day_number += rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
385 }
386}
387
388impl<T: AsJulianEpoch> SubAssign<Duration> for JulianDayNumber<T> {
389 fn sub_assign(&mut self, rhs: Duration) {
390 self.day_number -= rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
391 }
392}
393
394impl<T: AsJulianEpoch> SubAssign<&Duration> for JulianDayNumber<T> {
395 fn sub_assign(&mut self, rhs: &Duration) {
396 self.day_number -= rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
397 }
398}
399
400impl<T: AsJulianEpoch> Sub<JulianDayNumber<T>> for JulianDayNumber<T> {
401 type Output = Duration;
402
403 fn sub(self, rhs: JulianDayNumber<T>) -> Self::Output {
404 let dx = self.day_number - rhs.day_number;
405 Duration::new(dx, DurationUnit::Day)
406 }
407}
408impl<T: AsJulianEpoch> Sub<&JulianDayNumber<T>> for JulianDayNumber<T> {
409 type Output = Duration;
410
411 fn sub(self, rhs: &JulianDayNumber<T>) -> Self::Output {
412 let dx = self.day_number - rhs.day_number;
413 Duration::new(dx, DurationUnit::Day)
414 }
415}
416
417impl From<UnixTimestamp> for JulianDate {
418 fn from(value: UnixTimestamp) -> Self {
419 let jd = value.get_offset().as_seconds_f64() / SECONDS_IN_DAY as f64 + UNIX_TS_JD_OFFSET;
420 JulianDate::new(jd)
421 }
422}
423
424impl From<JulianDate> for UnixTimestamp {
425 fn from(value: JulianDate) -> Self {
426 let ts = (value.day_number - UNIX_TS_JD_OFFSET) * SECONDS_IN_DAY as f64;
427 UnixTimestamp::from_seconds_f64(ts)
428 }
429}