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
41#include "../ioutils/stringUtils.hpp"
42#include "../astro/sofa.hpp"
43
44
45namespace mx
46{
47namespace sys
48{
49
50/// Get the current system time in seconds.
51/** Uses timespec, so nanosecond resolution is possible.
52 *
53 * \tparam typeT is the type to return the time in [default=double].
54 * must have cast from integer types, and + and / operators defined.
55 * \tparam clk_id is the sys/time.h clock identifier [default=CLOCK_REALTIME]
56 *
57 * \retval typeT containing the current time in seconds
58 *
59 * Tests:
60 * - Verify operation of get_curr_time. \ref tests_sys_timeUtils_get_curr_time "[test doc]"
61 *
62 * \ingroup timeutils
63 */
64template <typename typeT = double, clockid_t clk_id = CLOCK_REALTIME>
65typeT get_curr_time( timespec &tsp /**<[out] a timespec to populate with the current time */ )
66{
67 clock_gettime( clk_id, &tsp );
68
69 return ( (typeT)tsp.tv_sec ) + ( (typeT)tsp.tv_nsec ) / 1e9;
70}
71
72// Specialization for most common use case.
73template <>
74double get_curr_time<double, CLOCK_REALTIME>( timespec &tsp );
75
76/// Get the current system time in seconds.
77/** Uses timespec, so nanosecond resolution is possible. This version creates a timespec internally.
78 *
79 * \overload
80 *
81 * \tparam typeT is the type to return the time in [default=double].
82 * must have cast from integer types, and + and / operators defined.
83 * \tparam clk_id is the sys/time.h clock identifier [default=CLOCK_REALTIME]
84 *
85 * \retval typeT containing the current time in seconds
86 *
87 * Tests:
88 * - Verify operation of get_curr_time. \ref tests_sys_timeUtils_get_curr_time "[test doc]"
89 *
90 * \ingroup timeutils
91 */
92template <typename typeT = double, clockid_t clk_id = CLOCK_REALTIME>
94{
95 struct timespec tsp;
96 return get_curr_time<typeT, clk_id>( tsp );
97}
98
99// Specialization for most common use case.
100template <>
101double get_curr_time<double, CLOCK_REALTIME>();
102
103/// Sleep for a specified period in seconds.
104/**
105 * Tests:
106 * - Verify operation of thread sleep functions. \ref tests_sys_timeUtils_sleep "[test doc]"
107 *
108 * \ingroup timeutils_sleep
109 */
110void sleep( unsigned sec /**< [in] the number of seconds to sleep. */ );
111
112/// Sleep for a specified period in milliseconds.
113/**
114 * Tests:
115 * - Verify operation of thread sleep functions. \ref tests_sys_timeUtils_sleep "[test doc]"
116 *
117 * \ingroup timeutils_sleep
118 */
119void milliSleep( unsigned msec /**< [in] the number of milliseconds to sleep. */ );
120
121/// Sleep for a specified period in microseconds.
122/**
123 * Tests:
124 * - Verify operation of thread sleep functions. \ref tests_sys_timeUtils_sleep "[test doc]"
125 *
126 * \ingroup timeutils_sleep
127 */
128void microSleep( unsigned usec /**< [in] the number of microseconds to sleep. */ );
129
130/// Sleep for a specified period in nanoseconds.
131/**
132 * Tests
133 * - Verify operation of thread sleep functions. \ref tests_sys_timeUtils_sleep "[test doc]"
134 *
135 * \ingroup timeutils_sleep
136 */
137void nanoSleep( unsigned nsec /**< [in] the number of microseconds to sleep. */ );
138
139/// Adds a time offset to an existing timespec
140/** The offset is specified in nanoseconds, which can be greater than 1e9.
141 *
142 * Tests:
143 * - Verify operation of timespecAddNsec. \ref tests_sys_timeUtils_timespecAddNsec "[test doc]"
144 *
145 * \ingroup timeutils
146 */
147void timespecAddNsec( timespec &ts, ///< [in.out] the time to add to
148 unsigned nsec ///< [in] the number of nanoseconds to add to ts.
149);
150
151/** Parse a string of format hh:mm:ss.s
152 * Breaks a time string into constituent parts. Handles -h by distributing the sign to m and s.
153 *
154 * \tparam floatT is a floating point type
155 *
156 * Tests
157 * - Verify parsing of a formatted time string. \ref tests_sys_timeUtils_parse_hms "[test doc]"
158 *
159 * \ingroup timeutils
160 */
161template <typename floatT>
162void parse_hms( floatT &h, ///< [out] the hour component coverted to floatT
163 floatT &m, ///< [out] the minute component converted to floatT
164 floatT &s, ///< [out] the second component converted to floatT
165 const std::string &hmsstr ///< [in] a string of format hh:mm:ss.s where ss.s can be of any precision
166)
167{
168 int st, en;
169
170 int sgn = 1;
171
172 st = 0;
173 en = hmsstr.find( ':', st );
174
175 h = ioutils::convertFromString<floatT>( hmsstr.substr( st, en - st ).c_str() );
176
177 // Check for negative
178 if( std::signbit( h ) )
179 sgn = -1;
180
181 st = en + 1;
182
183 en = hmsstr.find( ':', st );
184
185 m = sgn * ioutils::convertFromString<floatT>( hmsstr.substr( st, en - st ) );
186
187 st = en + 1;
188
189 s = sgn * ioutils::convertFromString<floatT>( hmsstr.substr( st, hmsstr.length() - st ).c_str() );
190}
191
192/// Converts a Gregorian calendar date into modified Julian date (MJD).
193/** Uses the SOFA function iauCal2jd. This is not a template in floating point
194 * because SOFA is always double precision.
195 *
196 * \retval double containing the MJD
197 * \retval <0 on error (-1 = bad year, -2 = bad month, -3 = bad day)
198 *
199 * Tests:
200 * - Verify calculation of MJD. \ref tests_sys_timeUtils_Cal2mjd "[test doc]"
201 *
202 * \ingroup timeutils
203 */
204double Cal2mjd( int yr, ///< [in] Gregorian calendar year
205 int mon, ///< [in] Gregorian calendar month
206 int day, ///< [in] Gregorian calendar day
207 int hr, ///< [in] Gregorian calendar hour
208 int min, ///< [in] Gregorian calendar minute
209 double sec ///< [in] Gregorian calendar second
210);
211
212/// Parse an ISO8601 date of the form "YYYY-MM-DDTHH:MM:SS.S" into the individual components.
213/** Parsing is currently only for the exact form above, which is the form in a FITS file.
214 * See https://en.wikipedia.org/?title=ISO_8601.
215 *
216 * \returns 0 on success
217 * \returns -4 if fdate is not long enough
218 *
219 * Tests:
220 * - Verify parsing of an ISO 8601 time string \ref tests_sys_timeUtils_ISO8601dateBreakdown "[test doc]"
221 *
222 * \ingroup timeutils
223 */
224int ISO8601dateBreakdown( int &yr, ///< [out] Gregorian calendar year
225 int &mon, ///< [out] Gregorian calendar month
226 int &day, ///< [out] Gregorian calendar day
227 int &hr, ///< [out] Gregorian calendar hour
228 int &min, ///< [out] Gregorian calendar minute
229 double &sec, ///< [out] Gregorian calendar second
230 const std::string &fdate ///< [in] is a standard ISO8601 date string
231);
232
233/// Parse an ISO8601 date of the form "YYYY-MM-DDTHH:MM:SS.S" and return the modified Julian date (MJD)
234/** Parsing is currently only for the exact form above, which is the form in a FITS file.
235 * See https://en.wikipedia.org/?title=ISO_8601.
236 * After parsing calls Cal2mjd.
237 *
238 * Tests:
239 * - Verify conversion of an ISO 8601 time string to MJD. \ref tests_sys_timeUtils_ISO8601date2mjd "[test doc]"
240 *
241 * \ingroup timeutils
242 */
243double ISO8601date2mjd( const std::string &fdate /**<[in] a standard ISO8601 date string*/ );
244
245/// Get a date-time string in ISO 8601 format
246/** For recognized time types, returns a string in the ISO 8601 format:
247 * YYYY-MM-DDYHH:MM:SS.SS, with optional timezone designation such as Z or +00:00.
248 *
249 * \tparam timeT is the time type
250 *
251 * \retval std::string containing the format date/time
252 *
253 * \ingroup timeutils
254 */
255template <typename timeT>
256std::string ISO8601DateTimeStr( const timeT &timeIn, ///< [in] the input time
257 int timeZone = 0 ///< [in] [optional] specifies whether to include a timezone
258 ///< designation. 0=> none, 1=> letter, 2=>offset.
259);
260
261/// Get a date-time string in ISO 8601 format for time_t
262/** Returns a string in the ISO 8601 format:
263 * YYYY-MM-DDYHH:MM:SS, with optional timezone designation such as Z or +00:00.
264 *
265 * \retval std::string containing the format date/time
266 *
267 * \ingroup timeutils
268 */
269template <>
270std::string ISO8601DateTimeStr<time_t>( const time_t &timeIn, ///< [in] the input time
271 int timeZone ///< [in] [optional] specifies whether to include a timezone
272 ///< designation. 0=> none, 1=> letter, 2=>offset.
273);
274
275/// Get a date-time string in ISO 8601 format for timespec
276/** Returns a string in the ISO 8601 format:
277 * YYYY-MM-DDYHH:MM:SS.SSSSSSSSS, with optional timezone designation such as Z or +00:00.
278 *
279 * \retval std::string containing the format date/time
280 *
281 * \ingroup timeutils
282 */
283template <>
284std::string ISO8601DateTimeStr<timespec>( const timespec &timeIn, ///< [in] the input time
285 int timeZone ///< [in] [optional] specifies whether to include a timezone
286 ///< designation. 0=> none, 1=> letter, 2=>offset.
287);
288
289/// Get a date-time string in ISO 8601 format for the current UTC time
290/** Returns a string in the ISO 8601 format:
291 * YYYY-MM-DDYHH:MM:SS.SS, with optional timezone designation such as Z or +00:00.
292 *
293 * \overload
294 *
295 * \retval std::string containing the format date/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
void nanoSleep(unsigned nsec)
Sleep for a specified period in nanoseconds.
Definition timeUtils.cpp:66
void sleep(unsigned sec)
Sleep for a specified period in seconds.
Definition timeUtils.cpp:51
void milliSleep(unsigned msec)
Sleep for a specified period in milliseconds.
Definition timeUtils.cpp:56
void microSleep(unsigned usec)
Sleep for a specified period in microseconds.
Definition timeUtils.cpp:61
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.
Definition timeUtils.cpp:98
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:71
void parse_hms(floatT &h, floatT &m, floatT &s, const std::string &hmsstr)
typeT get_curr_time()
Get the current system time in seconds.
Definition timeUtils.hpp:93
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:83
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 mxError.hpp:40
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.