mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
timeUtils.hpp
Go to the documentation of this file.
1/** \file timeUtils.hpp
2 * \brief Utilities for working with time
3 *
4 * \author Jared R. Males (jaredmales@gmail.com)
5 *
6 * \ingroup utils_files
7 *
8 */
9
10//***********************************************************************//
11// Copyright 2015-2020 Jared R. Males (jaredmales@gmail.com)
12//
13// This file is part of mxlib.
14//
15// mxlib is free software: you can redistribute it and/or modify
16// it under the terms of the GNU General Public License as published by
17// the Free Software Foundation, either version 3 of the License, or
18// (at your option) any later version.
19//
20// mxlib is distributed in the hope that it will be useful,
21// but WITHOUT ANY WARRANTY; without even the implied warranty of
22// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23// GNU General Public License for more details.
24//
25// You should have received a copy of the GNU General Public License
26// along with mxlib. If not, see <http://www.gnu.org/licenses/>.
27//***********************************************************************//
28
29#ifndef timeUtils_hpp
30#define timeUtils_hpp
31
32#include <time.h>
33#include <sys/time.h>
34#include <cmath>
35
36#include <thread>
37#include <chrono>
38
39#include <iostream>
40
42#include "../astro/sofa.hpp"
43
44namespace mx
45{
46namespace sys
47{
48
49namespace timeUtilsDetail
50{
51
52/** \cond */
53/// Function signature for the broken-down UTC time operation used by time utilities.
54using gmtimeRT = tm *(*)( const time_t *, tm * );
55
56/// Function signature for the SOFA TAI-minus-UTC operation used by time utilities.
57using iauDatT = int ( * )( int, int, int, double, double * );
58
59/// Function signature for the SOFA calendar-to-Julian-date operation used by time utilities.
60using iauCal2jdT = int ( * )( int, int, int, double *, double * );
61
62/// Resettable external operations used to exercise time-conversion failures deterministically.
63struct operations
64{
65 gmtimeRT gmtimeR; ///< Broken-down UTC time operation.
66 iauDatT iauDat; ///< TAI-minus-UTC operation.
67 iauCal2jdT iauCal2jd; ///< Calendar-to-Julian-date operation.
68};
69
70/// Access the process-wide time utility operation table.
71operations &operationsInstance();
72
73/// Restore the time utility operation table to its production functions.
74void resetOperations();
75/** \endcond */
76
77} // namespace timeUtilsDetail
78
79/// Get the current system time in seconds.
80/** Uses timespec, so nanosecond resolution is possible.
81 *
82 * \tparam typeT is the type to return the time in [default=double].
83 * must have cast from integer types, and + and / operators defined.
84 * \tparam clk_id is the sys/time.h clock identifier [default=CLOCK_REALTIME]
85 *
86 * \retval typeT containing the current time in seconds
87 *
88 * \ingroup timeutils
89 */
90template <typename typeT = double, clockid_t clk_id = CLOCK_REALTIME>
91typeT get_curr_time( timespec &tsp /**<[out] a timespec to populate with the current time */ )
92{
93 clock_gettime( clk_id, &tsp );
94
95 return ( (typeT)tsp.tv_sec ) + ( (typeT)tsp.tv_nsec ) / 1e9;
96}
97
98// Specialization for most common use case.
99template <>
100double get_curr_time<double, CLOCK_REALTIME>( timespec &tsp );
101
102/// Get the current system time in seconds.
103/** Uses timespec, so nanosecond resolution is possible. This version creates a timespec internally.
104 *
105 * \overload
106 *
107 * \tparam typeT is the type to return the time in [default=double].
108 * must have cast from integer types, and + and / operators defined.
109 * \tparam clk_id is the sys/time.h clock identifier [default=CLOCK_REALTIME]
110 *
111 * \retval typeT containing the current time in seconds
112 *
113 * \ingroup timeutils
114 */
115template <typename typeT = double, clockid_t clk_id = CLOCK_REALTIME>
117{
118 struct timespec tsp;
119 return get_curr_time<typeT, clk_id>( tsp );
120}
121
122// Specialization for most common use case.
123template <>
124double get_curr_time<double, CLOCK_REALTIME>();
125
126/// Sleep for a specified period in seconds.
127/**
128 * \ingroup timeutils_sleep
129 */
130void sleep( unsigned sec /**< [in] the number of seconds to sleep. */ );
131
132/// Sleep for a specified period in milliseconds.
133/**
134 * \ingroup timeutils_sleep
135 */
136void milliSleep( unsigned msec /**< [in] the number of milliseconds to sleep. */ );
137
138/// Sleep for a specified period in microseconds.
139/**
140 * \ingroup timeutils_sleep
141 */
142void microSleep( unsigned usec /**< [in] the number of microseconds to sleep. */ );
143
144/// Sleep for a specified period in nanoseconds.
145/**
146 * \ingroup timeutils_sleep
147 */
148void nanoSleep( unsigned nsec /**< [in] the number of microseconds to sleep. */ );
149
150/// Adds a time offset to an existing timespec
151/** The offset is specified in nanoseconds, which can be greater than 1e9.
152 *
153 * \ingroup timeutils
154 */
155void timespecAddNsec( timespec &ts, ///< [in.out] the time to add to
156 unsigned nsec ///< [in] the number of nanoseconds to add to ts.
157);
158
159/** Parse a string of format hh:mm:ss.s
160 * Breaks a time string into constituent parts. Handles -h by distributing the sign to m and s.
161 *
162 * \tparam floatT is a floating point type
163 *
164 * \ingroup timeutils
165 */
166template <typename floatT>
167void parse_hms( floatT &h, ///< [out] the hour component coverted to floatT
168 floatT &m, ///< [out] the minute component converted to floatT
169 floatT &s, ///< [out] the second component converted to floatT
170 const std::string &hmsstr ///< [in] a string of format hh:mm:ss.s where ss.s can be of any precision
171)
172{
173 int st, en;
174
175 int sgn = 1;
176
177 st = 0;
178 en = hmsstr.find( ':', st );
179
180 h = ioutils::stoT<floatT>( hmsstr.substr( st, en - st ).c_str() );
181
182 // Check for negative
183 if( std::signbit( h ) )
184 sgn = -1;
185
186 st = en + 1;
187
188 en = hmsstr.find( ':', st );
189
190 m = sgn * ioutils::stoT<floatT>( hmsstr.substr( st, en - st ) );
191
192 st = en + 1;
193
194 s = sgn * ioutils::stoT<floatT>( hmsstr.substr( st, hmsstr.length() - st ).c_str() );
195}
196
197/// Converts a Gregorian calendar date into modified Julian date (MJD).
198/** Uses the SOFA function iauCal2jd. This is not a template in floating point
199 * because SOFA is always double precision.
200 *
201 * \retval double containing the MJD
202 * \retval <0 on error (-1 = bad year, -2 = bad month, -3 = bad day)
203 *
204 * \ingroup timeutils
205 */
206double Cal2mjd( int yr, ///< [in] Gregorian calendar year
207 int mon, ///< [in] Gregorian calendar month
208 int day, ///< [in] Gregorian calendar day
209 int hr, ///< [in] Gregorian calendar hour
210 int min, ///< [in] Gregorian calendar minute
211 double sec ///< [in] Gregorian calendar second
212);
213
214/// Parse an ISO8601 date of the form "YYYY-MM-DDTHH:MM:SS.S" into the individual components.
215/** Parsing is currently only for the exact form above, which is the form in a FITS file.
216 * See https://en.wikipedia.org/?title=ISO_8601.
217 *
218 * \returns 0 on success
219 * \returns -4 if fdate is not long enough
220 *
221 * \ingroup timeutils
222 */
223int ISO8601dateBreakdown( int &yr, ///< [out] Gregorian calendar year
224 int &mon, ///< [out] Gregorian calendar month
225 int &day, ///< [out] Gregorian calendar day
226 int &hr, ///< [out] Gregorian calendar hour
227 int &min, ///< [out] Gregorian calendar minute
228 double &sec, ///< [out] Gregorian calendar second
229 const std::string &fdate ///< [in] is a standard ISO8601 date string
230);
231
232/// Parse an ISO8601 date of the form "YYYY-MM-DDTHH:MM:SS.S" and return the modified Julian date (MJD)
233/** Parsing is currently only for the exact form above, which is the form in a FITS file.
234 * See https://en.wikipedia.org/?title=ISO_8601.
235 * After parsing calls Cal2mjd.
236 *
237 * \ingroup timeutils
238 */
239double ISO8601date2mjd( const std::string &fdate /**<[in] a standard ISO8601 date string*/ );
240
241/// Get a date-time string in ISO 8601 format
242/** For recognized time types, returns a string in the ISO 8601 format:
243 * YYYY-MM-DDYHH:MM:SS.SS, with optional timezone designation such as Z or +00:00.
244 *
245 * \tparam timeT is the time type
246 *
247 * \retval std::string containing the format date/time
248 * \retval empty string if the input cannot be converted to broken-down UTC time
249 *
250 * \ingroup timeutils
251 */
252template <typename timeT>
253std::string ISO8601DateTimeStr( const timeT &timeIn, ///< [in] the input time
254 int timeZone = 0 ///< [in] [optional] specifies whether to include a timezone
255 ///< designation. 0=> none, 1=> letter, 2=>offset.
256);
257
258/// Get a date-time string in ISO 8601 format for time_t
259/** Returns a string in the ISO 8601 format:
260 * YYYY-MM-DDYHH:MM:SS, with optional timezone designation such as Z or +00:00.
261 *
262 * \retval std::string containing the format date/time
263 * \retval empty string if the input cannot be converted to broken-down UTC time
264 *
265 * \ingroup timeutils
266 */
267template <>
268std::string ISO8601DateTimeStr<time_t>( const time_t &timeIn, ///< [in] the input time
269 int timeZone ///< [in] [optional] specifies whether to include a timezone
270 ///< designation. 0=> none, 1=> letter, 2=>offset.
271);
272
273/// Get a date-time string in ISO 8601 format for timespec
274/** Returns a string in the ISO 8601 format:
275 * YYYY-MM-DDYHH:MM:SS.SSSSSSSSS, with optional timezone designation such as Z or +00:00.
276 *
277 * \retval std::string containing the format date/time
278 * \retval empty string if the input cannot be converted to broken-down UTC time
279 *
280 * \ingroup timeutils
281 */
282template <>
283std::string ISO8601DateTimeStr<timespec>( const timespec &timeIn, ///< [in] the input time
284 int timeZone ///< [in] [optional] specifies whether to include a timezone
285 ///< designation. 0=> none, 1=> letter, 2=>offset.
286);
287
288/// Get a date-time string in ISO 8601 format for the current UTC time
289/** Returns a string in the ISO 8601 format:
290 * YYYY-MM-DDYHH:MM:SS.SS, with optional timezone designation such as Z or +00:00.
291 *
292 * \overload
293 *
294 * \retval std::string containing the format date/time
295 * \retval empty string if the current time cannot be converted to broken-down UTC time
296 *
297 * \ingroup timeutils
298 */
299
300std::string ISO8601DateTimeStr(int timeZone = 0 /**< [in] [optional] specifies whether to include a timezone designation. 0=> none, 1=> letter, 2=>offset.*/);
301
302/// Get a date-time string in ISO 8601 format for an MJD
303/** Returns a string in the ISO 8601 format:
304 * YYYY-MM-DDYHH:MM:SS.SSSSSSSSS, with optional timezone designation such as Z or +00:00.
305 *
306 * \retval std::string containing the format date/time
307 *
308 * \ingroup timeutils
309 */
310std::string ISO8601DateTimeStrMJD(
311 const double &timeIn, ///< [in] the input time
312 int timeZone = 0 ///< [in] specifies whether to include a timezone designation. 0=> none, 1=> letter, 2=>offset.
313);
314/// Get a timestamp string in the form YYYYMMDDHHMMSS.SSSSSSSSS
315/** Assumes the input timespec is in UTC.
316 *
317 * \returns 0 on success
318 * \returns -1 on error.
319 *
320 * \ingroup timeutils
321 */
322int timeStamp( std::string &tstamp, ///< [out] the string to hold the formatted time
323 timespec &ts ///< [in] the timespec from which to produce the timestamp string
324);
325
326/// Convert a UTC timespec to TAI modified Julian date
327/** Converts a timespec assumed to be in <a href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time">Coordinated
328 * Universal Time (UTC)</a> to a <a href="https://en.wikipedia.org/wiki/Julian_day">
329 * Modified Julian Date (MJD)</a> in
330 * <a href="https://en.wikipedia.org/wiki/International_Atomic_Time">International Atomic Time (TAI)</a>.
331 *
332 * \retval 1 SOFA dubious year [see SOFA documentation for iauDat]
333 * \retval 0 success
334 * \retval -1 SOFA bad year [see SOFA documentation for iauDat and iauCal2jd]
335 * \retval -2 SOFA bad month [see SOFA documentation for iauDat and iauCal2jd]
336 * \retval -3 SOFA bad day [see SOFA documentation for iauDat and iauCal2jd]
337 * \retval -4 SOFA bad fractional day [see SOFA documentation for iauDat and iauCal2jd]
338 * \retval -5 SOFA internal error [see SOFA documentation for iauDat and iauCal2jd]
339 * \retval -10 gmtime_r returned error, check errno
340 *
341 * \ingroup timeutils
342 */
343int timespecUTC2TAIMJD( double &djm, ///< [out] the modified Julian day number
344 double &djmf, ///< [out] the fraction of the day
345 const timespec &tsp, ///< [in] contains the UTC time
346 tm *tm0 ///< [out] [optional] will be filled with the broken down UTC time
347);
348
349/// Calculate the mean time of two times given by timespecs
350/**
351 * \returns the mean value of the inputs.
352 */
353timespec meanTimespec( timespec ts1, ///< [in] one of the times to average
354 timespec ts2 ///< [in] the other time to average
355);
356
357namespace tscomp
358{
359
360/// Timespec comparison operator < (see caveats)
361/** Caveats:
362 * - If the inputs are in UTC (or similar scale) this does not account for leap seconds
363 * - Assumes that the `tv_nsec` field does not exceed 999999999 nanoseconds
364 *
365 * \returns true if tsL is earlier than tsR
366 * \returns false otherwise
367 *
368 * \ingroup timeutils_tscomp
369 */
370bool operator<( timespec const &tsL, ///< [in] the left hand side of the comparison
371 timespec const &tsR ///< [in] the right hand side of the comparison
372);
373
374/// Timespec comparison operator > (see caveats)
375/** Caveats:
376 * - If the inputs are in UTC (or similar scale) this does not account for leap seconds
377 * - Assumes that the `tv_nsec` field does not exceed 999999999 nanoseconds
378 *
379 * \returns true if tsL is later than tsR
380 * \returns false otherwise
381 *
382 * \ingroup timeutils_tscomp
383 */
384bool operator>( timespec const &tsL, ///< [in] the left hand side of the comparison
385 timespec const &tsR ///< [in] the right hand side of the comparison
386);
387
388/// Timespec comparison operator == (see caveats)
389/** Caveats:
390 * - If the inputs are in UTC (or similar scale) this does not account for leap seconds
391 * - Assumes that the `tv_nsec` field does not exceed 999999999 nanoseconds
392 *
393 * \returns true if tsL is exactly the same as tsR
394 * \returns false otherwise
395 *
396 * \ingroup timeutils_tscomp
397 */
398bool operator==( timespec const &tsL, ///< [in] the left hand side of the comparison
399 timespec const &tsR ///< [in] the right hand side of the comparison
400);
401
402/// Timespec comparison operator <= (see caveats)
403/** Caveats:
404 * - If the inputs are in UTC (or similar scale) this does not account for leap seconds
405 * - Assumes that the `tv_nsec` field does not exceed 999999999 nanoseconds.
406 *
407 * \returns true if tsL is earlier than or exactly equal to tsR
408 * \returns false otherwise
409 *
410 * \ingroup timeutils_tscomp
411 */
412bool operator<=( timespec const &tsL, ///< [in] the left hand side of the comparison
413 timespec const &tsR ///< [in] the right hand side of the comparison
414);
415
416/// Timespec comparison operator >= (see caveats)
417/** Caveats:
418 * - If the inputs are in UTC (or similar scale) this does not account for leap seconds
419 * - Assumes that the `tv_nsec` field does not exceed 999999999 nanoseconds
420 *
421 * \returns true if tsL is exactly equal to or is later than tsR
422 * \returns false otherwise
423 *
424 * \ingroup timeutils_tscomp
425 */
426bool operator>=( timespec const &tsL, ///< [in] the left hand side of the comparison
427 timespec const &tsR ///< [in] the right hand side of the comparison
428);
429
430} // namespace tscomp
431
432namespace tsop
433{
434
435/// Add an amount of time specified in seconds to a timespec
436/**
437 * \returns the resulting timespec after addition
438 */
439template <typename arithT>
440timespec operator+( timespec ts, ///< [in] the timespec to add to
441 arithT add ///< [in] the seconds to add
442)
443{
444 ts.tv_sec += floor( add );
445
446 ts.tv_nsec += ( add - floor( add ) ) * 1e9;
447
448 if( ts.tv_nsec >= 1e9 )
449 {
450 ts.tv_sec += 1;
451 ts.tv_nsec -= 1e9;
452 }
453
454 return ts;
455}
456
457/// Subtract an amount of time specified in seconds from a timespec
458/**
459 * \returns the resulting timespec after subtraction
460 */
461template <typename arithT>
462timespec operator-( timespec ts, ///< [in] the timespec to subtract from
463 arithT sub ///< [in] the seconds to subtract
464)
465{
466 ts.tv_sec -= floor( sub );
467
468 ts.tv_nsec -= ( sub - floor( sub ) ) * 1e9;
469
470 if( ts.tv_nsec < 0 )
471 {
472 ts.tv_sec -= 1;
473 ts.tv_nsec += 1e9;
474 }
475
476 return ts;
477}
478
479} // namespace tsop
480
481} // namespace sys
482} // namespace mx
483
484#endif // timeUtils_hpp
typeT stoT(const std::string &str, error_t *errc=nullptr)
Convert a string to a numerical value.
void nanoSleep(unsigned nsec)
Sleep for a specified period in nanoseconds.
Definition timeUtils.cpp:82
void sleep(unsigned sec)
Sleep for a specified period in seconds.
Definition timeUtils.cpp:67
void milliSleep(unsigned msec)
Sleep for a specified period in milliseconds.
Definition timeUtils.cpp:72
void microSleep(unsigned usec)
Sleep for a specified period in microseconds.
Definition timeUtils.cpp:77
bool operator<(timespec const &tsL, timespec const &tsR)
Timespec comparison operator < (see caveats).
bool operator==(timespec const &tsL, timespec const &tsR)
Timespec comparison operator == (see caveats).
bool operator>(timespec const &tsL, timespec const &tsR)
Timespec comparison operator > (see caveats).
bool operator>=(timespec const &tsL, timespec const &tsR)
Timespec comparison operator >= (see caveats).
bool operator<=(timespec const &tsL, timespec const &tsR)
Timespec comparison operator <= (see caveats).
int ISO8601dateBreakdown(int &yr, int &mon, int &day, int &hr, int &min, double &sec, const std::string &fdate)
Parse an ISO8601 date of the form "YYYY-MM-DDTHH:MM:SS.S" into the individual components.
std::string ISO8601DateTimeStr(const timeT &timeIn, int timeZone=0)
Get a date-time string in ISO 8601 format.
std::string ISO8601DateTimeStrMJD(const double &timeIn, int timeZone=0)
Get a date-time string in ISO 8601 format for an MJD.
int timespecUTC2TAIMJD(double &djm, double &djmf, const timespec &tsp, tm *tm0)
Convert a UTC timespec to TAI modified Julian date.
std::string ISO8601DateTimeStr< timespec >(const timespec &timeIn, int timeZone)
Get a date-time string in ISO 8601 format for timespec.
void timespecAddNsec(timespec &ts, unsigned nsec)
Adds a time offset to an existing timespec.
Definition timeUtils.cpp:87
void parse_hms(floatT &h, floatT &m, floatT &s, const std::string &hmsstr)
typeT get_curr_time()
Get the current system time in seconds.
int timeStamp(std::string &tstamp, timespec &ts)
Get a timestamp string in the form YYYYMMDDHHMMSS.SSSSSSSSS.
double Cal2mjd(int yr, int mon, int day, int hr, int min, double sec)
Converts a Gregorian calendar date into modified Julian date (MJD).
Definition timeUtils.cpp:99
double ISO8601date2mjd(const std::string &fdate)
Parse an ISO8601 date of the form "YYYY-MM-DDTHH:MM:SS.S" and return the modified Julian date (MJD).
std::string ISO8601DateTimeStr< time_t >(const time_t &timeIn, int timeZone)
Get a date-time string in ISO 8601 format for time_t.
The mxlib c++ namespace.
Definition mxlib.hpp:37
Wrapper for the sofa library headers, adding a namespace.
Utilities for working with strings.
timespec operator-(timespec ts, arithT sub)
Subtract an amount of time specified in seconds from a timespec.
timespec operator+(timespec ts, arithT add)
Add an amount of time specified in seconds to a timespec.
timespec meanTimespec(timespec ts1, timespec ts2)
Calculate the mean time of two times given by timespecs.