deep_time/dt/conveniences.rs
1use crate::ATTOS_PER_WEEK;
2use crate::{ATTOS_PER_SEC_I128, SEC_PER_DAYI64};
3use crate::{Dt, Real, Scale};
4
5impl Dt {
6 /// Returns this [`Dt`] but as a unix timestamp since the UNIX epoch (1970-01-01 00:00:00).
7 ///
8 /// ## Notes:
9 ///
10 /// - Assumes this [`Dt`] is from the 2000-01-01 noon epoch.
11 #[inline(always)]
12 pub const fn to_unix(&self) -> Dt {
13 self.to(self.target)
14 .to_diff_raw(Dt::UNIX_EPOCH.to(self.target))
15 }
16
17 /// Creates a TAI [`Dt`] from a unix (1970 epoch) timestamp.
18 ///
19 /// ## Examples
20 ///
21 /// ```
22 /// use deep_time::{Dt, Scale};
23 ///
24 /// let dt = Dt::from_ymd(1970, 1, 1, 0, 0, 0, 0, Scale::UTC);
25 ///
26 /// let unix = dt.to_unix().to_sec();
27 ///
28 /// assert_eq!(unix, 0);
29 ///
30 /// let roundtrip = Dt::from_unix(Dt::from_tai_sec(unix));
31 ///
32 /// assert_eq!(roundtrip, dt);
33 /// ```
34 #[inline(always)]
35 pub const fn from_unix(unix: Dt) -> Dt {
36 Self::from_diff_and_scale(unix, Dt::UNIX_EPOCH, true)
37 }
38
39 /// Returns this [`Dt`] but as an ntp timestamp since the epoch 1900-01-01 00:00:00 UTC.
40 ///
41 /// ## Notes:
42 ///
43 /// - Assumes this [`Dt`] is from the 2000-01-01 noon epoch.
44 ///
45 /// ## Examples
46 ///
47 /// ```
48 /// use deep_time::{Dt, Scale};
49 ///
50 /// // 2698012800
51 /// let dt = Dt::from_ymd(1985, 7, 1, 0, 0, 0, 0, Scale::TAI);
52 /// let ntp = dt.to_ntp();
53 ///
54 /// assert_eq!(
55 /// ntp.to_attos(), Dt::sec_to_attos(2698012800_i128),
56 /// "ntp sec for 1985 is wrong, got: {}, expected: {}",
57 /// ntp.to_attos(), Dt::sec_to_attos(2698012800_i128)
58 /// );
59 ///
60 /// let dt2 = Dt::from_ntp(ntp);
61 ///
62 /// assert_eq!(
63 /// dt.to_attos(), dt2.to_attos(),
64 /// "round trip to Dt got wrong sec, old: {}, new: {}",
65 /// dt.to_attos(), dt2.to_attos()
66 /// );
67 ///
68 /// let ymd = dt2.to_ymd();
69 /// assert_eq!(ymd.yr(), 1985_i64);
70 /// assert_eq!(ymd.mo(), 7);
71 /// assert_eq!(ymd.day(), 1);
72 /// assert_eq!(ymd.hr(), 0);
73 /// assert_eq!(ymd.min(), 0);
74 /// assert_eq!(ymd.sec(), 0);
75 /// assert_eq!(ymd.attos(), 0);
76 /// ```
77 #[inline(always)]
78 pub const fn to_ntp(&self) -> Dt {
79 self.to(self.target)
80 .to_diff_raw(Dt::NTP_EPOCH.to(self.target))
81 }
82
83 /// Creates a TAI [`Dt`] from an ntp (1900 epoch) timestamp.
84 #[inline(always)]
85 pub const fn from_ntp(ntp: Dt) -> Dt {
86 Self::from_diff_and_scale(ntp, Dt::NTP_EPOCH, true)
87 }
88
89 /// Returns the GPS week number and the exact Time of Week (TOW) for this instant
90 /// when expressed in **GPS Time**.
91 ///
92 /// - GPS Time is continuous (no leap seconds) and starts at the
93 /// [`Dt::GPS_EPOCH`](../struct.Dt.html#associatedconstant.GPS_EPOCH)
94 /// (1980-01-06 00:00:00 UTC).
95 /// - The returned TOW is a [`Dt`] on the TAI scale.
96 ///
97 /// This is the inverse of
98 /// [`Dt::from_gps_wk_and_tow`](../struct.Dt.html#method.from_gps_wk_and_tow).
99 ///
100 /// - `week`: Full GPS week number (can be negative for dates before 1980).
101 /// - `tow`: Time of Week as a [`Dt`]. Values ≥ 604800 seconds are
102 /// automatically carried into the week number.
103 ///
104 /// ## Examples
105 ///
106 /// ```
107 /// use deep_time::{Dt, Scale};
108 ///
109 /// let x = Dt::from_ymd(2000, 1, 1, 12, 0, 0, 0, Scale::TAI);
110 /// let g = x.to_gps_wk_and_tow();
111 /// let z = Dt::from_gps_wk_and_tow(g.0, g.1);
112 /// assert_eq!(x, z);
113 /// ```
114 pub const fn to_gps_wk_and_tow(&self) -> (i64, Dt) {
115 let total_attos = self.to_gps().to_attos();
116 let wk = total_attos.div_euclid(ATTOS_PER_WEEK) as i64;
117 let tow_attos = total_attos.rem_euclid(ATTOS_PER_WEEK);
118 // was converted to target scale, scale is now target
119 (wk, Dt::new(tow_attos, self.target, self.target))
120 }
121
122 /// Creates a [`Dt`] from a GPS week number and Time of Week (TOW).
123 ///
124 /// This is the inverse of
125 /// [`Dt::to_gps_wk_and_tow`](../struct.Dt.html#method.to_gps_wk_and_tow).
126 ///
127 /// - `week`: Full GPS week number (can be negative for dates before 1980).
128 /// - `tow`: Time of Week as a [`Dt`]. Values ≥ 604800 seconds are
129 /// automatically carried into the week number.
130 ///
131 /// ## Examples
132 ///
133 /// ```
134 /// use deep_time::{Dt, Scale};
135 ///
136 /// let x = Dt::from_ymd(2000, 1, 1, 12, 0, 0, 0, Scale::TAI);
137 /// let g = x.to_gps_wk_and_tow();
138 /// let z = Dt::from_gps_wk_and_tow(g.0, g.1);
139 /// assert_eq!(x, z);
140 /// ```
141 #[inline]
142 pub const fn from_gps_wk_and_tow(wk: i64, tow: Dt) -> Dt {
143 let total_attos = (wk as i128)
144 .saturating_mul(ATTOS_PER_WEEK)
145 .saturating_add(tow.to_attos());
146
147 Self::from_gps(Dt::new(total_attos, tow.scale, tow.target))
148 }
149
150 /// Returns the elapsed time since the GPS epoch as a [`Dt`] on the GPS scale.
151 ///
152 /// The GPS epoch is [`Dt::GPS_EPOCH`].
153 #[inline(always)]
154 pub const fn to_gps(&self) -> Dt {
155 self.to_scale_and_diff(Self::GPS_EPOCH, true)
156 }
157
158 /// Inverse of [`Self::to_gps`].
159 #[inline(always)]
160 pub const fn from_gps(elapsed: Dt) -> Dt {
161 Self::from_diff_and_scale(elapsed, Self::GPS_EPOCH, true)
162 }
163
164 /// Returns the day of the GPS week (0 = Sunday, 1 = Monday, …, 6 = Saturday).
165 ///
166 /// This value is computed directly from the GPS Time of Week and is
167 /// independent of the Gregorian calendar or civil time.
168 pub const fn to_gps_day_of_wk(&self) -> u8 {
169 let (_, tow) = self.to_gps_wk_and_tow();
170 let secs = tow.to_attos() / ATTOS_PER_SEC_I128;
171
172 (secs / SEC_PER_DAYI64 as i128) as u8
173 }
174
175 /// Returns the elapsed time since the Chandra X-ray Center (CXC) epoch
176 /// as a [`Dt`] on the TT scale.
177 ///
178 /// The CXC epoch is [`Dt::CXC_EPOCH`].
179 #[inline]
180 pub const fn to_cxcsec(&self) -> Dt {
181 self.to_scale_and_diff(Self::CXC_EPOCH, true)
182 }
183
184 /// Inverse of [`Self::to_cxcsec`].
185 #[inline]
186 pub const fn from_cxcsec(elapsed: Dt) -> Dt {
187 Self::from_diff_and_scale(elapsed, Self::CXC_EPOCH, true)
188 }
189
190 /// Floating-point counterpart of [`Self::from_cxcsec`].
191 #[inline]
192 pub const fn from_cxcsec_f(elapsed_sec: Real) -> Dt {
193 Self::from_cxcsec(Dt::from_sec_f(elapsed_sec, Scale::TAI))
194 }
195
196 /// Returns the elapsed time since the GPS/Galileo Experiment (GALEX) epoch
197 /// as a [`Dt`] on the TAI scale.
198 ///
199 /// The GALEX epoch is [`Self::GPS_EPOCH`].
200 #[inline]
201 pub const fn to_galexsec(&self) -> Dt {
202 self.to_scale_and_diff(Self::GPS_EPOCH, true)
203 }
204
205 /// Inverse of [`Self::to_galexsec`].
206 #[inline]
207 pub const fn from_galexsec(elapsed: Dt) -> Dt {
208 Self::from_diff_and_scale(elapsed, Self::GPS_EPOCH, true)
209 }
210
211 /// Floating-point counterpart of [`Self::from_galexsec`].
212 #[inline]
213 pub const fn from_galexsec_f(elapsed_sec: Real) -> Dt {
214 Self::from_galexsec(Dt::from_sec_f(elapsed_sec, Scale::TAI))
215 }
216}