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