1mod arithmetic;
2mod constructors;
3mod conversions;
4mod epoch;
5mod from_ccsds;
6mod from_gps;
7mod from_str;
8mod gregorian;
9mod julian_date;
10mod ops;
11mod to_ccsds_bin;
12mod to_gps;
13mod to_str;
14mod trajectory;
15
16#[cfg(feature = "alloc")]
17mod formatting;
18#[cfg(feature = "alloc")]
19mod to_ccsds_str;
20
21#[cfg(feature = "hifitime")]
22mod from_hifitime;
23#[cfg(feature = "hifitime")]
24mod to_hifitime;
25
26#[cfg(feature = "chrono")]
27mod from_chrono;
28#[cfg(feature = "chrono")]
29mod to_chrono;
30
31#[cfg(feature = "jiff")]
32mod from_jiff;
33#[cfg(feature = "jiff")]
34mod to_jiff;
35
36use crate::ATTOS_PER_SEC;
37use core::fmt;
38
39#[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 self) -> &mut Self {
68 if self.attos >= ATTOS_PER_SEC {
69 self.sec += (self.attos / ATTOS_PER_SEC) as i64;
70 self.attos %= ATTOS_PER_SEC;
71 }
72 self
73 }
74}
75
76impl Default for Dt {
77 fn default() -> Self {
78 Self::ZERO
79 }
80}
81
82impl fmt::Display for Dt {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 let sec = self.sec();
85 let attos = self.attos();
86
87 let precision = f.precision().unwrap_or(9);
89
90 if f.sign_plus() && sec >= 0 {
92 write!(f, "+")?;
93 }
94
95 write!(f, "{}", sec)?;
96
97 if precision > 0 {
98 let prec = precision.min(18);
99 let scale = 10u64.pow(18 - prec as u32);
100 let value = attos / scale;
101 write!(f, ".{:0>width$}", value, width = prec)?;
102 }
103
104 Ok(())
105 }
106}
107
108impl fmt::Debug for Dt {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 let approx_sec = self.sec() as f64 + (self.attos() as f64 / 1_000_000_000_000_000_000.0);
111
112 f.debug_struct("Dt")
113 .field("sec", &self.sec())
114 .field("attos", &self.attos())
115 .field("approx_sec", &approx_sec)
116 .finish()
117 }
118}
119
120#[cfg(feature = "wire")]
121impl Dt {
122 pub const WIRE_VERSION: u8 = 1;
124
125 pub const WIRE_SIZE: usize = 17;
127
128 pub fn to_wire_bytes(&self) -> [u8; Self::WIRE_SIZE] {
139 let mut buf = [0u8; Self::WIRE_SIZE];
140 buf[0] = Self::WIRE_VERSION;
141 buf[1..9].copy_from_slice(&self.sec.to_le_bytes());
142 buf[9..17].copy_from_slice(&self.attos.to_le_bytes());
143 buf
144 }
145
146 pub fn from_wire_bytes(bytes: &[u8]) -> Option<Self> {
159 if bytes.len() != Self::WIRE_SIZE {
160 return None;
161 }
162
163 if bytes[0] != Self::WIRE_VERSION {
164 return None;
165 }
166
167 let sec = i64::from_le_bytes([
168 bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8],
169 ]);
170 let subsec = u64::from_le_bytes([
171 bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16],
172 ]);
173
174 Some(Self::new(sec, subsec))
175 }
176}