1use super::constats::GPS_EPOCH_TAI;
8use super::context::TimeContext;
9use super::error::ConversionError;
10use super::scale::{TAI, UTC};
11use super::time::Time;
12use crate::data::active::{
13 time_data_tai_seconds_from_utc, time_data_tai_seconds_is_in_leap_window,
14 time_data_try_tai_minus_utc_mjd, time_data_utc_from_tai_seconds,
15};
16use crate::encoding::{mjd_to_j2000_seconds, unix_seconds_to_mjd};
17use chrono::{DateTime, Utc};
18use qtty::Second;
19
20impl Time<UTC> {
21 #[inline]
24 pub fn try_from_chrono_with(
25 dt: DateTime<Utc>,
26 ctx: &TimeContext,
27 ) -> Result<Self, ConversionError> {
28 let tai_secs =
29 time_data_tai_seconds_from_utc(ctx.time_data(), dt, ctx.allows_pre_definition_utc())?;
30 Self::try_new(tai_secs, Second::new(0.0))
31 }
32
33 #[inline]
40 pub fn try_from_chrono(dt: DateTime<Utc>) -> Result<Self, ConversionError> {
41 Self::try_from_chrono_with(dt, &TimeContext::new())
42 }
43
44 #[track_caller]
47 #[inline]
48 pub fn from_chrono_with(dt: DateTime<Utc>, ctx: &TimeContext) -> Self {
49 Self::try_from_chrono_with(dt, ctx)
50 .expect("UTC conversion failed; use try_from_chrono_with")
51 }
52
53 #[track_caller]
59 #[inline]
60 pub fn from_chrono(dt: DateTime<Utc>) -> Self {
61 Self::try_from_chrono(dt).expect("UTC conversion failed; use try_from_chrono")
62 }
63
64 #[inline]
67 pub fn try_to_chrono_with(self, ctx: &TimeContext) -> Result<DateTime<Utc>, ConversionError> {
68 time_data_utc_from_tai_seconds(
69 ctx.time_data(),
70 self.total_seconds(),
71 ctx.allows_pre_definition_utc(),
72 )
73 }
74
75 #[inline]
82 pub fn try_to_chrono(self) -> Result<DateTime<Utc>, ConversionError> {
83 self.try_to_chrono_with(&TimeContext::new())
84 }
85
86 #[inline]
89 pub fn to_chrono_with(self, ctx: &TimeContext) -> Option<DateTime<Utc>> {
90 self.try_to_chrono_with(ctx).ok()
91 }
92
93 #[inline]
99 pub fn to_chrono(self) -> Option<DateTime<Utc>> {
100 self.try_to_chrono().ok()
101 }
102
103 #[inline]
106 pub(crate) fn from_raw_unix_seconds_with(
107 seconds: Second,
108 ctx: &TimeContext,
109 ) -> Result<Self, ConversionError> {
110 if !seconds.is_finite() {
111 return Err(ConversionError::NonFinite);
112 }
113 let mjd_utc = unix_seconds_to_mjd(seconds);
114 let tai_minus_utc = time_data_try_tai_minus_utc_mjd(
115 ctx.time_data(),
116 mjd_utc,
117 ctx.allows_pre_definition_utc(),
118 )?;
119 let tai_secs = mjd_to_j2000_seconds(mjd_utc) + tai_minus_utc;
120 Self::try_new(tai_secs, Second::new(0.0))
121 }
122
123 #[inline]
126 pub(crate) fn raw_unix_seconds_with(
127 self,
128 ctx: &TimeContext,
129 ) -> Result<Second, ConversionError> {
130 if self.is_leap_second_with(ctx) {
131 return Err(ConversionError::InvalidLeapSecond);
132 }
133 let dt = self.try_to_chrono_with(ctx)?;
134 let nanos = dt.timestamp_subsec_nanos();
135 Ok(Second::new(dt.timestamp() as f64 + nanos as f64 / 1e9))
136 }
137
138 #[inline]
141 pub fn is_leap_second_with(self, ctx: &TimeContext) -> bool {
142 time_data_tai_seconds_is_in_leap_window(ctx.time_data(), self.total_seconds())
143 }
144
145 #[inline]
152 pub fn is_leap_second(self) -> bool {
153 self.is_leap_second_with(&TimeContext::new())
154 }
155}
156
157impl Time<TAI> {
158 #[inline]
160 pub(crate) fn from_raw_gps_seconds(seconds: Second) -> Result<Self, ConversionError> {
161 if !seconds.is_finite() {
162 return Err(ConversionError::NonFinite);
163 }
164 Self::try_new(seconds + GPS_EPOCH_TAI, Second::new(0.0))
165 }
166
167 #[inline]
169 pub(crate) fn raw_gps_seconds(self) -> Second {
170 self.total_seconds() - GPS_EPOCH_TAI
171 }
172}