LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-datetime.hpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 100.0 % 6 6
Test Date: 2026-11-02 18:19:43 Functions: 100.0 % 3 3
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * gnc-datetime.cpp -- Date and Time classes for GnuCash            *
       3              :  *                                                                  *
       4              :  * Copyright 2015 John Ralls <jralls@ceridwen.us>                   *
       5              :  *                                                                  *
       6              :  * This program is free software; you can redistribute it and/or    *
       7              :  * modify it under the terms of the GNU General Public License as   *
       8              :  * published by the Free Software Foundation; either version 2 of   *
       9              :  * the License, or (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, contact:                        *
      18              :  *                                                                  *
      19              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      20              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      21              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      22              :  *                                                                  *
      23              : \********************************************************************/
      24              : 
      25              : #ifndef __GNC_DATETIME_HPP__
      26              : #define  __GNC_DATETIME_HPP__
      27              : 
      28              : #include <cstdint>
      29              : #include <memory>
      30              : #include <string>
      31              : #include <vector>
      32              : #include <functional>
      33              : #include <optional>
      34              : 
      35              : #include <boost/date_time/gregorian/gregorian.hpp>
      36              : #include <boost/regex.hpp>
      37              : 
      38              : typedef struct
      39              : {
      40              :     int year;  //1400-9999
      41              :     int month; //1-12
      42              :     int day; //1-31
      43              : } gnc_ymd;
      44              : 
      45              : enum class DayPart {
      46              :     start,    // 00:00 local
      47              :     neutral,  // 10:59 UTC
      48              :     end,      // 23:59 local
      49              : };
      50              : 
      51              : class GncDateTimeImpl;
      52              : class GncDateImpl;
      53              : class GncDate;
      54              : using time64 = int64_t;
      55              : constexpr const time64 MINTIME = -17987443200;
      56              : constexpr const time64 MAXTIME = 253402214400;
      57              : 
      58              : /** GnuCash DateTime class
      59              :  *
      60              :  * Represents local time in the current timezone.
      61              :  * As with GncDate, the represented time is limited to the period
      62              :  * between 1400 and 9999 CE.
      63              :  *
      64              :  * Be careful when using times: A particular time is represented
      65              :  * differently depending on the timezone, which can shift the displayed
      66              :  * date. Accounting is generally not sensitive to the time of day, but
      67              :  * is sensitive to the recorded day. Since GncDates are not timezone
      68              :  * dependent they should be preferred for accounting entries.
      69              :  */
      70              : 
      71              : class GncDateTime
      72              : {
      73              : public:
      74              : /** Construct a GncDateTime representing the current time in the
      75              :  * current timezone.
      76              :  */
      77              :     GncDateTime();
      78              : /** Construct a GncDateTime in the current timezone representing the
      79              :  * timestamp as seconds from the POSIX epoch (1970-01-01T00:00:00UTC).
      80              :  * @param time Seconds from the POSIX epoch.
      81              :  * @exception std::invalid_argument if the year is outside the constraints.
      82              :  */
      83              :     GncDateTime(const time64 time);
      84              : /** Construct a GncDateTime in the current timezone representing the
      85              :  * standard struct tm provided.
      86              :  * @param tm A C-standard struct tm representing the date and
      87              :  * time. Note that the timezone and offset are ignored on those
      88              :  * systems which include them in struct tm.
      89              :  * @exception std::invalid_argument if the year is outside the constraints.
      90              :  */
      91              :     GncDateTime(const struct tm tm);
      92              : /** Construct a GncDateTime from a GncDate. As a GncDate doesn't contain time
      93              :  * information, the time will be set depending on the second parameter
      94              :  * to start of day, neutral or end of day.
      95              :  * @param date A GncDate representing a date.
      96              :  * @param part An optinoal DayPart indicating which time to use in the conversion.
      97              :  * This can be "DayPart::start" for start of day (00:00 local time),
      98              :  *             "DayPart::neutral" for a neutral time (10:59 UTC, chosen to have the
      99              :  *              least chance of date changes when crossing timezone borders),
     100              :  *             "DayPart::end" for end of day (23:59 local time).
     101              :  * If omitted part defaults to DayPart::neutral.
     102              :  * Note the different timezone used for DayPart::neutral compared to the other
     103              :  * two options!
     104              :  * @exception std::invalid_argument if the year is outside the constraints.
     105              :  */
     106              :     GncDateTime(const GncDate& date, DayPart part = DayPart::neutral);
     107              : /** Construct a GncDateTime
     108              :  * @param str A string representing the date and time in some
     109              :  * recognizable format. Note that if a timezone is not specified the
     110              :  * default is UTC, not the local one.
     111              :  * @exception std::invalid_argument if the year is outside the constraints.
     112              :  */
     113              :     GncDateTime(const std::string& str);
     114              :     GncDateTime(const char* str);
     115              :     ~GncDateTime();
     116              : /** Set the GncDateTime to the date and time indicated in the computer's clock.
     117              :  */
     118              :     void now();
     119              : /** Cast the GncDateTime to a time64, seconds from the POSIX epoch. */
     120              :     explicit operator time64() const;
     121              : /** Cast the GncDateTime to a struct tm. Timezone field isn't filled.
     122              :  */
     123              :     explicit operator struct tm() const;
     124              : /** Obtain the UTC offset in seconds
     125              :  *  @return seconds difference between this local time and UTC. West
     126              :  *  is negative.
     127              :  */
     128              :     long offset()const;
     129              : /** Obtain a struct tm representing the time in UTC.
     130              :  * @return struct tm
     131              :  */
     132              :     struct tm utc_tm() const;
     133              : /** Obtain the date from the time, as a GncDate, in the current timezone.
     134              :  *  @return GncDate represented by the GncDateTime.
     135              :  */
     136              :     GncDate date() const;
     137              : /** Test if the GncDateTime has a member pointer. Testing only. */
     138              :     bool isnull (void) { return m_impl == nullptr; }
     139              : /** Format the GncDateTime into a std::string
     140              :  *  @param format A cstr describing the way the date and time are
     141              :  *  presented. Code letters preceded with % stand in for arguments;
     142              :  *  most are the same as described in strftime(3), but there are a few
     143              :  *  differences. Consult the boost::date_time documentation.
     144              :  *  @return a std::string containing a representation of the date and time in
     145              :  *  the locale's time zone according to the format.
     146              :  */
     147              :     std::string format(const char* format) const;
     148              : /** Format the GncDateTime into a std::string in GMT
     149              :  *  @param format A cstr describing the way the date and time are
     150              :  *  presented. Code letters preceded with % stand in for arguments;
     151              :  *  most are the same as described in strftime(3), but there are a few
     152              :  *  differences. Consult the boost::date_time documentation.
     153              :  *  @return a std::string containing a representation of the date and time in
     154              :  *  GMT (timezone Z) according to the format.
     155              :  */
     156              :     std::string format_zulu(const char* format) const;
     157              : /** Format the GncDateTime into a gnucash-style iso8601 string in UTC.
     158              :  *  @return a std::string in the format YYYY-MM-DD HH:MM:SS.
     159              :  */
     160              :     std::string format_iso8601() const;
     161              : /** Get an undelimited string representing the current date and time.
     162              :  *  @return a std::string in the format YYYYMMDDHHMMSS.
     163              :  */
     164              :     static std::string timestamp();
     165              :     
     166              : private:
     167              :     std::unique_ptr<GncDateTimeImpl> m_impl;
     168              : };
     169              : 
     170              : /** GnuCash DateFormat class
     171              :  *
     172              :  * A helper class to represent a date format understood
     173              :  * by the GncDate string/format constructor. Consumers
     174              :  * of this header file are not supposed to create
     175              :  * objects of this class themselves. Instead they
     176              :  * can get a list of the understood formats from the
     177              :  * GncDate::c_formats class variable and work with those.
     178              :  */
     179              : 
     180              : using StringToDate = std::function<boost::gregorian::date(const std::string&)>;
     181              : 
     182              : class GncDateFormat
     183              : {
     184              : public:
     185              :     /** Construct a GncDateFormat with a given format and corresponding
     186              :      * regular expression. This should only be used internally by the
     187              :      * GncDate implementation. Consumers should never construct a GncDateFormat
     188              :      * themselves!
     189              :      */
     190          238 :     GncDateFormat (const char* fmt, const char* re) :
     191          714 :         m_fmt(fmt), m_re(re) {}
     192          357 :     GncDateFormat (const char* fmt, StringToDate str_to_date, const char* re) :
     193         1071 :         m_fmt(fmt), m_re(re), m_str_to_date(str_to_date) {}
     194          119 :     GncDateFormat (const char* fmt, StringToDate str_to_date) :
     195          357 :         m_fmt(fmt), m_str_to_date(str_to_date) {}
     196              :     /** A string representing the format. */
     197              :     const std::string m_fmt;
     198              : private:
     199              :     /** Regular expression associated with the format string. This is to and
     200              :      * only be used internally by the gnc-datetime code.
     201              :      */
     202              :     boost::regex m_re;
     203              :     std::optional<StringToDate> m_str_to_date;
     204              : 
     205              :     friend class GncDateImpl;
     206              : };
     207              : 
     208              : /** GnuCash Date class
     209              :  *
     210              :  * The represented date is limited to the period
     211              :  * between 1400 and 9999 CE.
     212              :  */
     213              : 
     214              : class GncDate
     215              : {
     216              :     public:
     217              :         /** A vector with all the date formats supported by the string constructor.
     218              :          * The currently supported formats are:
     219              :          * "y-m-d" (including yyyymmdd)
     220              :          * "d-m-y" (including ddmmyyyy)
     221              :          * "m-d-y" (including mmddyyyy)
     222              :          * "d-m"   (including ddmm)
     223              :          * "m-d"   (including mmdd)
     224              :          *
     225              :          * Notes:
     226              :          * - while the format names are using a "-" as separator, the
     227              :          * regexes will accept any of "-/.' " and will also work for dates
     228              :          * without separators.
     229              :          * - the format strings are marked for translation so it is possible
     230              :          * to use a localized version of a format string using gettext. Example:
     231              :          * gettext(GncDate::c_formats[0])
     232              :          */
     233              :         static const std::vector<GncDateFormat> c_formats;
     234              :         /** Construct a GncDate representing the current day.
     235              :          */
     236              :         GncDate();
     237              :         /** Construct a GncDate representing the given year, month, and day in
     238              :          * the proleptic Gregorian calendar.
     239              :          *
     240              :          * Years are constrained to be from 1400 - 9999 CE inclusive. Dates
     241              :          * will be normalized if the day or month values are outside of the
     242              :          * normal ranges. e.g. 1994, -3, 47 will be normalized to 1993-10-17.
     243              :          *
     244              :          * @param year The year in the Common Era.
     245              :          * @param month The month, where 1 is January and 12 is December.
     246              :          * @param day The day of the month, beginning with 1.
     247              :          * @exception std::invalid_argument if the calculated year is outside
     248              :          * of the constrained range.
     249              :          */
     250              :         GncDate(int year, int month, int day);
     251              :         /** Construct a GncDate by parsing a string assumed to be in the format
     252              :          * passed in.
     253              :          *
     254              :          * The currently recognized formats are d-m-y, m-d-y, y-m-d, m-d, d-m.
     255              :          * Note while the format descriptions use "-" as separator any of
     256              :          * "-" (hyphen), "/" (slash), "'" (single quote), " " (space) or
     257              :          * "." will be accepted.
     258              :          *
     259              :          * @param str The string to be interpreted.
     260              :          * @param fmt The expected date format of the string passed in.
     261              :          * @exception std::invalid_argument if
     262              :          * - the string couldn't be parsed using the provided format
     263              :          * - any of the date components is outside of its limit
     264              :          *   (like month being 13, or day being 31 in February)
     265              :          * - fmt doesn't specify a year, yet a year was found in the string
     266              :          */
     267              :         GncDate(const std::string str, const std::string fmt);
     268              :         /** Construct a GncDate from a GncDateImpl.
     269              :          */
     270              :         GncDate(std::unique_ptr<GncDateImpl> impl);
     271              :         /** Copy constructor.
     272              :          */
     273              :         GncDate(const GncDate&);
     274              :         /** Move constructor.
     275              :          */
     276              :         GncDate(GncDate&&);
     277              :         /** Default destructor.
     278              :          */
     279              :         ~GncDate();
     280              :         /** Copy assignment operator.
     281              :          */
     282              :         GncDate& operator=(const GncDate&);
     283              :         /** Move assignment operator.
     284              :          */
     285              :         GncDate& operator=(GncDate&&);
     286              :         /** Set the date object to the computer clock's current day. */
     287              :         void today();
     288              :         /** Get the year, month, and day from the date as a gnc_ymd.
     289              :          *  @return gnc_ymd struct
     290              :          */
     291              :         gnc_ymd year_month_day() const;
     292              :         /** Format the GncDate into a std::string
     293              :          *  @param format A cstr describing the way the date and time are
     294              :          *  presented. Code letters preceded with % stand in for arguments;
     295              :          *  most are the same as described in strftime(3), but there are a few
     296              :          *  differences. Consult the boost::date_time documentation.
     297              :          *  @return a std::string containing a representation of the date
     298              :          *  according to the format.
     299              :          */
     300              :         std::string format(const char* format);
     301              :         /** Test that the Date has an implementation. */
     302              :         bool isnull (void) { return m_impl == nullptr; }
     303              : 
     304              : private:
     305              :     std::unique_ptr<GncDateImpl> m_impl;
     306              : 
     307              :     friend GncDateTime::GncDateTime(const GncDate&, DayPart);
     308              :     friend bool operator<(const GncDate&, const GncDate&);
     309              :     friend bool operator>(const GncDate&, const GncDate&);
     310              :     friend bool operator==(const GncDate&, const GncDate&);
     311              :     friend bool operator<=(const GncDate&, const GncDate&);
     312              :     friend bool operator>=(const GncDate&, const GncDate&);
     313              :     friend bool operator!=(const GncDate&, const GncDate&);
     314              : };
     315              : 
     316              : /**@{
     317              :  *  Standard comparison operators working on GncDate objects.
     318              :  */
     319              : bool operator<(const GncDate& a, const GncDate& b);
     320              : bool operator>(const GncDate& a, const GncDate& b);
     321              : bool operator==(const GncDate& a, const GncDate& b);
     322              : bool operator<=(const GncDate& a, const GncDate& b);
     323              : bool operator>=(const GncDate& a, const GncDate& b);
     324              : bool operator!=(const GncDate& a, const GncDate& b);
     325              : /**@}*/
     326              : 
     327              : #endif // __GNC_DATETIME_HPP__
        

Generated by: LCOV version 2.0-1