HOPS
HOPS class reference
hops_leap_seconds.hh
Go to the documentation of this file.
1 #ifndef HOPS_LEAP_SECONDS_HH__
2 #define HOPS_LEAP_SECONDS_HH__
3 
4 #include <algorithm>
5 #include <chrono>
6 #include <cstdint>
7 #include <vector>
8 
9 //header-only portion of the H. Hinnant date library (civil calendar only, no
10 //linking required). We deliberately do NOT include "date/tz.h" here: that is the
11 //compiled part that loads the IANA timezone/leap-second database from the host
12 //at runtime and forces a link against libdate-tz.
13 #include "date/date.h"
14 
48 namespace hops
49 {
50 
57 {
58  std::chrono::seconds elapsed;
60 };
61 
62 namespace leap_detail
63 {
64 
65 //fixed continuous-scale offsets (see file header for the derivation/verification):
66 // to_utc(tai) = utc_time(tai_count - kTaiEpochOffset) [1958-01-01 gap + 10 s base]
67 // to_utc(gps) = utc_time(gps_count + kGpsEpochOffset) [1970->1980-01-06 + 9 leaps]
68 static const std::int64_t kTaiEpochOffset = 378691210;
69 static const std::int64_t kGpsEpochOffset = 315964809;
70 
71 //One leap-second event: the sys-time (unix seconds) of 00:00:00 UTC on the day
72 //AFTER the event, and the SIGNED cumulative offset (utc_count - sys_count) that
73 //applies at/after it. Storing the cumulative value rather than deriving it from
74 //the row index lets the table represent negative (removed) leap seconds too: a
75 //+1 event steps the offset up (inserted 23:59:60), a -1 event steps it down
76 //(23:59:59 skipped). Entries are sorted ascending by transition_sys.
77 struct leap_entry
78 {
79  std::int64_t transition_sys;
80  std::int64_t cumulative;
81 };
82 
83 inline const std::vector< leap_entry >& leap_table()
84 {
85  static const std::vector< leap_entry > table = [] {
86  //(year, month, day of the day-after, signed delta) for each leap event.
87  //All leaps to date are +1; any future negative leap would use -1.
88  static const struct
89  {
90  int y, m, d, delta;
91  } events[] = {
92  {1972, 7, 1, +1},
93  {1973, 1, 1, +1},
94  {1974, 1, 1, +1},
95  {1975, 1, 1, +1},
96  {1976, 1, 1, +1},
97  {1977, 1, 1, +1},
98  {1978, 1, 1, +1},
99  {1979, 1, 1, +1},
100  {1980, 1, 1, +1},
101  {1981, 7, 1, +1},
102  {1982, 7, 1, +1},
103  {1983, 7, 1, +1},
104  {1985, 7, 1, +1},
105  {1988, 1, 1, +1},
106  {1990, 1, 1, +1},
107  {1991, 1, 1, +1},
108  {1992, 7, 1, +1},
109  {1993, 7, 1, +1},
110  {1994, 7, 1, +1},
111  {1996, 1, 1, +1},
112  {1997, 7, 1, +1},
113  {1999, 1, 1, +1},
114  {2006, 1, 1, +1},
115  {2009, 1, 1, +1},
116  {2012, 7, 1, +1},
117  {2015, 7, 1, +1},
118  {2017, 1, 1, +1}
119  };
120  std::vector< leap_entry > t;
121  t.reserve(sizeof(events) / sizeof(events[0]));
122  std::int64_t cumulative = 0;
123  for(std::size_t i = 0; i < sizeof(events) / sizeof(events[0]); ++i)
124  {
125  date::year_month_day d{date::year{events[i].y}, date::month{static_cast< unsigned >(events[i].m)},
126  date::day{static_cast< unsigned >(events[i].d)}};
127  std::int64_t transition =
128  std::chrono::duration_cast< std::chrono::seconds >(date::sys_days{d}.time_since_epoch()).count();
129  cumulative += events[i].delta;
130  t.push_back(leap_entry{transition, cumulative});
131  }
132  return t;
133  }();
134  return table;
135 }
136 
137 //signed cumulative offset (utc_count - sys_count) in effect at a given unix-second
138 //value on the SYS timescale.
139 inline std::int64_t offset_at_sys(std::int64_t sys_seconds)
140 {
141  const std::vector< leap_entry >& T = leap_table();
142  std::int64_t offset = 0;
143  for(std::size_t k = 0; k < T.size(); ++k)
144  {
145  if(sys_seconds >= T[k].transition_sys)
146  {
147  offset = T[k].cumulative;
148  }
149  else
150  {
151  break;
152  }
153  }
154  return offset;
155 }
156 
157 } // namespace leap_detail
158 
159 //clock tag types. We only ever instantiate time_points with an explicit Duration,
160 //so the nested typedefs exist purely to model a Clock (and to give utc_clock a now()).
161 struct utc_clock;
162 struct tai_clock;
163 struct gps_clock;
164 template< class Duration > using utc_time = std::chrono::time_point< utc_clock, Duration >;
165 template< class Duration > using tai_time = std::chrono::time_point< tai_clock, Duration >;
166 template< class Duration > using gps_time = std::chrono::time_point< gps_clock, Duration >;
167 
171 template< class Duration > inline leap_second_info get_leap_second_info(const utc_time< Duration >& ut)
172 {
173  using namespace leap_detail;
174  const std::vector< leap_entry >& T = leap_table();
175  //floor to whole seconds (all HOPS instants are positive, so truncation == floor)
176  std::int64_t u = std::chrono::duration_cast< std::chrono::seconds >(ut.time_since_epoch()).count();
177 
178  //In the utc timescale the offset steps from C[k-1] to C[k] at the transition,
179  //and the step lands at utc threshold U_k = transition_sys + min(C[k-1], C[k]):
180  // +1 (inserted) leap: U_k = transition + C[k-1], and utc == U_k is the
181  // inserted 23:59:60 second;
182  // -1 (removed) leap: U_k = transition + C[k], with no inserted second (the
183  // skip is on the sys side, and surfaces via to_sys).
184  //U_k is strictly increasing, so scan for the last transition at/below u.
185  std::int64_t elapsed = 0;
186  long selected = -1;
187  for(std::size_t k = 0; k < T.size(); ++k)
188  {
189  std::int64_t prev = (k == 0) ? 0 : T[k - 1].cumulative;
190  std::int64_t threshold = T[k].transition_sys + std::min(prev, T[k].cumulative);
191  if(u >= threshold)
192  {
193  elapsed = T[k].cumulative;
194  selected = static_cast< long >(k);
195  }
196  else
197  {
198  break;
199  }
200  }
201 
202  bool is_ls = false;
203  if(selected >= 0)
204  {
205  std::size_t k = static_cast< std::size_t >(selected);
206  std::int64_t prev = (k == 0) ? 0 : T[k - 1].cumulative;
207  std::int64_t delta = T[k].cumulative - prev;
208  std::int64_t threshold = T[k].transition_sys + std::min(prev, T[k].cumulative);
209  //only a positive (inserted) leap has a real 23:59:60 second, one wide
210  is_ls = (delta > 0) && (u == threshold);
211  }
212  return leap_second_info{std::chrono::seconds{elapsed}, is_ls};
213 }
214 
218 struct utc_clock
219 {
220  using duration = std::chrono::system_clock::duration;
221  using rep = duration::rep;
222  using period = duration::period;
223  using time_point = std::chrono::time_point< utc_clock >;
224  static CONSTDATA bool is_steady = false;
225 
226  template< class Duration >
228  from_sys(const date::sys_time< Duration >& st)
229  {
230  using CD = typename std::common_type< Duration, std::chrono::seconds >::type;
231  std::int64_t s = std::chrono::duration_cast< std::chrono::seconds >(st.time_since_epoch()).count();
232  std::chrono::seconds leaps{leap_detail::offset_at_sys(s)};
233  return utc_time< CD >{st.time_since_epoch() + leaps};
234  }
235 
236  template< class Duration >
237  static date::sys_time< typename std::common_type< Duration, std::chrono::seconds >::type >
239  {
240  using CD = typename std::common_type< Duration, std::chrono::seconds >::type;
242  return date::sys_time< CD >{ut.time_since_epoch() - info.elapsed};
243  }
244 
245  template< class Duration >
246  static date::local_time< typename std::common_type< Duration, std::chrono::seconds >::type >
248  {
249  using CD = typename std::common_type< Duration, std::chrono::seconds >::type;
250  return date::local_time< CD >{to_sys(ut).time_since_epoch()};
251  }
252 
253  template< class Duration >
255  from_local(const date::local_time< Duration >& lt)
256  {
257  using CD = typename std::common_type< Duration, std::chrono::seconds >::type;
258  return from_sys(date::sys_time< Duration >{lt.time_since_epoch()});
259  }
260 
262  {
263  return from_sys(std::chrono::time_point_cast< std::chrono::nanoseconds >(std::chrono::system_clock::now()));
264  }
265 };
266 
270 struct tai_clock
271 {
272  using duration = std::chrono::system_clock::duration;
273  using rep = duration::rep;
274  using period = duration::period;
275  using time_point = std::chrono::time_point< tai_clock >;
276  static CONSTDATA bool is_steady = false;
277 
278  template< class Duration > static utc_time< Duration > to_utc(const tai_time< Duration >& t)
279  {
280  return utc_time< Duration >{t.time_since_epoch() - std::chrono::seconds{leap_detail::kTaiEpochOffset}};
281  }
282 
283  template< class Duration > static tai_time< Duration > from_utc(const utc_time< Duration >& t)
284  {
285  return tai_time< Duration >{t.time_since_epoch() + std::chrono::seconds{leap_detail::kTaiEpochOffset}};
286  }
287 };
288 
292 struct gps_clock
293 {
294  using duration = std::chrono::system_clock::duration;
295  using rep = duration::rep;
296  using period = duration::period;
297  using time_point = std::chrono::time_point< gps_clock >;
298  static CONSTDATA bool is_steady = false;
299 
300  template< class Duration > static utc_time< Duration > to_utc(const gps_time< Duration >& t)
301  {
302  return utc_time< Duration >{t.time_since_epoch() + std::chrono::seconds{leap_detail::kGpsEpochOffset}};
303  }
304 
305  template< class Duration > static gps_time< Duration > from_utc(const utc_time< Duration >& t)
306  {
307  return gps_time< Duration >{t.time_since_epoch() - std::chrono::seconds{leap_detail::kGpsEpochOffset}};
308  }
309 };
310 
311 } // namespace hops
312 
313 #endif
#define min(a, b)
Definition: max555.c:9
short day
Definition: mk4_typedefs.h:17
short year
Definition: mk4_typedefs.h:16
std::int64_t cumulative
Definition: hops_leap_seconds.hh:80
const std::vector< leap_entry > & leap_table()
Definition: hops_leap_seconds.hh:83
std::int64_t offset_at_sys(std::int64_t sys_seconds)
Definition: hops_leap_seconds.hh:139
std::int64_t transition_sys
Definition: hops_leap_seconds.hh:79
Definition: hops_leap_seconds.hh:78
t
Definition: picking_aedit.py:14
Definition: MHO_AdhocFlagging.hh:18
std::chrono::seconds elapsed
Definition: hops_leap_seconds.hh:58
leap_second_info get_leap_second_info(const utc_time< Duration > &ut)
Leap-second lookup for a utc_time, matching date::get_leap_second_info.
Definition: hops_leap_seconds.hh:171
bool is_leap_second
Definition: hops_leap_seconds.hh:59
std::chrono::time_point< gps_clock, Duration > gps_time
Definition: hops_leap_seconds.hh:166
std::chrono::time_point< utc_clock, Duration > utc_time
Definition: hops_leap_seconds.hh:164
std::chrono::time_point< tai_clock, Duration > tai_time
Definition: hops_leap_seconds.hh:165
Result of a leap-second lookup, mirroring date::leap_second_info. elapsed = number of leap seconds in...
Definition: hops_leap_seconds.hh:57
GPS clock: SI seconds since 1980-01-06; constant offset from UTC.
Definition: hops_leap_seconds.hh:293
static utc_time< Duration > to_utc(const gps_time< Duration > &t)
Definition: hops_leap_seconds.hh:300
duration::period period
Definition: hops_leap_seconds.hh:296
static CONSTDATA bool is_steady
Definition: hops_leap_seconds.hh:298
std::chrono::time_point< gps_clock > time_point
Definition: hops_leap_seconds.hh:297
duration::rep rep
Definition: hops_leap_seconds.hh:295
std::chrono::system_clock::duration duration
Definition: hops_leap_seconds.hh:294
static gps_time< Duration > from_utc(const utc_time< Duration > &t)
Definition: hops_leap_seconds.hh:305
TAI clock: SI seconds since 1958-01-01; constant offset from UTC.
Definition: hops_leap_seconds.hh:271
static utc_time< Duration > to_utc(const tai_time< Duration > &t)
Definition: hops_leap_seconds.hh:278
duration::period period
Definition: hops_leap_seconds.hh:274
static CONSTDATA bool is_steady
Definition: hops_leap_seconds.hh:276
std::chrono::system_clock::duration duration
Definition: hops_leap_seconds.hh:272
duration::rep rep
Definition: hops_leap_seconds.hh:273
static tai_time< Duration > from_utc(const utc_time< Duration > &t)
Definition: hops_leap_seconds.hh:283
std::chrono::time_point< tai_clock > time_point
Definition: hops_leap_seconds.hh:275
UTC clock: real SI seconds since 1970-01-01 including leap seconds.
Definition: hops_leap_seconds.hh:219
std::chrono::system_clock::duration duration
Definition: hops_leap_seconds.hh:220
static date::sys_time< typename std::common_type< Duration, std::chrono::seconds >::type > to_sys(const utc_time< Duration > &ut)
Definition: hops_leap_seconds.hh:238
std::chrono::time_point< utc_clock > time_point
Definition: hops_leap_seconds.hh:223
static CONSTDATA bool is_steady
Definition: hops_leap_seconds.hh:224
duration::rep rep
Definition: hops_leap_seconds.hh:221
static utc_time< typename std::common_type< Duration, std::chrono::seconds >::type > from_local(const date::local_time< Duration > &lt)
Definition: hops_leap_seconds.hh:255
duration::period period
Definition: hops_leap_seconds.hh:222
static date::local_time< typename std::common_type< Duration, std::chrono::seconds >::type > to_local(const utc_time< Duration > &ut)
Definition: hops_leap_seconds.hh:247
static utc_time< std::chrono::nanoseconds > now()
Definition: hops_leap_seconds.hh:261
static utc_time< typename std::common_type< Duration, std::chrono::seconds >::type > from_sys(const date::sys_time< Duration > &st)
Definition: hops_leap_seconds.hh:228