LCOV - code coverage report
Current view: top level - sys - timeUtils.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 100.0 % 13 13
Test Date: 2026-02-19 16:58:26 Functions: 100.0 % 1 1

            Line data    Source code
       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              : 
      45              : namespace mx
      46              : {
      47              : namespace 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              :  */
      64              : template <typename typeT = double, clockid_t clk_id = CLOCK_REALTIME>
      65              : typeT 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.
      73              : template <>
      74              : double 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              :  */
      92              : template <typename typeT = double, clockid_t clk_id = CLOCK_REALTIME>
      93              : typeT get_curr_time()
      94              : {
      95              :     struct timespec tsp;
      96              :     return get_curr_time<typeT, clk_id>( tsp );
      97              : }
      98              : 
      99              : // Specialization for most common use case.
     100              : template <>
     101              : double 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              :  */
     110              : void 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              :  */
     119              : void 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              :  */
     128              : void 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              :  */
     137              : void 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              :  */
     147              : void 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              :  */
     161              : template <typename floatT>
     162            4 : void 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            4 :     int sgn = 1;
     171              : 
     172            4 :     st = 0;
     173            4 :     en = hmsstr.find( ':', st );
     174              : 
     175            4 :     h = ioutils::stoT<floatT>( hmsstr.substr( st, en - st ).c_str() );
     176              : 
     177              :     // Check for negative
     178            4 :     if( std::signbit( h ) )
     179            1 :         sgn = -1;
     180              : 
     181            4 :     st = en + 1;
     182              : 
     183            4 :     en = hmsstr.find( ':', st );
     184              : 
     185            4 :     m = sgn * ioutils::stoT<floatT>( hmsstr.substr( st, en - st ) );
     186              : 
     187            4 :     st = en + 1;
     188              : 
     189            8 :     s = sgn * ioutils::stoT<floatT>( hmsstr.substr( st, hmsstr.length() - st ).c_str() );
     190            4 : }
     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              :  */
     204              : double 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              :  */
     224              : int 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              :  */
     243              : double 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              :  */
     255              : template <typename timeT>
     256              : std::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              :  */
     269              : template <>
     270              : std::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              :  */
     283              : template <>
     284              : std::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              : 
     300              : std::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              :  */
     310              : std::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              :  */
     322              : int 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              :  */
     343              : int 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              :  */
     353              : timespec meanTimespec( timespec ts1, ///< [in] one of the times to average
     354              :                        timespec ts2  ///< [in] the other time to average
     355              : );
     356              : 
     357              : namespace 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              :  */
     370              : bool 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              :  */
     384              : bool 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              :  */
     398              : bool 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              :  */
     412              : bool 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              :  */
     426              : bool 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              : 
     432              : namespace tsop
     433              : {
     434              : 
     435              : /// Add an amount of time specified in seconds to a timespec
     436              : /**
     437              :  * \returns the resulting timespec after addition
     438              :  */
     439              : template <typename arithT>
     440              : timespec 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              :  */
     461              : template <typename arithT>
     462              : timespec 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
        

Generated by: LCOV version 2.0-1