Skip to content

Commit 0b27c11

Browse files
authored
Merge pull request #2 from Siderust/time-scales
feat: implement Time<S> class template and CivilTime struct; update t…
2 parents a2e7e5b + b5a3bb6 commit 0b27c11

9 files changed

Lines changed: 644 additions & 266 deletions

File tree

include/tempoch/period.hpp

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
* class template `Period<T>`. The underlying storage is always
99
* `tempoch_period_mjd_t`; `TimeTraits<T>` handles conversion to/from the raw
1010
* MJD doubles.
11+
*
12+
* `TimeTraits` specialisations for every `Time<S>` are provided automatically
13+
* by `time_base.hpp`. A manual `CivilTime` specialisation is kept here for
14+
* `Period<CivilTime>` (i.e. `UTCPeriod`).
1115
*/
1216

1317
#include "qtty/qtty.hpp"
@@ -18,32 +22,18 @@
1822
namespace tempoch {
1923

2024
// ============================================================================
21-
// TimeTraits — connect each time type to the MJD-based FFI layer
25+
// TimeTraits — CivilTime specialisation
2226
// ============================================================================
27+
// TimeTraits<Time<S>> for all scale-based types is already defined in
28+
// time_base.hpp. We only need the CivilTime-specific one here.
2329

24-
/**
25-
* @brief Conversion traits between a time type @p T and raw MJD doubles.
26-
*
27-
* Specialise this struct to make `Period<T>` work for a custom time type.
28-
* Each specialisation must provide:
29-
* - `static double to_mjd_value(const T&)`
30-
* - `static T from_mjd_value(double)`
31-
*/
32-
template <typename T> struct TimeTraits;
33-
34-
template <> struct TimeTraits<MJD> {
35-
static double to_mjd_value(const MJD &t) { return t.value(); }
36-
static MJD from_mjd_value(double m) { return MJD(m); }
37-
};
38-
39-
template <> struct TimeTraits<JulianDate> {
40-
static double to_mjd_value(const JulianDate &t) { return t.to_mjd(); }
41-
static JulianDate from_mjd_value(double m) { return MJD(m).to_jd(); }
42-
};
43-
44-
template <> struct TimeTraits<UTC> {
45-
static double to_mjd_value(const UTC &t) { return MJD::from_utc(t).value(); }
46-
static UTC from_mjd_value(double m) { return MJD(m).to_utc(); }
30+
template <> struct TimeTraits<CivilTime> {
31+
static double to_mjd_value(const CivilTime &t) {
32+
return TimeScaleTraits<MJDScale>::from_civil(t);
33+
}
34+
static CivilTime from_mjd_value(double m) {
35+
return TimeScaleTraits<MJDScale>::to_civil(m);
36+
}
4737
};
4838

4939
// ============================================================================
@@ -128,8 +118,8 @@ template <typename T = MJD> class Period {
128118
*/
129119
template <typename TargetType = qtty::DayTag>
130120
qtty::Quantity<typename qtty::ExtractTag<TargetType>::type> duration() const {
131-
double days = tempoch_period_mjd_duration_days(m_inner);
132-
return qtty::Quantity<qtty::DayTag>(days).template to<TargetType>();
121+
auto qty = tempoch_period_mjd_duration_qty(m_inner);
122+
return qtty::Quantity<qtty::DayTag>(qty.value).template to<TargetType>();
133123
}
134124

135125
/**
@@ -162,7 +152,7 @@ template <typename T> Period(T, T) -> Period<T>;
162152

163153
using MJDPeriod = Period<MJD>; ///< Period expressed in Modified Julian Date.
164154
using JDPeriod = Period<JulianDate>; ///< Period expressed in Julian Date.
165-
using UTCPeriod = Period<UTC>; ///< Period expressed in UTC civil time.
155+
using UTCPeriod = Period<CivilTime>; ///< Period expressed in UTC civil time.
166156

167157
// ============================================================================
168158
// operator<<

include/tempoch/scales.hpp

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#pragma once
2+
3+
/**
4+
* @file scales.hpp
5+
* @brief Time-scale tag types and traits for the tempoch Time<S> template.
6+
*
7+
* Mirrors the Rust `tempoch_core::scales` module. Each tag is an empty struct
8+
* that selects the FFI functions used by `Time<S>`.
9+
*
10+
* Adding a new scale requires:
11+
* 1. Define a tag struct (e.g. `struct TTScale {};`).
12+
* 2. Specialise `TimeScaleTraits<TTScale>` with the FFI calls.
13+
* 3. Specialise `TimeConvertTraits<A,B>` for each supported conversion pair.
14+
*/
15+
16+
#include "ffi_core.hpp" // tempoch_ffi.h + check_status
17+
18+
namespace tempoch {
19+
20+
// Forward declaration — defined in time_base.hpp.
21+
struct CivilTime;
22+
23+
// ============================================================================
24+
// Scale Tags
25+
// ============================================================================
26+
27+
/// Julian Date (days since −4712‑01‑01T12:00 TT).
28+
struct JDScale {};
29+
30+
/// Modified Julian Date (JD − 2 400 000.5).
31+
struct MJDScale {};
32+
33+
/// UTC, internally stored as MJD days for arithmetic.
34+
struct UTCScale {};
35+
36+
// Stubs for future FFI-backed scales — uncomment and specialise traits once
37+
// the FFI exposes the conversion functions.
38+
// struct TTScale {}; // Terrestrial Time
39+
// struct TAIScale {}; // International Atomic Time
40+
// struct TDBScale {}; // Barycentric Dynamical Time
41+
// struct TCGScale {}; // Geocentric Coordinate Time
42+
// struct TCBScale {}; // Barycentric Coordinate Time
43+
// struct GPSScale {}; // GPS Time
44+
// struct UTScale {}; // Universal Time (UT1)
45+
46+
// ============================================================================
47+
// TimeScaleTraits — per-scale FFI dispatch
48+
// ============================================================================
49+
50+
/**
51+
* @brief Primary template — must be specialised for every supported scale.
52+
*
53+
* Required static members:
54+
* - `const char* label()`
55+
* - `double from_utc(const CivilTime&)` — civil time → raw days
56+
* - `CivilTime to_utc(double days)` — raw days → civil time
57+
* - `double add_days(double days, double delta)`
58+
* - `double difference(double a, double b)` — a − b in days
59+
*/
60+
template <typename S> struct TimeScaleTraits {
61+
static_assert(sizeof(S) == 0,
62+
"TimeScaleTraits<S> must be specialised for this scale.");
63+
};
64+
65+
// ── JDScale ─────────────────────────────────────────────────────────────────
66+
67+
template <> struct TimeScaleTraits<JDScale> {
68+
static constexpr const char *label() { return "JD"; }
69+
70+
static double from_civil(const CivilTime &ct);
71+
static CivilTime to_civil(double jd);
72+
73+
static double add_days(double jd, double delta) {
74+
return tempoch_jd_add_days(jd, delta);
75+
}
76+
77+
static double difference(double a, double b) {
78+
return tempoch_jd_difference(a, b);
79+
}
80+
81+
/// Difference as a typed `qtty_quantity_t` (Day unit).
82+
static qtty_quantity_t difference_qty(double a, double b) {
83+
return tempoch_jd_difference_qty(a, b);
84+
}
85+
86+
/// Add a typed duration quantity and write result to @p out.
87+
static void add_qty(double jd, qtty_quantity_t duration, double &out) {
88+
check_status(tempoch_jd_add_qty(jd, duration, &out), "Time<JD>::add_qty");
89+
}
90+
91+
/// J2000.0 epoch constant (JD 2 451 545.0).
92+
static double j2000() { return tempoch_jd_j2000(); }
93+
94+
/// Julian centuries elapsed since J2000.
95+
static double julian_centuries(double jd) {
96+
return tempoch_jd_julian_centuries(jd);
97+
}
98+
99+
/// Julian centuries since J2000 as a typed `qtty_quantity_t`
100+
/// (JulianCentury unit).
101+
static qtty_quantity_t julian_centuries_qty(double jd) {
102+
return tempoch_jd_julian_centuries_qty(jd);
103+
}
104+
};
105+
106+
// ── MJDScale ────────────────────────────────────────────────────────────────
107+
108+
template <> struct TimeScaleTraits<MJDScale> {
109+
static constexpr const char *label() { return "MJD"; }
110+
111+
static double from_civil(const CivilTime &ct);
112+
static CivilTime to_civil(double mjd);
113+
114+
static double add_days(double mjd, double delta) {
115+
return tempoch_mjd_add_days(mjd, delta);
116+
}
117+
118+
static double difference(double a, double b) {
119+
return tempoch_mjd_difference(a, b);
120+
}
121+
122+
/// Difference as a typed `qtty_quantity_t` (Day unit).
123+
static qtty_quantity_t difference_qty(double a, double b) {
124+
return tempoch_mjd_difference_qty(a, b);
125+
}
126+
127+
/// Add a typed duration quantity and write result to @p out.
128+
static void add_qty(double mjd, qtty_quantity_t duration, double &out) {
129+
check_status(tempoch_mjd_add_qty(mjd, duration, &out),
130+
"Time<MJD>::add_qty");
131+
}
132+
};
133+
134+
// ── UTCScale (internally stored as MJD) ─────────────────────────────────────
135+
136+
template <> struct TimeScaleTraits<UTCScale> {
137+
static constexpr const char *label() { return "UTC"; }
138+
139+
static double from_civil(const CivilTime &ct);
140+
static CivilTime to_civil(double mjd);
141+
142+
static double add_days(double mjd, double delta) {
143+
return tempoch_mjd_add_days(mjd, delta);
144+
}
145+
146+
static double difference(double a, double b) {
147+
return tempoch_mjd_difference(a, b);
148+
}
149+
150+
/// Difference as a typed `qtty_quantity_t` (Day unit).
151+
static qtty_quantity_t difference_qty(double a, double b) {
152+
return TimeScaleTraits<MJDScale>::difference_qty(a, b);
153+
}
154+
155+
/// Add a typed duration quantity and write result to @p out.
156+
static void add_qty(double mjd, qtty_quantity_t duration, double &out) {
157+
TimeScaleTraits<MJDScale>::add_qty(mjd, duration, out);
158+
}
159+
};
160+
161+
// ============================================================================
162+
// TimeConvertTraits — cross-scale conversion
163+
// ============================================================================
164+
165+
/**
166+
* @brief Primary template — specialise for each supported A→B pair.
167+
*
168+
* Required: `static double convert(double src_days)`.
169+
*/
170+
template <typename From, typename To> struct TimeConvertTraits {
171+
static_assert(sizeof(From) == 0,
172+
"TimeConvertTraits<From,To> is not specialised for this pair.");
173+
};
174+
175+
// ── JD ↔ MJD ────────────────────────────────────────────────────────────────
176+
177+
template <> struct TimeConvertTraits<JDScale, MJDScale> {
178+
static double convert(double jd) { return tempoch_jd_to_mjd(jd); }
179+
};
180+
181+
template <> struct TimeConvertTraits<MJDScale, JDScale> {
182+
static double convert(double mjd) { return tempoch_mjd_to_jd(mjd); }
183+
};
184+
185+
// ── JD ↔ UTC (UTC stored as MJD internally) ────────────────────────────────
186+
187+
template <> struct TimeConvertTraits<JDScale, UTCScale> {
188+
static double convert(double jd) { return tempoch_jd_to_mjd(jd); }
189+
};
190+
191+
template <> struct TimeConvertTraits<UTCScale, JDScale> {
192+
static double convert(double mjd) { return tempoch_mjd_to_jd(mjd); }
193+
};
194+
195+
// ── MJD ↔ UTC (identity — both stored as MJD) ──────────────────────────────
196+
197+
template <> struct TimeConvertTraits<MJDScale, UTCScale> {
198+
static double convert(double mjd) { return mjd; }
199+
};
200+
201+
template <> struct TimeConvertTraits<UTCScale, MJDScale> {
202+
static double convert(double mjd) { return mjd; }
203+
};
204+
205+
} // namespace tempoch

include/tempoch/tempoch.hpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@
55
* @brief Umbrella header for the tempoch C++ wrapper library.
66
*
77
* Include this single header to get the full tempoch C++ API:
8-
* - `tempoch::UTC` — UTC date-time breakdown
9-
* - `tempoch::JulianDate` — Julian Date wrapper
10-
* - `tempoch::MJD` — Modified Julian Date wrapper
11-
* - `tempoch::Period` — Time period [start, end] in MJD
8+
*
9+
* - `tempoch::Time<S>` — generic time-point template (core)
10+
* - `tempoch::JulianDate` = `Time<JDScale>`
11+
* - `tempoch::MJD` = `Time<MJDScale>`
12+
* - `tempoch::CivilTime` — UTC civil date-time breakdown
13+
* - `tempoch::UTC` — alias for `CivilTime`
14+
* - `tempoch::Period<T>` — time period [start, end]
1215
*
1316
* @code
1417
* #include <tempoch/tempoch.hpp>
1518
*
16-
* auto jd = tempoch::JulianDate::from_utc({2026, 7, 15, 22, 0, 0});
17-
* auto mjd = tempoch::MJD::from_jd(jd);
18-
* tempoch::Period night(60200.0, 60200.5);
19+
* auto jd = tempoch::JulianDate::from_utc({2026, 7, 15, 22, 0, 0});
20+
* auto mjd = jd.to<tempoch::MJDScale>(); // cross-scale conversion
1921
* @endcode
2022
*/
2123

2224
#include "ffi_core.hpp"
2325
#include "period.hpp"
26+
#include "scales.hpp"
2427
#include "time.hpp"
28+
#include "time_base.hpp"

0 commit comments

Comments
 (0)