LCOV - code coverage report
Current view: top level - snapdev - timespec_ex.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 322 323 99.7 %
Date: 2023-05-29 16:11:08 Functions: 51 52 98.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2021-2023  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/snapdev
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software: you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation, either version 3 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : #pragma once
      19             : 
      20             : /** \file
      21             :  * \brief Class extending the timespec structure.
      22             :  *
      23             :  * The C library includes a timespec structure and various functions used
      24             :  * to retrieve the system time. We also offer ways to compute durations
      25             :  * and add/remove a duration from a timespec.
      26             :  *
      27             :  * The class also supports convertions to string, negation, and a few
      28             :  * other features.
      29             :  */
      30             : 
      31             : // self
      32             : //
      33             : #include    <snapdev/join_strings.h>
      34             : #include    <snapdev/tokenize_format.h>
      35             : 
      36             : 
      37             : 
      38             : // libexcept
      39             : //
      40             : #include    <libexcept/exception.h>
      41             : 
      42             : 
      43             : // C++
      44             : //
      45             : #include    <cmath>
      46             : #include    <cstdint>
      47             : #include    <iomanip>
      48             : #include    <iostream>
      49             : #include    <sstream>
      50             : 
      51             : 
      52             : // C
      53             : //
      54             : #include    <langinfo.h>
      55             : #include    <stdlib.h>
      56             : #include    <string.h>
      57             : #include    <sys/time.h>
      58             : #include    <time.h>
      59             : 
      60             : 
      61             : 
      62             : 
      63             : extern "C" {
      64             : /** \brief Define the nl_langinfo() function type.
      65             :  *
      66             :  * This is primarily used to allow for testing with any format which
      67             :  * the test can control through an nl_langinfo() wrapper.
      68             :  *
      69             :  * See nl_langinfo(3) for details about the actualy function.
      70             :  */
      71             : typedef char *(nl_langinfo_func_t)(nl_item item);
      72             : }
      73             : 
      74             : 
      75             : namespace snapdev
      76             : {
      77             : 
      78           5 : DECLARE_MAIN_EXCEPTION(timespec_ex_exception);
      79             : 
      80           0 : DECLARE_EXCEPTION(timespec_ex_exception, clock_error);
      81           2 : DECLARE_EXCEPTION(timespec_ex_exception, syntax_error);
      82           3 : DECLARE_EXCEPTION(timespec_ex_exception, overflow);
      83             : 
      84             : 
      85             : class timespec_ex
      86             :     : public timespec
      87             : {
      88             : public:
      89             :     /** \brief Initialize a timespec_ex to zero.
      90             :      *
      91             :      * This constructor is used to initialize a new timespec_ex to the
      92             :      * default value, which is 0.
      93             :      */
      94          45 :     timespec_ex()
      95          45 :     {
      96          45 :         set(0L);
      97          45 :     }
      98             : 
      99             : 
     100             :     /** \brief Initialize a timespec_ex from another.
     101             :      *
     102             :      * This constructors allows you to directly copy a timespec_ex
     103             :      * in another new timespec_ex object.
     104             :      *
     105             :      * \param[in] t  The timespec_ex to directly copy.
     106             :      */
     107          64 :     timespec_ex(timespec_ex const & t)
     108          64 :     {
     109          64 :         set(t);
     110          64 :     }
     111             : 
     112             : 
     113             :     /** \brief Initialize a timespec_ex from a timespec structure.
     114             :      *
     115             :      * This constructors allows you to directly set a timespec_ex
     116             :      * to the specified timespec values.
     117             :      *
     118             :      * \param[in] t  The timespec to directly copy.
     119             :      */
     120          68 :     timespec_ex(timespec const & t)
     121          68 :     {
     122          68 :         set(t);
     123          68 :     }
     124             : 
     125             : 
     126             :     /** \brief Initialize a timespec_ex from seconds and nanoseconds.
     127             :      *
     128             :      * This constructors allows you to directly initialize a timespec_ex
     129             :      * from seconds and nanoseconds.
     130             :      *
     131             :      * To create a valid timespec_ex object, you must pass a number
     132             :      * between 0 and 999'999'999 for the \p nsec parameter.
     133             :      *
     134             :      * \param[in] sec  The number of seconds.
     135             :      * \param[in] nsec  The number of nano-seconds.
     136             :      */
     137           7 :     timespec_ex(time_t sec, long nsec)
     138           7 :     {
     139           7 :         set(sec, nsec);
     140           7 :     }
     141             : 
     142             : 
     143             : #if 0
     144             : // keeping this just in case, for older versions of g++ or other C++ compiler
     145             : // which may fail
     146             : //
     147             :     /** \brief Initialize a timespec_ex from an int in seconds.
     148             :      *
     149             :      * This constructors allows us to compile our tests.
     150             :      *
     151             :      * \bug
     152             :      * It is unfortunate that at the moment this constructor is required.
     153             :      * This is likely to cause bugs because the choice between int64_t
     154             :      * and int is not a good choice here.
     155             :      *
     156             :      * \param[in] sec  The seconds to copy to timespec_ex.
     157             :      */
     158             :     timespec_ex(int sec)
     159             :     {
     160             :         set(sec * static_cast<std::int64_t>(1'000'000'000));
     161             :     }
     162             : #endif
     163             : 
     164             : 
     165             :     /** \brief Initialize a timespec_ex from an int64_t in nanoseconds.
     166             :      *
     167             :      * This constructors allows you to directly set a timespec_ex
     168             :      * to the specified \p nsec value.
     169             :      *
     170             :      * \param[in] nsec  The nano-seconds to copy to timespec_ex.
     171             :      */
     172          28 :     timespec_ex(std::int64_t nsec)
     173          28 :     {
     174          28 :         set(nsec);
     175          28 :     }
     176             : 
     177             : 
     178             :     /** \brief Initialize a timespec_ex from an double in seconds.
     179             :      *
     180             :      * This constructors allows you to directly set a timespec_ex
     181             :      * to the specified \p sec value.
     182             :      *
     183             :      * \param[in] sec  The seconds to copy to timespec_ex.
     184             :      */
     185          14 :     timespec_ex(double sec)
     186          14 :     {
     187          14 :         set(sec);
     188          14 :     }
     189             : 
     190             : 
     191             :     /** \brief Initialize a timespec_ex from a timestamp.
     192             :      *
     193             :      * When using the ostream << operator, you generate a simple Unix
     194             :      * timestamp with a nanosecond precision. This is the converse
     195             :      * function which you can use to convert the value back to a
     196             :      * timespec_ex value.
     197             :      *
     198             :      * \warning
     199             :      * This will not work properly if you used the to_string() function,
     200             :      * even if you used the format "%s.%N". In that case, make sure to
     201             :      * use the from_string() function instead.
     202             :      *
     203             :      * \param[in] timestamp  The string to convert to a timespec_ex.
     204             :      */
     205           4 :     timespec_ex(std::string const & timestamp)
     206           4 :     {
     207           4 :         set(timestamp);
     208           1 :     }
     209             : 
     210             : 
     211             :     /** \brief Set the timespec_ex to the specified timespec_ex.
     212             :      *
     213             :      * This function copies the specified timespec_ex (\p t) to this
     214             :      * timespec_ex object.
     215             :      *
     216             :      * \param[in] t  The timespec to copy in this timespec_ex.
     217             :      *
     218             :      * \return A reference to this object.
     219             :      */
     220          48 :     timespec_ex & operator = (timespec_ex t)
     221             :     {
     222          48 :         tv_sec = t.tv_sec;
     223          48 :         tv_nsec = t.tv_nsec;
     224          48 :         return *this;
     225             :     }
     226             : 
     227             : 
     228             :     /** \brief Set the timespec_ex to the specified timespec.
     229             :      *
     230             :      * This function copies the specified timespec to this timespec_ex
     231             :      * object.
     232             :      *
     233             :      * \param[in] t  The timespec to copy in this timespec_ex.
     234             :      *
     235             :      * \return A reference to this object.
     236             :      */
     237           1 :     timespec_ex & operator = (timespec const & t)
     238             :     {
     239           1 :         tv_sec = t.tv_sec;
     240           1 :         tv_nsec = t.tv_nsec;
     241           1 :         return *this;
     242             :     }
     243             : 
     244             : 
     245             :     /** \brief Set the timespec_ex to the number of nanoseconds.
     246             :      *
     247             :      * This function saves the number of nanoseconds in \p nsec as a
     248             :      * tv_sec and tv_nsec representation.
     249             :      *
     250             :      * \param[in] nsec  The nano-seconds to save in this timespec_ex.
     251             :      *
     252             :      * \return A reference to this object.
     253             :      */
     254           1 :     timespec_ex & operator = (std::int64_t nsec)
     255             :     {
     256           1 :         set(nsec);
     257           1 :         return *this;
     258             :     }
     259             : 
     260             : 
     261             :     /** \brief Set this timespec_ex to the number of seconds in \p sec.
     262             :      *
     263             :      * This function transforms the specified double \p sec in a timespec_ex.
     264             :      *
     265             :      * \note
     266             :      * At this time, the number of nanoseconds is floored.
     267             :      *
     268             :      * \param[in] sec  The number of seconds defined in a double.
     269             :      *
     270             :      * \return A reference to this object.
     271             :      */
     272           3 :     timespec_ex & operator = (double sec)
     273             :     {
     274           3 :         set(sec);
     275           3 :         return *this;
     276             :     }
     277             : 
     278             : 
     279             :     /** \brief Set this timespec_ex to a timestamp.
     280             :      *
     281             :      * When using the ostream << operator, you generate a simple Unix
     282             :      * timestamp with a nanosecond precision. This is the converse
     283             :      * function which you can use to convert the value back to a
     284             :      * timespec_ex value.
     285             :      *
     286             :      * \warning
     287             :      * This will not work properly if you used the to_string() function,
     288             :      * even if you used the format "%s.%N". In that case, make sure to
     289             :      * use the from_string() function instead.
     290             :      *
     291             :      * \param[in] timestamp  The string to convert to a timespec_ex.
     292             :      *
     293             :      * \return A reference to this object.
     294             :      */
     295           1 :     timespec_ex & operator = (std::string const & timestamp)
     296             :     {
     297           1 :         set(timestamp);
     298           1 :         return *this;
     299             :     }
     300             : 
     301             : 
     302             :     /** \brief Set the timespec_ex to the number of nanoseconds.
     303             :      *
     304             :      * This function saves the number of nanoseconds in \p nsec as a
     305             :      * tv_sec and tv_nsec representation.
     306             :      *
     307             :      * \param[in] t  The nano-seconds to save in this timespec_ex.
     308             :      *
     309             :      * \return A reference to this object.
     310             :      */
     311          64 :     void set(timespec_ex const & t)
     312             :     {
     313          64 :         tv_sec = t.tv_sec;
     314          64 :         tv_nsec = t.tv_nsec;
     315          64 :     }
     316             : 
     317             : 
     318             :     /** \brief Set the timespec_ex to the specified timespec.
     319             :      *
     320             :      * This function copies the timespec in \p t in this timespec_ex object.
     321             :      *
     322             :      * \param[in] t  The timespec to save in this timespec_ex.
     323             :      *
     324             :      * \return A reference to this object.
     325             :      */
     326          68 :     void set(timespec const & t)
     327             :     {
     328          68 :         tv_sec = t.tv_sec;
     329          68 :         tv_nsec = t.tv_nsec;
     330          68 :     }
     331             : 
     332             : 
     333             :     /** \brief Set the timespec_ex to the specified values.
     334             :      *
     335             :      * This function allows you to set the timespec_ex to the specified
     336             :      * number of seconds (\p sec) and nano-seconds (\p nsec).
     337             :      *
     338             :      * \param[in] sec  The new number of seconds.
     339             :      * \param[in] nsec  The new number of nano-seconds.
     340             :      */
     341           7 :     void set(time_t sec, long nsec)
     342             :     {
     343           7 :         tv_sec = sec;
     344           7 :         tv_nsec = nsec;
     345           7 :     }
     346             : 
     347             : 
     348             :     /** \brief Set the timespec_ex to the number of nanoseconds.
     349             :      *
     350             :      * This function saves the number of nanoseconds in \p nsec as a
     351             :      * tv_sec and tv_nsec representation.
     352             :      *
     353             :      * \param[in] nsec  The nano-seconds to save in this timespec_ex.
     354             :      *
     355             :      * \return A reference to this object.
     356             :      */
     357          74 :     void set(std::int64_t nsec)
     358             :     {
     359          74 :         bool const neg(nsec < 0);
     360          74 :         if(neg)
     361             :         {
     362           8 :             nsec = -nsec;
     363             :         }
     364          74 :         tv_sec = static_cast<time_t>(nsec / 1'000'000'000LL);
     365          74 :         tv_nsec = static_cast<long>(nsec % 1'000'000'000LL);
     366          74 :         if(neg)
     367             :         {
     368           8 :             *this = -*this;
     369             :         }
     370          74 :     }
     371             : 
     372             : 
     373             :     /** \brief Set this timespec_ex to the number of seconds in \p sec.
     374             :      *
     375             :      * This function transforms the specified double \p sec in a timespec_ex.
     376             :      *
     377             :      * \note
     378             :      * At this time, the number of nanoseconds is floored.
     379             :      *
     380             :      * \param[in] sec  The number of seconds defined in a double.
     381             :      *
     382             :      * \return A reference to this object.
     383             :      */
     384          17 :     void set(double sec)
     385             :     {
     386          17 :         bool const neg(sec < 0.0);
     387          17 :         if(neg)
     388             :         {
     389           1 :             sec = -sec;
     390             :         }
     391          17 :         tv_sec = static_cast<time_t>(floor(sec));
     392          17 :         tv_nsec = static_cast<long>((sec - floor(sec)) * 1.0e9);
     393          17 :         if(neg)
     394             :         {
     395           1 :             *this = -*this;
     396             :         }
     397          17 :     }
     398             : 
     399             :     /** \brief Convert a timestamp in a string to a timespec_ex structure.
     400             :      *
     401             :      * This function converts the timestamp as generated by the ostream
     402             :      * << operator back into a timespec_ex structure.
     403             :      *
     404             :      * The parser here assumes that the input is a valid Unix UTC timestamp
     405             :      * with seconds and nanoseconds separated by a period.
     406             :      *
     407             :      * The number may be negative (the number of seconds is a signed number).
     408             :      *
     409             :      * The number can end with an 's' representing the unit "second".
     410             :      *
     411             :      * The function trims the input string (ignores the leading and ending
     412             :      * spaces).
     413             :      *
     414             :      * Examples of valid input:
     415             :      *
     416             :      * \code
     417             :      *     "1685372468.564231883"
     418             :      *     "1685372468.564231883s"
     419             :      *     "  1685372468.564231883s  "
     420             :      *     "1685372468.564"
     421             :      * \endcode
     422             :      *
     423             :      * Note that the precision does not need to be to the nanosecond. The
     424             :      * function properly interprets the number after the decimal point
     425             :      * by reading the equivalent of 9 digits, using '0' when all 9 digits
     426             :      * are not present.
     427             :      *
     428             :      * \exception overflow
     429             :      * If the number of seconds is too large, this exception is raised.
     430             :      *
     431             :      * \exception syntax_error
     432             :      * The number of seconds must be indicated with at least one digit,
     433             :      * even if zero (0). If the number starts with any other character,
     434             :      * this exception is raised. Further, if the string includes any
     435             :      * character other than digits, a decimal point, and the 's' unit
     436             :      * at the end, then a syntax error is also raised.
     437             :      *
     438             :      * \param[in] timestamp  The string to convert to a timespec_ex.
     439             :      */
     440          18 :     void set(std::string const & timestamp)
     441             :     {
     442          18 :         char const * s(timestamp.c_str());
     443          43 :         while(isspace(*s))
     444             :         {
     445          25 :             ++s;
     446             :         }
     447          18 :         time_t sign(1);
     448          18 :         if(*s == '-')
     449             :         {
     450           4 :             ++s;
     451           4 :             sign = -1;
     452             :         }
     453          14 :         else if(*s == '+')
     454             :         {
     455           2 :             ++s;
     456             :         }
     457          18 :         time_t sec(0);
     458          18 :         long nsec(0);
     459          18 :         if(*s < '0' || *s > '9')
     460             :         {
     461           1 :             throw syntax_error("number of seconds must include at least one digit, even if '0'.");
     462             :         }
     463         118 :         for(; *s >= '0' && *s <= '9'; ++s)
     464             :         {
     465         102 :             sec = sec * 10 + (*s - '0');
     466         102 :             if(sec < 0)
     467             :             {
     468           1 :                 throw overflow("number of seconds is too large.");
     469             :             }
     470             :         }
     471          16 :         if(*s == '.')
     472             :         {
     473          14 :             ++s;
     474             : 
     475             :             // we count and only read up to 9 digits which means we cannot
     476             :             // have an overflow or an invalid number (>= 1,000,000,000)
     477             :             //
     478          14 :             int count(0);
     479          92 :             for(; *s >= '0' && *s <= '9' && count < 9; ++s, ++count)
     480             :             {
     481          78 :                 nsec = nsec * 10 + (*s - '0');
     482             :             }
     483             : 
     484             :             // skip additional digits
     485             :             //
     486          17 :             for(; *s >= '0' && *s <= '9'; ++s)
     487             :             {
     488           3 :                 ++s;
     489             :             }
     490             : 
     491             :             // in case all 9 digits were not included
     492             :             //
     493          62 :             for(; count < 9; ++count)
     494             :             {
     495          48 :                 nsec *= 10;
     496             :             }
     497             :         }
     498             : 
     499             :         // give user the ability to enter 's' for "second" as a unit
     500             :         //
     501          16 :         if(*s == 's')
     502             :         {
     503           5 :             ++s;
     504             :         }
     505          33 :         while(isspace(*s))
     506             :         {
     507          17 :             ++s;
     508             :         }
     509          16 :         if(*s != '\0')
     510             :         {
     511           1 :             throw syntax_error(std::string("number include unexpected characters (") + s + ").");
     512             :         }
     513             : 
     514          15 :         tv_sec = sec * sign;
     515          15 :         tv_nsec = nsec;
     516          15 :     }
     517             : 
     518             :     /** \brief Get system time.
     519             :      *
     520             :      * This function reads the system time and saves it into this
     521             :      * timespec_ex object.
     522             :      *
     523             :      * \todo
     524             :      * Look into whether we want to return an error if clock_gettime()
     525             :      * fails.
     526             :      *
     527             :      * \param[in] clk_id  The type of clock you want to query.
     528             :      *
     529             :      * \return A timespec_ex representing the specified \p clk_id.
     530             :      */
     531           1 :     static timespec_ex gettime(clockid_t clk_id = CLOCK_REALTIME)
     532             :     {
     533           1 :         timespec_ex result;
     534           1 :         clock_gettime(clk_id, &result);
     535           1 :         return result;
     536             :     }
     537             : 
     538             : 
     539             :     /** \brief Extract the timespec_ex as an int64_t value.
     540             :      *
     541             :      * This function transforms a timespec_ex structure in an int64_t
     542             :      * in nanoseconds.
     543             :      *
     544             :      * \return This timespec_ex converted to nanoseconds.
     545             :      */
     546           1 :     std::int64_t to_nsec() const
     547             :     {
     548           1 :         return tv_nsec
     549           1 :              + tv_sec * 1'000'000'000LL;
     550             :     }
     551             : 
     552             : 
     553             :     /** \brief Extract the timespec_ex as an int64_t value.
     554             :      *
     555             :      * This function transforms a timespec_ex structure in an int64_t
     556             :      * in microseconds. The bottom 3 digits are lost. No rounding
     557             :      * happens.
     558             :      *
     559             :      * \note
     560             :      * To can round the value up by first adding 500 (round) or 999 (ceil)
     561             :      * to the timespec_ex value.
     562             :      *
     563             :      * \return This timespec_ex converted to microseconds.
     564             :      */
     565           1 :     std::int64_t to_usec() const
     566             :     {
     567           1 :         return tv_nsec / 1'000LL
     568           1 :              + tv_sec * 1'000'000LL;
     569             :     }
     570             : 
     571             : 
     572             :     /** \brief Extract the timespec_ex as a double value.
     573             :      *
     574             :      * This function transforms a timespec_ex structure into a double
     575             :      * in seconds. The nanoseconds are added as one billionth of a
     576             :      * second.
     577             :      *
     578             :      * \return The timespec_ex converted the seconds.
     579             :      */
     580           6 :     double to_sec() const
     581             :     {
     582           6 :         return static_cast<double>(tv_sec)
     583           6 :              + static_cast<double>(tv_nsec) / 1.0e9;
     584             :     }
     585             : 
     586             : 
     587             :     /** \brief Convert the timespec_ex to a simple Unix timestamp.
     588             :      *
     589             :      * This function converts this timespec_ex structure to a string.
     590             :      * The seconds and nanoseconds are not adjust in any way making
     591             :      * it possible to use this function to serialize the timespec_ex
     592             :      * value. Use the set(std::string const & timestamp) function
     593             :      * to convert the string back to a timespec_ex value.
     594             :      *
     595             :      * If the \p remove_ending_zeroes parameter is set to true, the
     596             :      * nanoseconds ending zeroes are removed from the resulting string.
     597             :      * If there were no nanoseconds (0), then the period also gets
     598             :      * removed.
     599             :      *
     600             :      * \return A string representing this timespec_ex exactly.
     601             :      *
     602             :      * \sa set(std::string const & timestamp);
     603             :      */
     604          10 :     std::string to_timestamp(bool remove_ending_zeroes = false) const
     605             :     {
     606          10 :         std::stringstream s;
     607          10 :         s << tv_sec << "." << std::setw(9) << std::setfill('0') << tv_nsec;
     608          10 :         std::string result(s.str());
     609          10 :         if(remove_ending_zeroes)
     610             :         {
     611          27 :             while(result.back() == '0')
     612             :             {
     613          22 :                 result.pop_back();
     614             :             }
     615           5 :             if(result.back() == '.')
     616             :             {
     617           1 :                 result.pop_back();
     618             :             }
     619             :         }
     620          20 :         return result;
     621          10 :     }
     622             : 
     623             : 
     624             :     /** \brief Format the date to the specified format.
     625             :      *
     626             :      * \warning
     627             :      * This function uses strftime() which interprets the input time as
     628             :      * localtime, no matter what. You may want to consider using the
     629             :      * ostream<<() function if you want to save the time as a UTC string
     630             :      * as seconds & nanoseconds.
     631             :      *
     632             :      * This function transforms the time in a string and returns that string.
     633             :      * The function uses the strftime(). See that manual page to define
     634             :      * the format properly.
     635             :      *
     636             :      * This function supports a format extension: `%N`, to output 9 digits
     637             :      * with the nanoseconds available in the `timespec_ex` object. Without
     638             :      * the `%N`, the precision is to the second.
     639             :      *
     640             :      * It is possible to retrieve the timezone using the `"%z"` or `"%Z"`
     641             :      * format. If you want the full name, since we are beyond C++20,
     642             :      * you can use `std::chrono` like so:
     643             :      *
     644             :      * \code
     645             :      *     std::chrono::current_zone()->name()
     646             :      * \endcode
     647             :      *
     648             :      * \warning
     649             :      * Internally, the function uses a buffer of 256 bytes maximum.
     650             :      * Make sure your format is limited to the date and time. Things
     651             :      * you want to prepend or append should be managed outside of this
     652             :      * call.
     653             :      *
     654             :      * \warning
     655             :      * The `%N` should be preceeded by a period if included just after the
     656             :      * seconds (`%s` or `%S`). Some format arguments do not end with seconds,
     657             :      * such as the `%c`, `%r`, `%X`, `%EX`. If you want to use those, then
     658             :      * the `%N` should be separated by a space and probably followed by `ns`.
     659             :      * Note, however, that leading `0` are automatically added so `%N` is
     660             :      * always 9 characters at the moment.
     661             :      *
     662             :      * \exception overflow
     663             :      * In case the conversion of the `tv_sec` fails, this exception is raised.
     664             :      *
     665             :      * \param[in] format  The format used to transform the date and time in
     666             :      * a string.
     667             :      *
     668             :      * \return The formatted date and time.
     669             :      *
     670             :      * \sa from_string()
     671             :      */
     672             :     template<nl_langinfo_func_t nl_langinfo_wrapper = nl_langinfo>
     673          13 :     std::string to_string(std::string const & format = std::string()) const
     674             :     {
     675          13 :         struct tm date_and_time = {};
     676          13 :         struct tm * ptr(nullptr);
     677          13 :         ptr = localtime_r(&tv_sec, &date_and_time);
     678          13 :         if(ptr == nullptr)
     679             :         {
     680             :             throw overflow("the specified number of seconds could not be transformed in a 'struct tm'."); // LCOV_EXCL_LINE
     681             :         }
     682          13 :         format_item<char>::list_t format_items;
     683          13 :         std::string f(format);
     684          13 :         if(f.empty())
     685             :         {
     686             :             // compute the default using the lcoale
     687             :             //
     688             :             // if there is a %r, we convert it to the T_FMT_AMPM
     689             :             // if there is a %X, we convert it to the T_FMT
     690             :             // if there is a %EX, we convert it to the ERA_T_FMT
     691             :             // and then search for "%T" or "%S" or "%s" and insert ".%N"
     692             :             // right after
     693             :             //
     694             :             // Note: our algorithm doesn't work very well if the format
     695             :             //       includes multiple %r, %X, %EX, %T, %S
     696             :             //
     697           4 :             f = nl_langinfo_wrapper(D_T_FMT);
     698           4 :             if(f.empty())
     699             :             {
     700             :                 // use POSIX default if %c is not defined in the locale
     701             :                 //
     702           1 :                 f = "%a %b %e %H:%M:%S %Y";
     703             :             }
     704           4 :             format_items = tokenize_format<
     705             :                                   char
     706             :                                 , snapdev::strftime_letter_traits<char>
     707             :                                 , snapdev::strftime_flag_traits<char>>(f);
     708             : 
     709             :             // replace 'r', 'X', 'EX' with their content because those
     710             :             // will include the actual 'T', 'S', or 's'
     711             :             //
     712             :             // count the number of times we loop, if more than 10, forget
     713             :             // it; that means the locale is broken (creates an infinite loop)
     714             :             //
     715           4 :             int loop(0);
     716          36 :             for(auto it(format_items.begin()); it != format_items.end() && loop < 10; )
     717             :             {
     718          32 :                 int t(0);
     719          32 :                 switch(it->format())
     720             :                 {
     721           1 :                 case 'r':
     722             :                     // "r"
     723           1 :                     t = T_FMT_AMPM;
     724           1 :                     break;
     725             : 
     726           2 :                 case 'X':
     727           2 :                     if(it->has_flags(snapdev::strftime_flag_traits<char>::FORMAT_FLAG_EXTENDED))
     728             :                     {
     729             :                         // "EX"
     730           1 :                         t = ERA_T_FMT;
     731             :                     }
     732             :                     else
     733             :                     {
     734             :                         // "X"
     735           1 :                         t = T_FMT;
     736             :                     }
     737           2 :                     break;
     738             : 
     739          29 :                 default:
     740          29 :                     ++it;
     741          29 :                     continue;
     742             : 
     743             :                 }
     744           6 :                 std::string const sub_format(nl_langinfo_wrapper(t));
     745           3 :                 if(!sub_format.empty())
     746             :                 {
     747           3 :                     auto const & sub_format_items(tokenize_format<
     748             :                                       char
     749             :                                     , snapdev::strftime_letter_traits<char>
     750             :                                     , snapdev::strftime_flag_traits<char>>(sub_format));
     751           3 :                     format_items.insert(
     752             :                                       it
     753             :                                     , sub_format_items.begin()
     754             :                                     , sub_format_items.end());
     755           3 :                 }
     756           3 :                 it = format_items.erase(it);
     757           3 :                 ++loop;
     758             :             }
     759             : 
     760          50 :             for(auto it(format_items.begin()); it != format_items.end(); ++it)
     761             :             {
     762          46 :                 switch(it->format())
     763             :                 {
     764           6 :                 case 'T':
     765             :                 case 'S':
     766             :                 case 's':
     767             :                     {
     768           6 :                         ++it;
     769             : 
     770           6 :                         snapdev::format_item<char> period;
     771           6 :                         period.string(".");
     772           6 :                         format_items.insert(it, period);
     773             : 
     774           6 :                         snapdev::format_item<char> nanoseconds;
     775           6 :                         nanoseconds.string("%N");
     776           6 :                         nanoseconds.format('N');
     777           6 :                         it = format_items.insert(it, nanoseconds);
     778           6 :                     }
     779             :                     break;
     780             : 
     781             :                 }
     782             :             }
     783             :         }
     784             :         else
     785             :         {
     786             :             // user format, do not temper with it, if no .%N, that's
     787             :             // the user's choice
     788             :             //
     789           9 :             format_items = tokenize_format<
     790             :                               char
     791             :                             , snapdev::strftime_letter_traits<char, true>
     792             :                             , snapdev::strftime_flag_traits<char>
     793             :                             , snapdev::strftime_number_traits<char>>(f);
     794             :         }
     795             : 
     796             :         // Add support for microseconds and milliseconds
     797             :         //
     798         111 :         for(auto it(format_items.begin()); it != format_items.end();)
     799             :         {
     800          99 :             if(it->format() == 'N')
     801             :             {
     802          13 :                 std::string n(std::to_string(tv_nsec));
     803          13 :                 if(n.length() > 9)
     804             :                 {
     805           1 :                     throw overflow("tv_nsec is 1 billion or more, which is invalid.");
     806             :                 }
     807          12 :                 if(!it->has_flags(snapdev::strftime_flag_traits<char>::FORMAT_FLAG_NO_PAD))
     808             :                 {
     809             :                     // prepend padding zeroes or spaces
     810             :                     //
     811          12 :                     char const pad(it->has_flags(snapdev::strftime_flag_traits<char>::FORMAT_FLAG_PAD_WITH_SPACES)
     812          12 :                                 ? ' '
     813             :                                 : '0');
     814          24 :                     std::string const indent(9 - n.length(), pad);
     815          12 :                     n = indent + n;
     816          12 :                 }
     817          12 :                 if(it->has_flags(snapdev::strftime_flag_traits<char>::FORMAT_FLAG_EXTENDED))
     818             :                 {
     819             :                     // remove ending zeroes
     820             :                     //
     821           2 :                     std::string::size_type const last_non_zero(n.find_last_not_of('0'));
     822           2 :                     if(last_non_zero == std::string::npos)
     823             :                     {
     824           1 :                         n = "0";
     825             :                     }
     826             :                     else
     827             :                     {
     828           1 :                         n = n.substr(0, last_non_zero + 1);
     829             :                     }
     830             :                 }
     831             : 
     832             :                 // replace the %N with the final nanoseconds string
     833             :                 //
     834          12 :                 snapdev::format_item<char> nanoseconds;
     835          12 :                 nanoseconds.string(n);
     836          12 :                 format_items.insert(it, nanoseconds);
     837             : 
     838          12 :                 it = format_items.erase(it);
     839          13 :             }
     840             :             else
     841             :             {
     842          86 :                 ++it;
     843             :             }
     844             :         }
     845             : 
     846             :         // convert the format items back in a format string
     847             :         //
     848          12 :         snapdev::format_item<char> empty_item;
     849          12 :         f = snapdev::join_strings(format_items, empty_item);
     850             : 
     851          12 :         char buf[256];
     852          12 :         std::size_t const sz(strftime(buf, sizeof(buf), f.c_str(), &date_and_time));
     853          12 :         if(sz == 0)
     854             :         {
     855             :             // this happens with just a "%p" and "wrong locale"
     856             :             // or when the buffer is too small, which should not
     857             :             // be the case unless you add much more than the format
     858             :             // in that string
     859             :             //
     860           1 :             throw overflow(
     861             :                   "the specified strftime() format \""
     862             :                 + format
     863             :                 + "\" failed.");
     864             :         }
     865             : 
     866          22 :         return std::string(buf, sz);
     867          16 :     }
     868             : 
     869             : 
     870             :     /** \brief Convert a string with a date to a timespec_ex.
     871             :      *
     872             :      * This function is the converse of the to_string() function. It
     873             :      * converts a string (\p s) to a timespec_ex time and date using
     874             :      * the specified \p format.
     875             :      *
     876             :      * \note
     877             :      * The format is used with the strptime() function. Please refer
     878             :      * to that function for additional information. To some extend, the
     879             :      * function also supports our %N extension.
     880             :      *
     881             :      * \param[in] s  The string to be converted to this timespec_ex.
     882             :      * \param[in] format  The format to use for the input data.
     883             :      *
     884             :      * \sa to_string()
     885             :      */
     886           2 :     void from_string(std::string const & s, std::string const & format)
     887             :     {
     888             :         // I really have no clue how to properly support the %N option
     889             :         // without rewriting strptime() which I readlly don't want to do
     890             :         //
     891             :         // one way is to look for the '.' (assuming the %N is preceeded
     892             :         // by such) but some people write dates with those as in:
     893             :         //
     894             :         //    29.05.2023
     895             :         //
     896           2 :         if(format.find("%N") != std::string::npos)
     897             :         {
     898           1 :             throw libexcept::fixme("the from_string() %N extension is not yet implemented.");
     899             :         }
     900             : 
     901           1 :         struct tm t;
     902           1 :         strptime(s.c_str(), format.c_str(), &t);
     903           1 :         tv_sec = mktime(&t);
     904           1 :         tv_nsec = 0;
     905           1 :     }
     906             : 
     907             : 
     908             :     /** \brief Validate this timespec_ex structure.
     909             :      *
     910             :      * This function returns true if this timespec_ex structure is considered
     911             :      * valid.
     912             :      *
     913             :      * At this time, the validation consists of verifying that the
     914             :      * nanoseconds is a number between 0 and 1 billion (maximum excluded).
     915             :      *
     916             :      * \note
     917             :      * Negative timespec_ex are represented by a negative tv_sec. The
     918             :      * tv_nsec can never be negative after a valid operation.
     919             :      *
     920             :      * \return true if the timespec_ex is considered valid.
     921             :      */
     922           7 :     bool valid() const
     923             :     {
     924           7 :         return tv_nsec < 1'000'000'000LL;
     925             :     }
     926             : 
     927             : 
     928             :     /** \brief Check whether this timespec_ex is negative.
     929             :      *
     930             :      * This function checks whether the number represents a negative
     931             :      * timespec_ex. This is true if the number of seconds is negative.
     932             :      *
     933             :      * \note
     934             :      * The first negative timespec_ex is { -1, 999,999,999 }.
     935             :      *
     936             :      * \return true if the timespec_ex is considered negative.
     937             :      */
     938          74 :     bool negative() const
     939             :     {
     940          74 :         return tv_sec < 0LL;
     941             :     }
     942             : 
     943             : 
     944             :     /** \brief Add two timespec_ex together.
     945             :      *
     946             :      * This function adds \p rhs to this timespec_ex value and returns a
     947             :      * new timespec_ex with the result. This timespec_ex is not modified.
     948             :      *
     949             :      * \param[in] rhs  The right handside to add to this number.
     950             :      *
     951             :      * \return A new timespec_ex representing the sum of 'this' and rhs.
     952             :      */
     953          34 :     timespec_ex add(timespec_ex const & rhs) const
     954             :     {
     955          34 :         bool const lneg(negative());
     956          34 :         bool const rneg(rhs.negative());
     957             : 
     958          34 :         timespec_ex lp(lneg ? -*this : *this);
     959          34 :         timespec_ex rp(rneg ? -rhs : rhs);
     960             : 
     961          34 :         timespec_ex result;
     962             : 
     963          34 :         switch((lneg ? 1 : 0) + (rneg ? 2 : 0))
     964             :         {
     965          16 :         case 0:     // positive + positive
     966             :         case 3:     // negative + negative
     967          16 :             result.tv_sec = lp.tv_sec + rp.tv_sec;
     968          16 :             result.tv_nsec = lp.tv_nsec + rp.tv_nsec;
     969          16 :             break;
     970             : 
     971           3 :         case 1:     // negative + positive
     972           3 :             result.tv_sec = rp.tv_sec - lp.tv_sec;
     973           3 :             result.tv_nsec = rp.tv_nsec - lp.tv_nsec;
     974           3 :             break;
     975             : 
     976          15 :         case 2:     // positive + negative
     977          15 :             result.tv_sec = lp.tv_sec - rp.tv_sec;
     978          15 :             result.tv_nsec = lp.tv_nsec - rp.tv_nsec;
     979          15 :             break;
     980             : 
     981             :         }
     982             : 
     983          34 :         if(result.tv_nsec < 0)
     984             :         {
     985           5 :             --result.tv_sec;
     986           5 :             result.tv_nsec += 1'000'000'000L;
     987             :         }
     988          29 :         else if(result.tv_nsec >= 1'000'000'000)
     989             :         {
     990           5 :             ++result.tv_sec;
     991           5 :             result.tv_nsec -= 1'000'000'000;
     992             :         }
     993             : 
     994          34 :         if(lneg && rneg)
     995             :         {
     996           2 :             result = -result;
     997             :         }
     998             : 
     999          34 :         return result;
    1000             :     }
    1001             : 
    1002             : 
    1003             :     /** \brief Compare two timespec_ex together.
    1004             :      *
    1005             :      * This function compares two timespecs and determine whether they
    1006             :      * are equal (0), 'this' is smaller (negative) or \p rhs is smaller
    1007             :      * (positive).
    1008             :      *
    1009             :      * \param[in] rhs  The right handside to compare.
    1010             :      *
    1011             :      * \return negative, 0, or positive depending on the order between
    1012             :      *         \p lhs and \p rhs.
    1013             :      */
    1014         113 :     int compare(timespec_ex const & rhs) const
    1015             :     {
    1016             :         // see operator <=> ... catch2 seems to not accept these just yet
    1017             :         //return tv_sec == rhs.tv_sec
    1018             :         //            ? tv_nsec <=> rhs.tv_nsec
    1019             :         //            : tv_sec <=> rhs.tv_sec;
    1020             : 
    1021         113 :         if(tv_sec == rhs.tv_sec)
    1022             :         {
    1023         100 :             return tv_nsec == rhs.tv_nsec
    1024         127 :                     ? 0
    1025         127 :                     : (tv_nsec < rhs.tv_nsec ? -1 : 1);
    1026             :         }
    1027             :         
    1028          13 :         return tv_sec < rhs.tv_sec ? -1 : 1;
    1029             :     }
    1030             : 
    1031             : 
    1032             :     /** \brief Check whether the timespec_ex is zero.
    1033             :      *
    1034             :      * This function returns true if the timespec_ex represents zero
    1035             :      * (i.e. zero seconds and zero nano-seconds).
    1036             :      *
    1037             :      * \return true if the timespec_ex is zero, false if not zero.
    1038             :      */
    1039          11 :     bool operator ! () const
    1040             :     {
    1041          11 :         return tv_sec == 0 && tv_nsec == 0;
    1042             :     }
    1043             : 
    1044             : 
    1045             :     /** \brief Add the right handside to this timespec_ex.
    1046             :      *
    1047             :      * This operator adds the right handside to this object.
    1048             :      *
    1049             :      * \param[in] rhs  Another timespec_ex to add to this one.
    1050             :      *
    1051             :      * \return A reference to this timespec_ex object.
    1052             :      */
    1053          19 :     timespec_ex & operator += (timespec_ex const & rhs)
    1054             :     {
    1055          19 :         *this = add(rhs);
    1056          19 :         return *this;
    1057             :     }
    1058             : 
    1059             : 
    1060             :     /** \brief Add 1 nanosecond to this timespec_ex object.
    1061             :      *
    1062             :      * This function adds exactly one nanonsecond to this timespec_ex
    1063             :      * object.
    1064             :      *
    1065             :      * \return A reference to this timespec_ex object.
    1066             :      */
    1067           3 :     timespec_ex & operator ++ ()
    1068             :     {
    1069           3 :         *this += 1L;
    1070           3 :         return *this;
    1071             :     }
    1072             : 
    1073             : 
    1074             :     /** \brief Add 1 nanosecond to this timespec_ex object.
    1075             :      *
    1076             :      * This function adds exactly one nanonsecond to this timespec_ex
    1077             :      * object and returns the original value.
    1078             :      *
    1079             :      * \return A copy of this timespec_ex object before the add() occurs.
    1080             :      */
    1081           1 :     timespec_ex operator ++ (int)
    1082             :     {
    1083           1 :         timespec_ex result(*this);
    1084           1 :         *this += 1L;
    1085           1 :         return result;
    1086             :     }
    1087             : 
    1088             : 
    1089             :     /** \brief Add two timespec_ex objects and return the result.
    1090             :      *
    1091             :      * This function computes the addition of this timespec_ex object
    1092             :      * and the \p t timespec_ex and returns the result. The inputs
    1093             :      * are not modified.
    1094             :      *
    1095             :      * \param[in] t  The right handside to add to this timespex_ex object.
    1096             :      *
    1097             :      * \return The sum of the inputs in a new timespec_ex object.
    1098             :      */
    1099           7 :     timespec_ex operator + (timespec_ex const & t) const
    1100             :     {
    1101           7 :         timespec_ex result(*this);
    1102           7 :         result += t;
    1103           7 :         return result;
    1104             :     }
    1105             : 
    1106             : 
    1107             :     /** \brief Subtract the right handside from this timespec_ex.
    1108             :      *
    1109             :      * This operator subtracts the right handside from this object.
    1110             :      *
    1111             :      * \param[in] rhs  Another timespec_ex to subtract from this one.
    1112             :      *
    1113             :      * \return A reference to this timespec_ex object.
    1114             :      */
    1115          15 :     timespec_ex & operator -= (timespec_ex const & rhs)
    1116             :     {
    1117          15 :         *this = add(-rhs);
    1118          15 :         return *this;
    1119             :     }
    1120             : 
    1121             : 
    1122             :     /** \brief Subtract 1 nanosecond from timespec_ex object.
    1123             :      *
    1124             :      * This function subtracts exactly one nanonsecond from this
    1125             :      * timespec_ex object.
    1126             :      *
    1127             :      * \return A reference to this timespec_ex object.
    1128             :      */
    1129           3 :     timespec_ex & operator -- ()
    1130             :     {
    1131           3 :         *this -= 1L;
    1132           3 :         return *this;
    1133             :     }
    1134             : 
    1135             : 
    1136             :     /** \brief Subtract 1 nanosecond from timespec_ex object.
    1137             :      *
    1138             :      * This function subtracts exactly one nanonsecond from this
    1139             :      * timespec_ex object and returns the original value.
    1140             :      *
    1141             :      * \return A copy of this timespec_ex object before the subtract occurs.
    1142             :      */
    1143           1 :     timespec_ex operator -- (int)
    1144             :     {
    1145           1 :         timespec_ex result(*this);
    1146           1 :         *this -= 1L;
    1147           1 :         return result;
    1148             :     }
    1149             : 
    1150             : 
    1151             :     /** \brief Compute the additive opposite of the right handside timespec_ex.
    1152             :      *
    1153             :      * This function computers the opposite of the right handside timespec_ex
    1154             :      * and returns a copy with the result.
    1155             :      *
    1156             :      * This is equivalent to computing `0 - t`.
    1157             :      *
    1158             :      * \param[in] t  The right handside time to negate.
    1159             :      *
    1160             :      * \return A timespec_ex representing the additive opposite of the input.
    1161             :      */
    1162          51 :     timespec_ex operator - () const
    1163             :     {
    1164          51 :         timespec_ex result(timespec{ -tv_sec, -tv_nsec });
    1165          51 :         if(result.tv_nsec < 0)
    1166             :         {
    1167          50 :             --result.tv_sec;
    1168          50 :             result.tv_nsec += 1'000'000'000L;
    1169             :         }
    1170          51 :         return result;
    1171             :     }
    1172             : 
    1173             : 
    1174             :     /** \brief Subtract \p t from this timespec_ex object.
    1175             :      *
    1176             :      * This function computes the difference of this timespec_ex object
    1177             :      * and the \p t timespec_ex object and returns the result. The inputs
    1178             :      * are not modified.
    1179             :      *
    1180             :      * \param[in] rhs  The right handside to subtract from this timespex_ex
    1181             :      * object.
    1182             :      *
    1183             :      * \return The different of the inputs in a new timespec_ex object.
    1184             :      */
    1185           7 :     timespec_ex operator - (timespec_ex const & rhs) const
    1186             :     {
    1187           7 :         timespec_ex result(*this);
    1188           7 :         result -= rhs;
    1189           7 :         return result;
    1190             :     }
    1191             : 
    1192             : #if 0
    1193             :     // it looks like catch is not quite ready for this one
    1194             :     auto operator <=> (timespec_ex const & t) const
    1195             :     {
    1196             :         return compare(t);
    1197             :     }
    1198             : #endif
    1199             : 
    1200             :     /** \brief Compare whether the two timespec_ex are equal.
    1201             :      *
    1202             :      * \param[in] t  The time to compare against.
    1203             :      *
    1204             :      * \return true if both timespec_ex objects are equal.
    1205             :      */
    1206          38 :     bool operator == (timespec_ex const & t) const
    1207             :     {
    1208          38 :         return compare(t) == 0;
    1209             :     }
    1210             : 
    1211             : 
    1212             :     /** \brief Compare whether the two timespec_ex are not equal.
    1213             :      *
    1214             :      * \param[in] t  The time to compare against.
    1215             :      *
    1216             :      * \return true if both timespec_ex objects are not equal.
    1217             :      */
    1218          15 :     bool operator != (timespec_ex const & t) const
    1219             :     {
    1220          15 :         return compare(t) != 0;
    1221             :     }
    1222             : 
    1223             : 
    1224             :     /** \brief Compare whether the left handside is smaller.
    1225             :      *
    1226             :      * \param[in] t  The time to compare against.
    1227             :      *
    1228             :      * \return true if the left handside timespec_ex object is smaller.
    1229             :      */
    1230          16 :     bool operator < (timespec_ex const & t) const
    1231             :     {
    1232          16 :         return compare(t) == -1;
    1233             :     }
    1234             : 
    1235             : 
    1236             :     /** \brief Compare whether the left handside is smaller or equal.
    1237             :      *
    1238             :      * \param[in] t  The time to compare against.
    1239             :      *
    1240             :      * \return true if the left handside timespec_ex object is smaller
    1241             :      * or equal.
    1242             :      */
    1243          15 :     bool operator <= (timespec_ex const & t) const
    1244             :     {
    1245          15 :         return compare(t) <= 0;
    1246             :     }
    1247             : 
    1248             : 
    1249             :     /** \brief Compare whether the left handside is larger.
    1250             :      *
    1251             :      * \param[in] t  The time to compare against.
    1252             :      *
    1253             :      * \return true if the left handside timespec_ex object is larger.
    1254             :      */
    1255          14 :     bool operator > (timespec_ex const & t) const
    1256             :     {
    1257          14 :         return compare(t) == 1;
    1258             :     }
    1259             : 
    1260             : 
    1261             :     /** \brief Compare whether the left handside is larger or equal.
    1262             :      *
    1263             :      * \param[in] t  The time to compare against.
    1264             :      *
    1265             :      * \return true if the left handside timespec_ex object is larger
    1266             :      * or equal.
    1267             :      */
    1268          15 :     bool operator >= (timespec_ex const & t) const
    1269             :     {
    1270          15 :         return compare(t) >= 0;
    1271             :     }
    1272             : };
    1273             : 
    1274             : 
    1275             : 
    1276             : /** \brief Create a timespec_ex object with "now".
    1277             :  *
    1278             :  * This function creates a timespec_ex object with the time set to "now".
    1279             :  *
    1280             :  * This is a wrapper of the clock_gettime(2) function.
    1281             :  *
    1282             :  * \exception clock_error
    1283             :  * This function raises the clock_error if the clock could not be read.
    1284             :  *
    1285             :  * \param[in] clk_id  The type of clock to read. By default this is
    1286             :  * CLOCK_REALTIME.
    1287             :  *
    1288             :  * \return A timespec_ex object with "now" as the time.
    1289             :  */
    1290           1 : inline timespec_ex now(clockid_t clk_id = CLOCK_REALTIME)
    1291             : {
    1292           1 :     timespec_ex n;
    1293           1 :     int const r(clock_gettime(clk_id, &n));
    1294           1 :     if(r != 0)
    1295             :     {
    1296             :         // LCOV_EXCL_START
    1297             :         int const e(errno);
    1298             :         throw clock_error(
    1299             :               "clock_gettime() failed: "
    1300             :             + std::to_string(e)
    1301             :             + ", "
    1302             :             + strerror(e));
    1303             :         // LCOV_EXCL_STOP
    1304             :     }
    1305           1 :     return n;
    1306             : }
    1307             : 
    1308             : 
    1309             : 
    1310             : 
    1311             : 
    1312             : 
    1313             : /** \brief Output a timespec to a basic_ostream.
    1314             :  *
    1315             :  * This function allows one to print out a timespec. By default the function
    1316             :  * prints the timespec as a floating point.
    1317             :  *
    1318             :  * To wrinte a date and time string instead, use the timespec_ex::to_string()
    1319             :  * function as in:
    1320             :  *
    1321             :  * \code
    1322             :  *     out << t.to_string("%Y/%m/%d %H:%M:%S.%N");
    1323             :  * \endcode
    1324             :  *
    1325             :  * Just keep in mind that to_string() generates a local timestamp. The
    1326             :  * timespec_ex::from_string() reverses the value back to a timespec_ex.
    1327             :  *
    1328             :  * \todo
    1329             :  * Add a flag to determine whether the nanoseconds ending zeroes should
    1330             :  * be removed or not (warning: the timespec_ex::to_timestamp() cannot
    1331             :  * be used as is because this function receives a timespec not our
    1332             :  * timespec_ex structure).
    1333             :  *
    1334             :  * \param[in] out  The output stream where the timespec gets written.
    1335             :  * \param[in] t  The actual timespec that is to be printed.
    1336             :  *
    1337             :  * \return A reference to the basic_ostream object.
    1338             :  */
    1339             : template<typename CharT, typename Traits>
    1340           5 : std::basic_ostream<CharT, Traits> & operator << (std::basic_ostream<CharT, Traits> & out, timespec const & t)
    1341             : {
    1342             :     // write to a string buffer first
    1343             :     //
    1344           5 :     std::basic_ostringstream<CharT, Traits, std::allocator<CharT> > s;
    1345             : 
    1346             :     // setup the string output like the out stream
    1347             :     //
    1348           5 :     s.flags(out.flags());
    1349           5 :     s.imbue(out.getloc());
    1350           5 :     s.precision(out.precision());
    1351           5 :     s << t.tv_sec << "." << std::setw(9) << std::setfill('0') << t.tv_nsec;
    1352             : 
    1353             :     // buffer is ready, display in output in one go
    1354             :     //
    1355          10 :     return out << s.str();
    1356           5 : }
    1357             : 
    1358             : 
    1359             : } // namespace snapdev
    1360             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14