Vulcan
Aerospace Engineering Utilities Built on Janus
Loading...
Searching...
No Matches
LeapSeconds.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <janus/math/Interpolate.hpp>
7
8namespace vulcan::time {
9
10// =============================================================================
11// Leap Second Table (IERS Bulletin C)
12// =============================================================================
13
21 int year;
22 int month;
23 int day;
25};
26
36inline constexpr std::array<LeapSecondEntry, 28> LEAP_SECOND_TABLE = {{
37 // Year, Month, Day, TAI-UTC
38 {1972, 1, 1, 10}, // Initial offset when leap seconds began
39 {1972, 7, 1, 11}, {1973, 1, 1, 12}, {1974, 1, 1, 13}, {1975, 1, 1, 14},
40 {1976, 1, 1, 15}, {1977, 1, 1, 16}, {1978, 1, 1, 17}, {1979, 1, 1, 18},
41 {1980, 1, 1, 19}, {1981, 7, 1, 20}, {1982, 7, 1, 21}, {1983, 7, 1, 22},
42 {1985, 7, 1, 23}, {1988, 1, 1, 24}, {1990, 1, 1, 25}, {1991, 1, 1, 26},
43 {1992, 7, 1, 27}, {1993, 7, 1, 28}, {1994, 7, 1, 29}, {1996, 1, 1, 30},
44 {1997, 7, 1, 31}, {1999, 1, 1, 32}, {2006, 1, 1, 33}, {2009, 1, 1, 34},
45 {2012, 7, 1, 35}, {2015, 7, 1, 36}, {2017, 1, 1, 37}, // Most recent leap
46 // second
47}};
48
58[[nodiscard]] inline int leap_seconds_at_tai(double tai_jd) {
59 // Before 1972: use 10 seconds as approximation
60 double jd_1972 = calendar_to_jd(1972, 1, 1, 0, 0, 0.0) +
62 if (tai_jd < jd_1972) {
63 return 10;
64 }
65
66 // Search backwards through table (most queries will be recent dates)
67 for (int i = static_cast<int>(LEAP_SECOND_TABLE.size()) - 1; i >= 0; --i) {
68 const auto &entry = LEAP_SECOND_TABLE[i];
69 // Convert entry date to TAI JD
70 // Entry is when delta_at takes effect at 00:00:00 UTC
71 // In TAI, this is 00:00:00 UTC + delta_at seconds
72 double entry_tai_jd =
73 calendar_to_jd(entry.year, entry.month, entry.day, 0, 0, 0.0) +
74 entry.delta_at / constants::time::SECONDS_PER_DAY;
75
76 if (tai_jd >= entry_tai_jd) {
77 return entry.delta_at;
78 }
79 }
80
81 return 10; // Fallback (shouldn't reach for valid inputs)
82}
83
93[[nodiscard]] inline int leap_seconds_at_utc(double utc_jd) {
94 // Before 1972: use 10 seconds
95 double jd_1972 = calendar_to_jd(1972, 1, 1, 0, 0, 0.0);
96 if (utc_jd < jd_1972) {
97 return 10;
98 }
99
100 // Search backwards
101 for (int i = static_cast<int>(LEAP_SECOND_TABLE.size()) - 1; i >= 0; --i) {
102 const auto &entry = LEAP_SECOND_TABLE[i];
103 double entry_utc_jd =
104 calendar_to_jd(entry.year, entry.month, entry.day, 0, 0, 0.0);
105
106 if (utc_jd >= entry_utc_jd) {
107 return entry.delta_at;
108 }
109 }
110
111 return 10;
112}
113
127[[nodiscard]] inline bool is_leap_second(int year, int month, int day, int hour,
128 int min, double sec) {
129 // Leap seconds only occur at 23:59:60 on June 30 or December 31
130 if (hour != 23 || min != 59 || sec < 60.0) {
131 return false;
132 }
133
134 if (!((month == 6 && day == 30) || (month == 12 && day == 31))) {
135 return false;
136 }
137
138 // Check if this date had a leap second
139 int next_month = (month == 6) ? 7 : 1;
140 int next_year = (month == 6) ? year : year + 1;
141
142 for (const auto &entry : LEAP_SECOND_TABLE) {
143 if (entry.year == next_year && entry.month == next_month) {
144 return true;
145 }
146 }
147
148 return false;
149}
150
157[[nodiscard]] inline double next_leap_second(double utc_jd) {
158 for (const auto &entry : LEAP_SECOND_TABLE) {
159 double entry_jd =
160 calendar_to_jd(entry.year, entry.month, entry.day, 0, 0, 0.0);
161 if (entry_jd > utc_jd) {
162 return entry_jd;
163 }
164 }
165 return 0.0; // No future leap second known
166}
167
168// =============================================================================
169// Symbolic Leap Second Lookup (via janus::Interpolator)
170// =============================================================================
171
183[[nodiscard]] inline const janus::Interpolator &leap_second_interpolator() {
184 static const janus::Interpolator interp = []() {
185 janus::NumericVector jd_points(
186 static_cast<int>(LEAP_SECOND_TABLE.size()));
187 janus::NumericVector delta_at(
188 static_cast<int>(LEAP_SECOND_TABLE.size()));
189 for (size_t i = 0; i < LEAP_SECOND_TABLE.size(); ++i) {
190 const auto &entry = LEAP_SECOND_TABLE[i];
191 jd_points(static_cast<int>(i)) =
192 calendar_to_jd(entry.year, entry.month, entry.day);
193 delta_at(static_cast<int>(i)) = static_cast<double>(entry.delta_at);
194 }
195 return janus::Interpolator(jd_points, delta_at,
196 janus::InterpolationMethod::Linear);
197 }();
198 return interp;
199}
200
212template <typename Scalar>
213[[nodiscard]] Scalar leap_seconds_symbolic(const Scalar &utc_jd) {
214 return leap_second_interpolator()(utc_jd);
215}
216
217} // namespace vulcan::time
constexpr double SECONDS_PER_DAY
Seconds per day.
Definition TimeConstants.hpp:45
Definition Epoch.hpp:12
int leap_seconds_at_utc(double utc_jd)
Get TAI - UTC offset for a given UTC Julian Date.
Definition LeapSeconds.hpp:93
const janus::Interpolator & leap_second_interpolator()
Get a symbolic-compatible leap second interpolator.
Definition LeapSeconds.hpp:183
constexpr std::array< LeapSecondEntry, 28 > LEAP_SECOND_TABLE
Complete leap second table from 1972 to present.
Definition LeapSeconds.hpp:36
int leap_seconds_at_tai(double tai_jd)
Get TAI - UTC offset (leap seconds) for a given TAI Julian Date.
Definition LeapSeconds.hpp:58
Scalar leap_seconds_symbolic(const Scalar &utc_jd)
Symbolic leap second lookup (smooth approximation).
Definition LeapSeconds.hpp:213
bool is_leap_second(int year, int month, int day, int hour, int min, double sec)
Check if a given UTC date/time is during a leap second.
Definition LeapSeconds.hpp:127
double next_leap_second(double utc_jd)
Get the date of the next leap second after a given date.
Definition LeapSeconds.hpp:157
double calendar_to_jd(int year, int month, int day, int hour=0, int min=0, double sec=0.0)
Convert calendar date/time to Julian Date.
Definition JulianDate.hpp:24
Leap second entry: date and cumulative offset.
Definition LeapSeconds.hpp:20
int year
Definition LeapSeconds.hpp:21
int month
Definition LeapSeconds.hpp:22
int delta_at
TAI - UTC [seconds].
Definition LeapSeconds.hpp:24
int day
Definition LeapSeconds.hpp:23