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