1mod arithmetic;
2mod constructors;
3mod conversions;
4mod conversions_lunar;
5mod conversions_mars;
6mod decimal_year;
7mod from_ccsds;
8mod from_gps;
9mod from_str;
10mod gregorian;
11mod julian_date;
12mod ops;
13mod to_ccsds_bin;
14mod to_gps;
15mod to_str;
16
17pub mod time_units;
18pub mod trajectory;
19
20#[cfg(feature = "alloc")]
21mod formatting;
22#[cfg(feature = "alloc")]
23mod to_ccsds_str;
24
25#[cfg(feature = "hifitime")]
26mod hifitime;
27
28#[cfg(feature = "chrono")]
29mod chrono;
30
31#[cfg(feature = "jiff")]
32mod jiff;
33
34use crate::ATTOS_PER_SEC;
35use core::fmt;
36
37#[derive(Clone, Copy)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43#[cfg_attr(feature = "js", derive(tsify::Tsify))]
44pub struct Dt {
45 pub(crate) sec: i64,
46 pub(crate) attos: u64,
47}
48
49impl Dt {
50 #[inline]
52 pub const fn sec(&self) -> i64 {
53 self.sec
54 }
55
56 #[inline]
58 pub const fn attos(&self) -> u64 {
59 self.attos
60 }
61
62 #[inline]
64 pub const fn carry_over(&mut self) -> &mut Self {
65 if self.attos >= ATTOS_PER_SEC {
66 self.sec = self.sec.saturating_add((self.attos / ATTOS_PER_SEC) as i64);
67 self.attos %= ATTOS_PER_SEC;
68 }
69 self
70 }
71}
72
73impl Default for Dt {
74 fn default() -> Self {
75 Self::ZERO
76 }
77}
78
79impl fmt::Display for Dt {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 let sec = self.sec();
82 let attos = self.attos();
83
84 let precision = f.precision().unwrap_or(9);
86
87 if f.sign_plus() && sec >= 0 {
89 write!(f, "+")?;
90 }
91
92 write!(f, "{}", sec)?;
93
94 if precision > 0 {
95 let prec = precision.min(18);
96 let scale = 10u64.pow(18 - prec as u32);
97 let value = attos / scale;
98 write!(f, ".{:0>width$}", value, width = prec)?;
99 }
100
101 Ok(())
102 }
103}
104
105impl fmt::Debug for Dt {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 f.debug_struct("Dt")
108 .field("sec", &self.sec())
109 .field("attos", &self.attos())
110 .finish()
111 }
112}
113
114#[cfg(feature = "wire")]
115impl Dt {
116 pub const WIRE_VERSION: u8 = 1;
118
119 pub const WIRE_SIZE: usize = 17;
121
122 pub fn to_wire_bytes(&self) -> [u8; Self::WIRE_SIZE] {
133 let mut buf = [0u8; Self::WIRE_SIZE];
134 buf[0] = Self::WIRE_VERSION;
135 buf[1..9].copy_from_slice(&self.sec.to_le_bytes());
136 buf[9..17].copy_from_slice(&self.attos.to_le_bytes());
137 buf
138 }
139
140 pub fn from_wire_bytes(bytes: &[u8]) -> Option<Self> {
153 if bytes.len() != Self::WIRE_SIZE {
154 return None;
155 }
156
157 if bytes[0] != Self::WIRE_VERSION {
158 return None;
159 }
160
161 let sec = i64::from_le_bytes([
162 bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8],
163 ]);
164 let subsec = u64::from_le_bytes([
165 bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16],
166 ]);
167
168 Some(Self::new(sec, subsec))
169 }
170}