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