1use crate::historical_sofa::historical_sofa_offset_for_non_adjusted;
2use crate::{
3 ClockModel, Drift, Dt, LB_DEN, LB_NUM, LG_DEN, LG_NUM, Scale, TAI_SEC_AT_1972,
4 TCG_TCB_REF_ATTOS_SINCE_J2000, TDB0_ATTOS, TT_TAI_OFFSET,
5};
6
7impl Dt {
8 #[inline]
9 pub const fn from_dt(dt: Dt, scale: Scale) -> Dt {
10 Self::from(dt.sec, dt.attos, scale)
11 }
12
13 #[inline]
14 pub const fn from_attos_since(attos: i128, reference: Dt) -> Self {
15 reference.add(Dt::from_attos(attos, Scale::TAI))
16 }
17
18 #[inline]
19 pub const fn to_scale_and_then_diff(&self, scale: Scale, epoch: Dt) -> Dt {
20 self.to_internal(scale).to_diff_raw(epoch)
21 }
22
23 #[inline]
24 pub const fn from_diff_and_scale(diff: Dt, epoch: Dt, current: Scale) -> Self {
25 Dt::from_dt(epoch.add(diff), current)
26 }
27
28 pub const fn from(sec: i64, attos: u64, scale: Scale) -> Dt {
35 let raw = Dt::new(sec, attos);
36 match scale {
37 Scale::UTC => raw.add(Dt {
38 sec: raw.leap_seconds(true).offset,
39 attos: 0,
40 }),
41 Scale::TAI => raw,
42 Scale::TT => raw.sub(TT_TAI_OFFSET),
43 Scale::UTCSpice => {
44 let tai = raw.add(Dt {
45 sec: raw.leap_seconds(true).offset,
46 attos: 0,
47 });
48 if sec < TAI_SEC_AT_1972 - 10 {
49 tai.add(Dt::from_sec(9, Scale::TAI))
50 } else {
51 tai
52 }
53 }
54 Scale::UTCSofa => {
55 let tai = raw.add(Dt {
56 sec: raw.leap_seconds(true).offset,
57 attos: 0,
58 });
59 if let Some(offset) = historical_sofa_offset_for_non_adjusted(&raw) {
60 tai.add(Dt::from_sec_f(offset))
61 } else {
62 tai
63 }
64 }
65 Scale::GPS | Scale::QZSS | Scale::GST => raw.add(Dt::SEC_19),
66 Scale::BDT => raw.add(Dt::SEC_33),
67 Scale::TDB | Scale::ET => Self::tdb_to_tai(raw),
68 Scale::TCG => {
69 let tt = Self::tcg_to_tt(raw);
70 tt.sub(TT_TAI_OFFSET)
71 }
72 Scale::TCB => {
73 let tdb = Self::tcb_to_tdb(raw);
74 Self::tdb_to_tai(tdb)
75 }
76 Scale::LTC => {
77 let tt = Self::ltc_to_tt(raw);
78 tt.sub(TT_TAI_OFFSET)
79 }
80 Scale::TCL => Self::tcl_to_tai(raw),
81 _ => raw,
82 }
83 }
84
85 pub(crate) const fn to_internal(&self, scale: Scale) -> Dt {
86 match scale {
87 Scale::TAI | Scale::Custom | Scale::UT1 => *self,
88 Scale::TT => self.add(TT_TAI_OFFSET),
89 Scale::UTC => self.sub(Dt {
90 sec: self.leap_seconds(false).offset,
91 attos: 0,
92 }),
93 Scale::UTCSpice => {
94 let spice = self.sub(Dt {
95 sec: self.leap_seconds(false).offset,
96 attos: 0,
97 });
98 if self.sec < TAI_SEC_AT_1972 {
99 spice.sub(Dt::from_sec_f(f!(9.0)))
100 } else {
101 spice
102 }
103 }
104 Scale::UTCSofa => {
105 let sofa = self.sub(Dt {
106 sec: self.leap_seconds(false).offset,
107 attos: 0,
108 });
109 if let Some(offset) = historical_sofa_offset_for_non_adjusted(self) {
110 sofa.sub(Dt::from_sec_f(offset))
111 } else {
112 sofa
113 }
114 }
115 Scale::GPS | Scale::QZSS | Scale::GST => self.sub(Dt::SEC_19),
116 Scale::BDT => self.sub(Dt::SEC_33),
117 Scale::TDB | Scale::ET => Self::tai_to_tdb(*self),
118 Scale::TCG => Self::tai_to_tcg(*self),
119 Scale::TCB => Self::tai_to_tcb(*self),
120 Scale::LTC => {
121 let tt = self.add(TT_TAI_OFFSET);
122 Self::tt_to_ltc(tt)
123 }
124 Scale::TCL => Self::tai_to_tcl(*self),
125 }
126 }
127
128 #[inline]
129 pub const fn to_tai(&self, current: Scale) -> Dt {
130 Self::from(self.sec, self.attos, current)
131 }
132
133 #[inline]
134 pub const fn to(&self, current: Scale, target: Scale) -> Dt {
135 if !current.eq(target) {
136 Self::from(self.sec, self.attos, current).to_internal(target)
137 } else {
138 *self
139 }
140 }
141
142 #[inline]
145 pub const fn convert_using_drift(self, reference: Self, drift: Drift) -> Self {
146 let span = self.to_diff_raw(reference);
147 let correction = drift.time_diff_after(&span);
148 self.add(correction)
149 }
150
151 pub const fn convert_back_using_drift(self, reference: Self, drift: Drift) -> Self {
157 if drift.rate().is_zero() && drift.accel().is_zero() {
158 return self.sub(*drift.constant());
159 }
160 let mut guess = self;
161 let mut i = 0u32;
162 while i < 16 {
163 let span = guess.to_diff_raw(reference);
164 let correction = drift.time_diff_after(&span);
165 guess = self.sub(correction);
166 i += 1;
167 }
168 guess
169 }
170
171 #[inline]
173 pub const fn convert_using_model(self, model: ClockModel) -> Self {
174 self.convert_using_drift(model.reference, model.drift)
175 }
176
177 #[inline]
179 pub const fn convert_back_using_model(self, model: ClockModel) -> Self {
180 self.convert_back_using_drift(model.reference, model.drift)
181 }
182
183 pub(crate) const fn tai_to_tcg(tai: Self) -> Self {
184 let tt = tai.add(TT_TAI_OFFSET);
185 Self::tt_to_tcg(tt)
186 }
187
188 pub(crate) const fn tai_to_tcb(tai: Self) -> Self {
189 let tdb = Self::tai_to_tdb(tai);
190 Self::tdb_to_tcb(tdb)
191 }
192
193 #[inline]
196 pub(crate) const fn to_attos_since_tcg_tcb_epoch(numerical: Self) -> i128 {
197 numerical.to_attos() - TCG_TCB_REF_ATTOS_SINCE_J2000
198 }
199
200 pub(crate) const fn mul_rate(attos: i128, num: i128, den: i128) -> i128 {
202 if attos == 0 {
203 return 0;
204 }
205 let sign = if attos < 0 { -1i128 } else { 1i128 };
206 let a = if attos < 0 { -attos } else { attos };
207 let q = a / den;
208 let r = a % den;
209 sign * (q * num + (r * num) / den)
210 }
211
212 #[inline]
213 pub(crate) const fn mul_lg(attos: i128) -> i128 {
214 Self::mul_rate(attos, LG_NUM, LG_DEN)
215 }
216
217 #[inline]
218 pub(crate) const fn mul_lb(attos: i128) -> i128 {
219 Self::mul_rate(attos, LB_NUM, LB_DEN)
220 }
221
222 pub(crate) const fn tt_to_tcg(tt: Self) -> Self {
223 let elapsed = Self::to_attos_since_tcg_tcb_epoch(tt);
224 let span_attos = Self::mul_lg(elapsed);
225 tt.add(Dt::from_attos(span_attos, Scale::TAI))
226 }
227
228 pub(crate) const fn tcg_to_tt(tcg: Self) -> Self {
229 let elapsed_cg = Self::to_attos_since_tcg_tcb_epoch(tcg);
230 let span_attos = Self::mul_rate(elapsed_cg, LG_NUM, LG_DEN + LG_NUM);
231 tcg.sub(Dt::from_attos(span_attos, Scale::TAI))
232 }
233
234 pub(crate) const fn tcb_to_tdb(tcb: Self) -> Self {
235 let elapsed_cg = Self::to_attos_since_tcg_tcb_epoch(tcb);
236 let span_attos = Self::mul_rate(elapsed_cg, LB_NUM, LB_DEN + LB_NUM);
237 tcb.sub(Dt::from_attos(span_attos, Scale::TAI))
238 .sub(Dt::from_attos(TDB0_ATTOS, Scale::TAI))
239 }
240
241 pub(crate) const fn tdb_to_tcb(tdb: Self) -> Self {
242 let elapsed = Self::to_attos_since_tcg_tcb_epoch(tdb);
243 let span_attos = Self::mul_lb(elapsed);
244 tdb.add(Dt::from_attos(span_attos, Scale::TAI))
245 .add(Dt::from_attos(TDB0_ATTOS, Scale::TAI))
246 }
247}