1mod arithmetic;
2mod constructors;
3mod conversions;
4mod decimal_year;
5mod from_ccsds;
6mod from_gps;
7mod from_str;
8mod gregorian;
9mod julian_date;
10mod ops;
11mod tdb;
12mod to_ccsds_bin;
13mod to_gps;
14mod to_str;
15
16pub mod lunar;
17pub mod numbers_traits;
18pub mod trajectory;
19
20#[cfg(feature = "alloc")]
21mod formatting;
22#[cfg(feature = "alloc")]
23mod to_ccsds_str;
24
25#[cfg(feature = "mars")]
26pub mod mars;
27
28#[cfg(feature = "hifitime")]
29mod hifitime;
30
31#[cfg(feature = "chrono")]
32mod chrono;
33
34#[cfg(feature = "jiff")]
35mod jiff;
36
37use crate::ATTOS_PER_SEC;
38use core::fmt;
39
40#[derive(Clone, Copy)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46#[cfg_attr(feature = "js", derive(tsify::Tsify))]
47pub struct Dt {
48 pub(crate) sec: i64,
49 pub(crate) attos: u64,
50}
51
52impl Dt {
53 #[inline]
55 pub const fn sec(&self) -> i64 {
56 self.sec
57 }
58
59 #[inline]
61 pub const fn attos(&self) -> u64 {
62 self.attos
63 }
64
65 #[inline]
67 pub const fn carry_over_mut(&mut self) -> &mut Self {
68 if self.attos >= ATTOS_PER_SEC {
69 self.sec = self.sec.saturating_add((self.attos / ATTOS_PER_SEC) as i64);
70 self.attos %= ATTOS_PER_SEC;
71 }
72 self
73 }
74
75 #[inline]
77 pub const fn carry_over(&self) -> Self {
78 if self.attos < ATTOS_PER_SEC {
79 return *self;
80 }
81 Self {
82 sec: self.sec.saturating_add((self.attos / ATTOS_PER_SEC) as i64),
83 attos: self.attos % ATTOS_PER_SEC,
84 }
85 }
86}
87
88impl Default for Dt {
89 fn default() -> Self {
90 Self::ZERO
91 }
92}
93
94impl fmt::Display for Dt {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 let sec = self.sec();
97 let attos = self.attos();
98
99 let precision = f.precision().unwrap_or(9);
101
102 if f.sign_plus() && sec >= 0 {
104 write!(f, "+")?;
105 }
106
107 write!(f, "{}", sec)?;
108
109 if precision > 0 {
110 let prec = precision.min(18);
111 let scale = 10u64.pow(18 - prec as u32);
112 let value = attos / scale;
113 write!(f, ".{:0>width$}", value, width = prec)?;
114 }
115
116 Ok(())
117 }
118}
119
120impl fmt::Debug for Dt {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.debug_struct("Dt")
123 .field("sec", &self.sec())
124 .field("attos", &self.attos())
125 .finish()
126 }
127}
128
129#[cfg(feature = "wire")]
130impl Dt {
131 pub const WIRE_VERSION: u8 = 1;
133
134 pub const WIRE_SIZE: usize = 17;
136
137 pub fn to_wire_bytes(&self) -> [u8; Self::WIRE_SIZE] {
148 let mut buf = [0u8; Self::WIRE_SIZE];
149 buf[0] = Self::WIRE_VERSION;
150 buf[1..9].copy_from_slice(&self.sec.to_le_bytes());
151 buf[9..17].copy_from_slice(&self.attos.to_le_bytes());
152 buf
153 }
154
155 pub fn from_wire_bytes(bytes: &[u8]) -> Option<Self> {
168 if bytes.len() != Self::WIRE_SIZE {
169 return None;
170 }
171
172 if bytes[0] != Self::WIRE_VERSION {
173 return None;
174 }
175
176 let sec = i64::from_le_bytes([
177 bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8],
178 ]);
179 let subsec = u64::from_le_bytes([
180 bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16],
181 ]);
182
183 Some(Self::new(sec, subsec))
184 }
185}