tempoch-cpp 0.5.3
Header-only C++ wrapper for tempoch
Loading...
Searching...
No Matches
time_base.hpp
Go to the documentation of this file.
1#pragma once
2
8#include "civil_time.hpp"
9#include "ffi_core.hpp"
10#include "formats/formats.hpp"
11#include "scales/scales.hpp"
12#include <cmath>
13#include <memory>
14#include <optional>
15#include <ostream>
16#include <type_traits>
17#include <utility>
18
19namespace tempoch {
20
21template <typename S> class Time;
22template <typename S, typename F> class EncodedTime;
23template <typename T> struct TimeTraits;
24
25namespace detail {
26
28 void operator()(tempoch_context_t *ptr) const noexcept { tempoch_context_free(ptr); }
29};
30
31inline std::shared_ptr<tempoch_context_t> make_default_context() {
32 tempoch_context_t *raw = nullptr;
33 check_status(tempoch_context_create_default(&raw), "tempoch_context_create_default");
34 return std::shared_ptr<tempoch_context_t>(raw, ContextDeleter{});
35}
36
37inline std::shared_ptr<tempoch_context_t> make_builtin_eop_context() {
38 tempoch_context_t *raw = nullptr;
40 "tempoch_context_create_with_builtin_eop");
41 return std::shared_ptr<tempoch_context_t>(raw, ContextDeleter{});
42}
43
44inline std::shared_ptr<tempoch_context_t>
46 tempoch_context_t *raw = nullptr;
48 "tempoch_context_allow_pre_definition_utc");
49 return std::shared_ptr<tempoch_context_t>(raw, ContextDeleter{});
50}
51
54 check_status(tempoch_time_new(hi_seconds, lo_seconds, &out), "tempoch_time_new");
55 return out;
56}
57
58template <typename From, typename To>
62 static_cast<int32_t>(scale_tag_v<To>), ctx, &out),
63 "tempoch_time_scale_convert");
64 return out;
65}
66
67template <typename S, typename F>
68inline double encode_time(const tempoch_time_t &value, const tempoch_context_t *ctx) {
69 double out = 0.0;
71 static_cast<int32_t>(format_tag_v<F>), ctx, &out),
72 "tempoch_time_to_format");
73 return out;
74}
75
76template <typename S, typename F>
77inline tempoch_time_t decode_time(double raw, const tempoch_context_t *ctx) {
80 static_cast<int32_t>(format_tag_v<F>), ctx, &out),
81 "tempoch_time_from_format");
82 return out;
83}
84
87 check_status(tempoch_time_from_civil(civil.to_c(), ctx, &out), "tempoch_time_from_civil");
88 return out;
89}
90
93 check_status(tempoch_time_to_civil(value, ctx, &out), "tempoch_time_to_civil");
94 return CivilTime::from_c(out);
95}
96
97template <typename Q> inline tempoch_time_t add_seconds(const tempoch_time_t &value, const Q &qty) {
99 qtty_quantity_t raw{qty.value(), qtty::UnitTraits<typename Q::unit_tag>::unit_id()};
100 check_status(tempoch_time_add_seconds(value, raw, &out), "tempoch_time_add_seconds");
101 return out;
102}
103
104inline qtty::Second difference_seconds(const tempoch_time_t &lhs, const tempoch_time_t &rhs) {
105 double out = 0.0;
106 check_status(tempoch_time_difference_seconds(lhs, rhs, &out), "tempoch_time_difference_seconds");
107 return qtty::Second(out);
108}
109
110template <typename F> inline typename FormatTraits<F>::quantity_type quantity_from_raw(double raw) {
111 return typename FormatTraits<F>::quantity_type(raw);
112}
113
114template <typename F> inline void ensure_finite_encoded(double raw, const char *operation) {
115 if (!std::isfinite(raw))
116 throw ConversionFailedError(std::string(operation) + " failed: non-finite raw value");
117}
118
119} // namespace detail
120
125 std::shared_ptr<tempoch_context_t> handle_;
126
127 explicit TimeContext(std::shared_ptr<tempoch_context_t> handle) : handle_(std::move(handle)) {}
128
129public:
130 TimeContext() : handle_(detail::make_default_context()) {}
131
133
137
138 const tempoch_context_t *get() const noexcept { return handle_.get(); }
139};
140
144template <typename S> class Time {
145 static_assert(is_scale_v<S>, "Time<S> requires a valid tempoch::scale tag");
146
147 tempoch_time_t raw_;
148
149 explicit Time(const tempoch_time_t &raw) : raw_(raw) {}
150
151 template <typename> friend class Time;
152 template <typename, typename> friend class EncodedTime;
153
154public:
155 using scale_type = S;
156
157 Time() : raw_(detail::make_time(0.0, 0.0)) {}
158
159 static Time from_split_seconds(qtty::Second hi, qtty::Second lo = qtty::Second(0.0)) {
160 return Time(detail::make_time(hi.value(), lo.value()));
161 }
162
164
166 template <typename Fmt> static Time from_encoded(const EncodedTime<S, Fmt> &encoded) {
167 return Time(detail::decode_time<S, Fmt>(encoded.value(), nullptr));
168 }
169
171 template <typename Fmt>
173 return Time(detail::decode_time<S, Fmt>(encoded.value(), ctx.get()));
174 }
175
176 std::pair<qtty::Second, qtty::Second> split_seconds() const noexcept {
177 return {qtty::Second(raw_.hi_seconds), qtty::Second(raw_.lo_seconds)};
178 }
179
180 qtty::Second total_seconds() const noexcept {
181 return qtty::Second(raw_.hi_seconds + raw_.lo_seconds);
182 }
183
184 const tempoch_time_t &c_inner() const noexcept { return raw_; }
185
186 static constexpr const char *label() { return ScaleTraits<S>::name(); }
187
188 template <typename U = S, std::enable_if_t<std::is_same_v<U, scale::UTC>, int> = 0>
190 return Time(detail::time_from_civil(civil, nullptr));
191 }
192
193 template <typename U = S, std::enable_if_t<std::is_same_v<U, scale::UTC>, int> = 0>
194 static Time from_civil(const CivilTime &civil, const TimeContext &ctx) {
195 return Time(detail::time_from_civil(civil, ctx.get()));
196 }
197
198 template <typename U = S, std::enable_if_t<std::is_same_v<U, scale::UTC>, int> = 0>
200 return detail::time_to_civil(raw_, nullptr);
201 }
202
203 template <typename U = S, std::enable_if_t<std::is_same_v<U, scale::UTC>, int> = 0>
205 return detail::time_to_civil(raw_, ctx.get());
206 }
207
208 template <typename TargetScale,
209 std::enable_if_t<is_scale_v<TargetScale> && !std::is_same_v<S, scale::UT1> &&
210 !std::is_same_v<TargetScale, scale::UT1>,
211 int> = 0>
213 return Time<TargetScale>(detail::scale_convert<S, TargetScale>(raw_, nullptr));
214 }
215
216 template <typename TargetScale,
217 std::enable_if_t<is_scale_v<TargetScale> && (std::is_same_v<S, scale::UT1> ||
218 std::is_same_v<TargetScale, scale::UT1>),
219 int> = 0>
220 Time<TargetScale> to() const = delete;
221
222 template <typename TargetScale, std::enable_if_t<is_scale_v<TargetScale>, int> = 0>
224 return Time<TargetScale>(detail::scale_convert<S, TargetScale>(raw_, ctx.get()));
225 }
226
227 template <typename TargetScale, typename TargetFormat,
228 std::enable_if_t<is_scale_v<TargetScale> && is_format_v<TargetFormat>, int> = 0>
232
233 template <typename TargetScale, typename TargetFormat,
234 std::enable_if_t<is_scale_v<TargetScale> && is_format_v<TargetFormat>, int> = 0>
238
239 template <typename TargetFormat, std::enable_if_t<is_format_v<TargetFormat>, int> = 0>
241 return EncodedTime<S, TargetFormat>(detail::quantity_from_raw<TargetFormat>(
242 detail::encode_time<S, TargetFormat>(raw_, nullptr)));
243 }
244
245 template <typename TargetFormat, std::enable_if_t<is_format_v<TargetFormat>, int> = 0>
247 return EncodedTime<S, TargetFormat>(detail::quantity_from_raw<TargetFormat>(
248 detail::encode_time<S, TargetFormat>(raw_, ctx.get())));
249 }
250
251 template <typename TargetScale, std::enable_if_t<is_scale_v<TargetScale>, int> = 0>
252 std::optional<Time<TargetScale>> try_to() const {
253 try {
255 } catch (const std::exception &) {
256 return std::nullopt;
257 }
258 }
259
260 template <typename TargetFormat, std::enable_if_t<is_format_v<TargetFormat>, int> = 0>
261 std::optional<EncodedTime<S, TargetFormat>> try_to() const {
262 try {
264 } catch (const std::exception &) {
265 return std::nullopt;
266 }
267 }
268
269 template <typename Q> Time operator+(const Q &delta) const {
270 return Time(detail::add_seconds(raw_, delta));
271 }
272
273 template <typename Q> Time operator-(const Q &delta) const { return *this + Q(-delta.value()); }
274
275 template <typename Q> Time &operator+=(const Q &delta) {
276 raw_ = detail::add_seconds(raw_, delta);
277 return *this;
278 }
279
280 template <typename Q> Time &operator-=(const Q &delta) {
281 raw_ = detail::add_seconds(raw_, Q(-delta.value()));
282 return *this;
283 }
284
285 qtty::Second operator-(const Time &other) const {
286 return detail::difference_seconds(raw_, other.raw_);
287 }
288
289 bool operator==(const Time &other) const noexcept {
290 return raw_.hi_seconds == other.raw_.hi_seconds && raw_.lo_seconds == other.raw_.lo_seconds;
291 }
292
293 bool operator!=(const Time &other) const noexcept { return !(*this == other); }
294
295 bool operator<(const Time &other) const noexcept {
296 return raw_.hi_seconds < other.raw_.hi_seconds ||
297 (raw_.hi_seconds == other.raw_.hi_seconds && raw_.lo_seconds < other.raw_.lo_seconds);
298 }
299
300 bool operator<=(const Time &other) const noexcept { return !(other < *this); }
301 bool operator>(const Time &other) const noexcept { return other < *this; }
302 bool operator>=(const Time &other) const noexcept { return !(*this < other); }
303};
304
305template <typename S> inline std::ostream &operator<<(std::ostream &os, const Time<S> &time) {
306 return os << Time<S>::label() << " " << time.total_seconds().value() << " s";
307}
308
312template <typename S, typename F> class EncodedTime {
313 static_assert(is_scale_v<S>, "EncodedTime<S, F> requires a valid tempoch::scale tag");
314 static_assert(is_format_v<F>, "EncodedTime<S, F> requires a valid tempoch::format tag");
315
316public:
317 using scale_type = S;
318 using format_type = F;
320
321private:
322 quantity_type raw_;
323
324public:
325 EncodedTime() : raw_(0.0) {}
326
327 explicit EncodedTime(quantity_type raw) : raw_(raw) {
328 detail::ensure_finite_encoded<F>(raw_.value(), "EncodedTime::EncodedTime");
329 }
330
332
333 static std::optional<EncodedTime> try_new(quantity_type raw) {
334 if (!std::isfinite(raw.value()))
335 return std::nullopt;
336 return EncodedTime(raw);
337 }
338
343
344 quantity_type raw() const noexcept { return raw_; }
346 double value() const noexcept { return raw_.value(); }
347
348 template <typename U = F, std::enable_if_t<std::is_same_v<U, format::JD>, int> = 0>
352
353 template <typename TargetScale, std::enable_if_t<is_scale_v<TargetScale>, int> = 0>
354 auto to() const {
355 return Time<S>::from_encoded(*this).template to<TargetScale>();
356 }
357
358 template <typename TargetScale, typename TargetFormat,
359 std::enable_if_t<is_scale_v<TargetScale> && is_format_v<TargetFormat>, int> = 0>
363
364 template <typename TargetScale, std::enable_if_t<is_scale_v<TargetScale>, int> = 0>
368
369 template <typename TargetScale, typename TargetFormat,
370 std::enable_if_t<is_scale_v<TargetScale> && is_format_v<TargetFormat>, int> = 0>
374
375 template <typename TargetFormat, std::enable_if_t<is_format_v<TargetFormat>, int> = 0>
377 return Time<S>::from_encoded(*this).template to<TargetFormat>();
378 }
379
380 template <typename TargetFormat, std::enable_if_t<is_format_v<TargetFormat>, int> = 0>
384
385 template <typename TargetScale, std::enable_if_t<is_scale_v<TargetScale>, int> = 0>
386 std::optional<Time<TargetScale>> try_to() const {
387 try {
389 } catch (const std::exception &) {
390 return std::nullopt;
391 }
392 }
393
394 template <typename TargetFormat, std::enable_if_t<is_format_v<TargetFormat>, int> = 0>
395 std::optional<EncodedTime<S, TargetFormat>> try_to() const {
396 try {
398 } catch (const std::exception &) {
399 return std::nullopt;
400 }
401 }
402
403 template <typename Q> EncodedTime operator+(const Q &delta) const {
404 return (Time<S>::from_encoded(*this) + delta).template to<F>();
405 }
406
407 template <typename Q> EncodedTime operator-(const Q &delta) const {
408 return (Time<S>::from_encoded(*this) - delta).template to<F>();
409 }
410
411 template <typename Q> EncodedTime &operator+=(const Q &delta) {
412 *this = *this + delta;
413 return *this;
414 }
415
416 template <typename Q> EncodedTime &operator-=(const Q &delta) {
417 *this = *this - delta;
418 return *this;
419 }
420
423 .template to<quantity_type>();
424 }
425
426 bool operator==(const EncodedTime &other) const noexcept { return raw_ == other.raw_; }
427 bool operator!=(const EncodedTime &other) const noexcept { return raw_ != other.raw_; }
428 bool operator<(const EncodedTime &other) const noexcept { return raw_ < other.raw_; }
429 bool operator<=(const EncodedTime &other) const noexcept { return raw_ <= other.raw_; }
430 bool operator>(const EncodedTime &other) const noexcept { return raw_ > other.raw_; }
431 bool operator>=(const EncodedTime &other) const noexcept { return raw_ >= other.raw_; }
432
433 EncodedTime min(const EncodedTime &other) const noexcept {
434 return *this <= other ? *this : other;
435 }
436 EncodedTime max(const EncodedTime &other) const noexcept {
437 return *this >= other ? *this : other;
438 }
439
441 return EncodedTime(quantity_type((raw_.value() + other.raw_.value()) * 0.5));
442 }
443
444 template <typename U = F, std::enable_if_t<std::is_same_v<U, format::JD>, int> = 0>
446 return raw_.value();
447 }
448
449 template <typename U = F, std::enable_if_t<std::is_same_v<U, format::MJD>, int> = 0>
451 return raw_.value();
452 }
453
454 template <typename U = F, std::enable_if_t<std::is_same_v<U, format::JD>, int> = 0>
455 qtty::JulianCentury julian_centuries_qty() const noexcept {
456 return qtty::JulianCentury((raw_.value() - 2451545.0) / 36525.0);
457 }
458
459 template <typename U = F, std::enable_if_t<std::is_same_v<U, format::JD>, int> = 0>
461 return julian_centuries_qty().value();
462 }
463
464 template <typename U = F, std::enable_if_t<std::is_same_v<U, format::JD>, int> = 0>
466 return this->template to<format::MJD>();
467 }
468
469 template <typename U = F, std::enable_if_t<std::is_same_v<U, format::MJD>, int> = 0>
471 return this->template to<format::JD>();
472 }
473
474 template <typename U = F, std::enable_if_t<std::is_same_v<U, format::JD>, int> = 0>
476 return mjd.template to<format::JD>();
477 }
478
479 template <typename U = F, std::enable_if_t<std::is_same_v<U, format::MJD>, int> = 0>
481 return jd.template to<format::MJD>();
482 }
483
484 template <typename U = S, std::enable_if_t<std::is_same_v<U, scale::TT>, int> = 0>
486 return Time<scale::UTC>::from_civil(civil).template to<scale::TT>().template to<F>();
487 }
488
489 template <typename U = S, std::enable_if_t<std::is_same_v<U, scale::TT>, int> = 0>
491 return Time<scale::UTC>::from_civil(civil, ctx).template to<scale::TT>().template to<F>();
492 }
493
494 template <typename U = S, std::enable_if_t<std::is_same_v<U, scale::TT>, int> = 0>
496 return Time<S>::from_encoded(*this).template to<scale::UTC>().to_civil();
497 }
498
499 template <typename U = S, std::enable_if_t<std::is_same_v<U, scale::TT>, int> = 0>
501 return Time<S>::from_encoded_with(*this, ctx).template to<scale::UTC>().to_civil(ctx);
502 }
503};
504
505template <typename S, typename F>
506inline std::ostream &operator<<(std::ostream &os, const EncodedTime<S, F> &time) {
507 return os << ScaleTraits<S>::name() << ' ' << FormatTraits<F>::name() << ' ' << time.raw();
508}
509
510} // namespace tempoch
UTC date-time breakdown struct.
A generic scale/format conversion failed.
Definition ffi_core.hpp:83
A typed external encoding of a time instant on scale S.
bool operator>=(const EncodedTime &other) const noexcept
static EncodedTime from_mjd(const EncodedTime< S, format::MJD > &mjd)
bool operator<(const EncodedTime &other) const noexcept
EncodedTime operator-(const Q &delta) const
quantity_type quantity() const noexcept
EncodedTime mean(const EncodedTime &other) const
EncodedTime< S, TargetFormat > to_with(const TimeContext &ctx) const
bool operator<=(const EncodedTime &other) const noexcept
static std::optional< EncodedTime > try_new(quantity_type raw)
EncodedTime< TargetScale, TargetFormat > to_with(const TimeContext &ctx) const
double julian_centuries() const noexcept
EncodedTime< S, format::JD > to_jd() const
EncodedTime(double value)
static EncodedTime J2000()
bool operator!=(const EncodedTime &other) const noexcept
EncodedTime(quantity_type raw)
EncodedTime< TargetScale, TargetFormat > to() const
static EncodedTime from_utc(const CivilTime &civil)
static EncodedTime from_jd(const EncodedTime< S, format::JD > &jd)
quantity_type raw() const noexcept
static EncodedTime from_raw(quantity_type raw)
EncodedTime min(const EncodedTime &other) const noexcept
typename FormatTraits< F >::quantity_type quantity_type
Time< TargetScale > to_with(const TimeContext &ctx) const
EncodedTime< S, TargetFormat > to() const
double value() const noexcept
EncodedTime & operator-=(const Q &delta)
qtty::JulianCentury julian_centuries_qty() const noexcept
double jd_value() const noexcept
EncodedTime operator+(const Q &delta) const
bool operator>(const EncodedTime &other) const noexcept
CivilTime to_utc(const TimeContext &ctx) const
std::optional< EncodedTime< S, TargetFormat > > try_to() const
EncodedTime & operator+=(const Q &delta)
std::optional< Time< TargetScale > > try_to() const
static EncodedTime from_utc(const CivilTime &civil, const TimeContext &ctx)
EncodedTime< S, format::MJD > to_mjd() const
CivilTime to_utc() const
quantity_type operator-(const EncodedTime &other) const
bool operator==(const EncodedTime &other) const noexcept
EncodedTime max(const EncodedTime &other) const noexcept
double mjd_value() const noexcept
Immutable conversion context for UT1 and historical UTC routes.
const tempoch_context_t * get() const noexcept
TimeContext allow_pre_definition_utc() const
static TimeContext with_builtin_eop()
A point in time on scale S, stored as a split J2000-second pair.
static Time from_civil(const CivilTime &civil)
Time operator-(const Q &delta) const
EncodedTime< S, TargetFormat > to_with(const TimeContext &ctx) const
friend class Time
std::optional< EncodedTime< S, TargetFormat > > try_to() const
static constexpr const char * label()
Time< TargetScale > to() const =delete
EncodedTime< TargetScale, TargetFormat > to_with(const TimeContext &ctx) const
std::pair< qtty::Second, qtty::Second > split_seconds() const noexcept
bool operator==(const Time &other) const noexcept
CivilTime to_civil(const TimeContext &ctx) const
bool operator<=(const Time &other) const noexcept
const tempoch_time_t & c_inner() const noexcept
EncodedTime< S, TargetFormat > to() const
static Time from_encoded_with(const EncodedTime< S, Fmt > &encoded, const TimeContext &ctx)
Decode using explicit UTC / UT1 policy from ctx when required by format Fmt.
Time & operator-=(const Q &delta)
bool operator!=(const Time &other) const noexcept
std::optional< Time< TargetScale > > try_to() const
bool operator>=(const Time &other) const noexcept
static Time from_raw_j2000_seconds(qtty::Second seconds)
static Time from_civil(const CivilTime &civil, const TimeContext &ctx)
Time< TargetScale > to() const
static Time from_split_seconds(qtty::Second hi, qtty::Second lo=qtty::Second(0.0))
Time< TargetScale > to_with(const TimeContext &ctx) const
qtty::Second total_seconds() const noexcept
Time & operator+=(const Q &delta)
Time operator+(const Q &delta) const
EncodedTime< TargetScale, TargetFormat > to() const
qtty::Second operator-(const Time &other) const
static Time from_encoded(const EncodedTime< S, Fmt > &encoded)
Decode a scalar encoding Fmt into canonical split storage on scale S (default context).
bool operator<(const Time &other) const noexcept
CivilTime to_civil() const
bool operator>(const Time &other) const noexcept
Error handling for the tempoch C++ wrapper.
Time-format tag types for the tempoch C++ API.
qtty::Second difference_seconds(const tempoch_time_t &lhs, const tempoch_time_t &rhs)
void ensure_finite_encoded(double raw, const char *operation)
double encode_time(const tempoch_time_t &value, const tempoch_context_t *ctx)
Definition time_base.hpp:68
tempoch_time_t decode_time(double raw, const tempoch_context_t *ctx)
Definition time_base.hpp:77
std::shared_ptr< tempoch_context_t > make_builtin_eop_context()
Definition time_base.hpp:37
CivilTime time_to_civil(const tempoch_time_t &value, const tempoch_context_t *ctx)
Definition time_base.hpp:91
std::shared_ptr< tempoch_context_t > make_default_context()
Definition time_base.hpp:31
FormatTraits< F >::quantity_type quantity_from_raw(double raw)
tempoch_time_t scale_convert(const tempoch_time_t &value, const tempoch_context_t *ctx)
Definition time_base.hpp:59
tempoch_time_t time_from_civil(const CivilTime &civil, const tempoch_context_t *ctx)
Definition time_base.hpp:85
tempoch_time_t add_seconds(const tempoch_time_t &value, const Q &qty)
Definition time_base.hpp:97
std::shared_ptr< tempoch_context_t > make_pre_definition_context(const tempoch_context_t *parent)
Definition time_base.hpp:45
tempoch_time_t make_time(double hi_seconds, double lo_seconds)
Definition time_base.hpp:52
std::ostream & operator<<(std::ostream &os, const CivilTime &u)
Stream CivilTime as YYYY-MM-DD HH:MM:SS[.nnnnnnnnn].
void check_status(tempoch_status_t status, const char *operation)
Check a tempoch_status_t and throw the appropriate exception on error.
Definition ffi_core.hpp:139
constexpr tempoch_scale_tag_t scale_tag_v
Time-scale tag types for the tempoch C++ API.
UTC date-time breakdown.
static CivilTime from_c(const tempoch_utc_t &c)
Create from the C FFI struct.
void operator()(tempoch_context_t *ptr) const noexcept
Definition time_base.hpp:28