LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-date.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 69.9 % 728 509
Test Date: 2025-06-02 17:54:15 Functions: 80.7 % 88 71
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /********************************************************************\
       2                 :             :  * gnc-date.cpp -- C interface for date and time                    *
       3                 :             :  *                                                                  *
       4                 :             :  * Copyright 1997 Robin D. Clark <rclark@cs.hmc.edu>                *
       5                 :             :  * Copyright 1998-2000, 2003 Linas Vepstas <linas@linas.org>        *
       6                 :             :  * Copyright (C) 2005 David Hampton <hampton@employees.org>         *
       7                 :             :  * Copyright 2011-2015 John Ralls <jralls@ceridwen.us               *
       8                 :             :  *                                                                  *
       9                 :             :  * This program is free software; you can redistribute it and/or    *
      10                 :             :  * modify it under the terms of the GNU General Public License as   *
      11                 :             :  * published by the Free Software Foundation; either version 2 of   *
      12                 :             :  * the License, or (at your option) any later version.              *
      13                 :             :  *                                                                  *
      14                 :             :  * This program is distributed in the hope that it will be useful,  *
      15                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      16                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      17                 :             :  * GNU General Public License for more details.                     *
      18                 :             :  *                                                                  *
      19                 :             :  * You should have received a copy of the GNU General Public License*
      20                 :             :  * along with this program; if not, contact:                        *
      21                 :             :  *                                                                  *
      22                 :             :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      23                 :             :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      24                 :             :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      25                 :             :  *                                                                  *
      26                 :             : \********************************************************************/
      27                 :             : 
      28                 :             : #include <glib.h>
      29                 :             : 
      30                 :             : #include <config.h>
      31                 :             : #include <libintl.h>
      32                 :             : #include <stdlib.h>
      33                 :             : #include "platform.h"
      34                 :             : #include "qof.h"
      35                 :             : 
      36                 :             : #ifdef HAVE_LANGINFO_D_FMT
      37                 :             : # include <langinfo.h>
      38                 :             : #endif
      39                 :             : #ifndef HAVE_STRPTIME
      40                 :             : #include <strptime.h>
      41                 :             : #endif
      42                 :             : #ifdef G_OS_WIN32
      43                 :             : #  include <windows.h>
      44                 :             : #endif
      45                 :             : 
      46                 :             : #include <cinttypes>
      47                 :             : #include <unicode/calendar.h>
      48                 :             : 
      49                 :             : #include "gnc-date.h"
      50                 :             : #include "gnc-date-p.h"
      51                 :             : #include "gnc-datetime.hpp"
      52                 :             : #include "gnc-timezone.hpp"
      53                 :             : #define BOOST_ERROR_CODE_HEADER_ONLY
      54                 :             : #include <boost/date_time/local_time/local_time.hpp>
      55                 :             : 
      56                 :             : #define N_(string) string //So that xgettext will find it
      57                 :             : 
      58                 :             : #ifdef HAVE_LANGINFO_D_FMT
      59                 :             : #  define GNC_D_FMT (nl_langinfo (D_FMT))
      60                 :             : #  define GNC_D_T_FMT (nl_langinfo (D_T_FMT))
      61                 :             : #  define GNC_T_FMT (nl_langinfo (T_FMT))
      62                 :             : #elif defined(G_OS_WIN32)
      63                 :             : #  define GNC_D_FMT (qof_win32_get_time_format(QOF_WIN32_PICTURE_DATE))
      64                 :             : #  define GNC_T_FMT (qof_win32_get_time_format(QOF_WIN32_PICTURE_TIME))
      65                 :             : #  define GNC_D_T_FMT (qof_win32_get_time_format(QOF_WIN32_PICTURE_DATETIME))
      66                 :             : #else
      67                 :             : #  define GNC_D_FMT "%Y-%m-%d"
      68                 :             : #  define GNC_D_T_FMT "%Y-%m-%d %r"
      69                 :             : #  define GNC_T_FMT "%r"
      70                 :             : #endif
      71                 :             : 
      72                 :             : const char *gnc_default_strftime_date_format =
      73                 :             : #ifdef G_OS_WIN32
      74                 :             :     /* The default date format for use with strftime in Win32. */
      75                 :             :     N_("%B %#d, %Y")
      76                 :             : #else
      77                 :             :     /* The default date format for use with strftime in other OS. */
      78                 :             :     /* Translators: call "man strftime" for possible values. */
      79                 :             :     N_("%B %e, %Y")
      80                 :             : #endif
      81                 :             :     ;
      82                 :             : 
      83                 :             : /* This is now user configured through the gnome options system() */
      84                 :             : static QofDateFormat dateFormat = QOF_DATE_FORMAT_LOCALE;
      85                 :             : static QofDateFormat prevQofDateFormat = QOF_DATE_FORMAT_LOCALE;
      86                 :             : 
      87                 :             : static QofDateCompletion dateCompletion = QOF_DATE_COMPLETION_THISYEAR;
      88                 :             : static int dateCompletionBackMonths = 6;
      89                 :             : 
      90                 :             : /* This static indicates the debugging module that this .o belongs to. */
      91                 :             : static QofLogModule log_module = QOF_MOD_ENGINE;
      92                 :             : 
      93                 :             : /****************** Posix Replacement Functions ***************************/
      94                 :             : void
      95                 :       88408 : gnc_tm_free (struct tm* time)
      96                 :             : {
      97                 :       88408 :     free(time);
      98                 :       88408 : }
      99                 :             : 
     100                 :             : struct tm*
     101                 :       88403 : gnc_localtime (const time64 *secs)
     102                 :             : {
     103                 :       88403 :     auto time = static_cast<struct tm*>(calloc(1, sizeof(struct tm)));
     104                 :       88403 :     if (gnc_localtime_r (secs, time) == nullptr)
     105                 :             :     {
     106                 :           1 :         gnc_tm_free (time);
     107                 :           1 :         return nullptr;
     108                 :             :     }
     109                 :       88402 :     return time;
     110                 :             : }
     111                 :             : 
     112                 :             : struct tm*
     113                 :       95069 : gnc_localtime_r (const time64 *secs, struct tm* time)
     114                 :             : {
     115                 :             :     try
     116                 :             :     {
     117                 :       95069 :         *time = static_cast<struct tm>(GncDateTime(*secs));
     118                 :       95068 :         return time;
     119                 :             :     }
     120                 :           1 :     catch(std::invalid_argument&)
     121                 :             :     {
     122                 :           1 :         return nullptr;
     123                 :           1 :     }
     124                 :             : }
     125                 :             : 
     126                 :             : static void
     127                 :      297248 : normalize_time_component (int *inner, int *outer, int divisor)
     128                 :             : {
     129                 :      308895 :      while (*inner < 0)
     130                 :             :      {
     131                 :       11647 :           --(*outer);
     132                 :       11647 :           *inner += divisor;
     133                 :             :      }
     134                 :      305898 :      while (*inner >= divisor)
     135                 :             :      {
     136                 :        8650 :           ++(*outer);
     137                 :        8650 :           *inner -= divisor;
     138                 :             :      }
     139                 :      297248 : }
     140                 :             : 
     141                 :             : static void
     142                 :       73631 : normalize_struct_tm (struct tm* time)
     143                 :             : {
     144                 :       73631 :      gint year = time->tm_year + 1900;
     145                 :             :      gint last_day;
     146                 :             : 
     147                 :             :      /* Gregorian_date throws if it gets an out-of-range year
     148                 :             :       * so clamp year into gregorian_date's range.
     149                 :             :       */
     150                 :       73631 :      if (year < 1400) year += 1400;
     151                 :       73631 :      if (year > 9999) year %= 10000;
     152                 :             : 
     153                 :       73631 :      normalize_time_component (&(time->tm_sec), &(time->tm_min), 60);
     154                 :       73631 :      normalize_time_component (&(time->tm_min), &(time->tm_hour), 60);
     155                 :       73631 :      normalize_time_component (&(time->tm_hour), &(time->tm_mday), 24);
     156                 :       73631 :      normalize_time_component (&(time->tm_mon), &year, 12);
     157                 :             : 
     158                 :             :      // auto month_in_range = []int (int m){ return (m + 12) % 12; }
     159                 :       74154 :      while (time->tm_mday < 1)
     160                 :             :      {
     161                 :         523 :          normalize_time_component (&(--time->tm_mon), &year, 12);
     162                 :         523 :          last_day = gnc_date_get_last_mday (time->tm_mon, year);
     163                 :         523 :          time->tm_mday += last_day;
     164                 :             :      }
     165                 :       73631 :      last_day = gnc_date_get_last_mday (time->tm_mon, year);
     166                 :       75832 :      while (time->tm_mday > last_day)
     167                 :             :      {
     168                 :        2201 :           time->tm_mday -= last_day;
     169                 :        2201 :           normalize_time_component(&(++time->tm_mon), &year, 12);
     170                 :        2201 :           last_day = gnc_date_get_last_mday (time->tm_mon, year);
     171                 :             :      }
     172                 :       73631 :      time->tm_year = year - 1900;
     173                 :       73631 : }
     174                 :             : 
     175                 :             : struct tm*
     176                 :          15 : gnc_gmtime (const time64 *secs)
     177                 :             : {
     178                 :             :     try
     179                 :             :     {
     180                 :          15 :         GncDateTime gncdt(*secs);
     181                 :          14 :         auto time = static_cast<struct tm*>(calloc(1, sizeof(struct tm)));
     182                 :          14 :         *time = gncdt.utc_tm();
     183                 :          14 :         return time;
     184                 :          14 :     }
     185                 :           1 :     catch(std::invalid_argument&)
     186                 :             :     {
     187                 :           1 :         return nullptr;
     188                 :           1 :     }
     189                 :             : 
     190                 :             : }
     191                 :             : 
     192                 :             : gint
     193                 :          42 : gnc_start_of_week (void)
     194                 :             : {
     195                 :             :     /* icu's day of week is 1 based. Using 0 here to mean unset or error while setting */
     196                 :             :     static int cached_result = 0;
     197                 :             : 
     198                 :          42 :     if (!cached_result)
     199                 :             :     {
     200                 :          42 :         UErrorCode err = U_ZERO_ERROR;
     201                 :          42 :         auto cal = icu::Calendar::createInstance (err);
     202                 :          42 :         if (!cal)
     203                 :             :         {
     204                 :           0 :             PERR("ICU error: %s\n", u_errorName (err));
     205                 :           0 :             return 0;
     206                 :             :         }
     207                 :             : 
     208                 :             :         /* 1 for sunday, 2 for monday, etc. */
     209                 :          42 :         cached_result = cal->getFirstDayOfWeek (err);
     210                 :          42 :         delete cal;
     211                 :             :     }
     212                 :             : 
     213                 :          42 :     return cached_result;
     214                 :             : }
     215                 :             : 
     216                 :             : time64
     217                 :       73581 : gnc_mktime (struct tm* time)
     218                 :             : {
     219                 :             :     try
     220                 :             :     {
     221                 :       73581 :         normalize_struct_tm (time);
     222                 :       73581 :         GncDateTime gncdt(*time);
     223                 :       73581 :         *time = static_cast<struct tm>(gncdt);
     224                 :       73581 :         return static_cast<time64>(gncdt);
     225                 :       73581 :     }
     226                 :           0 :     catch(std::invalid_argument&)
     227                 :             :     {
     228                 :           0 :         return 0;
     229                 :           0 :     }
     230                 :             : }
     231                 :             : 
     232                 :             : time64
     233                 :          18 : gnc_timegm (struct tm* time)
     234                 :             : {
     235                 :             :     try
     236                 :             :     {
     237                 :          18 :         normalize_struct_tm(time);
     238                 :          18 :         GncDateTime gncdt(*time);
     239                 :          18 :         *time = static_cast<struct tm>(gncdt);
     240                 :          18 :         time->tm_sec -= gncdt.offset();
     241                 :          18 :         normalize_struct_tm(time);
     242                 :             : #ifdef HAVE_STRUcT_TM_GMTOFF
     243                 :             :         time->tm_gmtoff = 0;
     244                 :             : #endif
     245                 :          18 :         return static_cast<time64>(gncdt) - gncdt.offset();
     246                 :          18 :     }
     247                 :           0 :     catch(std::invalid_argument&)
     248                 :             :     {
     249                 :           0 :         return 0;
     250                 :           0 :     }
     251                 :             : }
     252                 :             : 
     253                 :             : char*
     254                 :           6 : gnc_ctime (const time64 *secs)
     255                 :             : {
     256                 :           6 :     return gnc_print_time64(*secs, "%a %b %d %H:%M:%S %Y");
     257                 :             : }
     258                 :             : 
     259                 :             : time64
     260                 :        9419 : gnc_time (time64 *tbuf)
     261                 :             : {
     262                 :        9419 :     GncDateTime gncdt;
     263                 :        9419 :     auto time = static_cast<time64>(gncdt);
     264                 :        9419 :     if (tbuf != nullptr)
     265                 :          15 :         *tbuf = time;
     266                 :        9419 :     return time;
     267                 :        9419 : }
     268                 :             : 
     269                 :             : gdouble
     270                 :           0 : gnc_difftime (const time64 secs1, const time64 secs2)
     271                 :             : {
     272                 :           0 :     PWARN ("gnc_difftime is deprecated");
     273                 :           0 :     return (double)secs1 - (double)secs2;
     274                 :             : }
     275                 :             : 
     276                 :             : /****************************************************************************/
     277                 :             : 
     278                 :             : const char*
     279                 :           8 : gnc_date_dateformat_to_string(QofDateFormat format)
     280                 :             : {
     281                 :           8 :     switch (format)
     282                 :             :     {
     283                 :           1 :     case QOF_DATE_FORMAT_US:
     284                 :           1 :         return "us";
     285                 :           1 :     case QOF_DATE_FORMAT_UK:
     286                 :           1 :         return "uk";
     287                 :           1 :     case QOF_DATE_FORMAT_CE:
     288                 :           1 :         return "ce";
     289                 :           1 :     case QOF_DATE_FORMAT_ISO:
     290                 :           1 :         return "iso";
     291                 :           1 :     case QOF_DATE_FORMAT_UTC:
     292                 :           1 :         return "utc";
     293                 :           1 :     case QOF_DATE_FORMAT_LOCALE:
     294                 :           1 :         return "locale";
     295                 :           1 :     case QOF_DATE_FORMAT_CUSTOM:
     296                 :           1 :         return "custom";
     297                 :           1 :     case QOF_DATE_FORMAT_UNSET:
     298                 :           1 :         return "unset";
     299                 :           0 :     default:
     300                 :           0 :         return nullptr;
     301                 :             :     }
     302                 :             : }
     303                 :             : 
     304                 :             : gboolean
     305                 :          11 : gnc_date_string_to_dateformat(const char* fmt_str, QofDateFormat *format)
     306                 :             : {
     307                 :          11 :     if (!fmt_str)
     308                 :           1 :         return TRUE;
     309                 :             : 
     310                 :          10 :     if (!strcmp(fmt_str, "us"))
     311                 :           1 :         *format = QOF_DATE_FORMAT_US;
     312                 :           9 :     else if (!strcmp(fmt_str, "uk"))
     313                 :           1 :         *format = QOF_DATE_FORMAT_UK;
     314                 :           8 :     else if (!strcmp(fmt_str, "ce"))
     315                 :           1 :         *format = QOF_DATE_FORMAT_CE;
     316                 :           7 :     else if (!strcmp(fmt_str, "utc"))
     317                 :           1 :         *format = QOF_DATE_FORMAT_UTC;
     318                 :           6 :     else if (!strcmp(fmt_str, "iso"))
     319                 :           1 :         *format = QOF_DATE_FORMAT_ISO;
     320                 :           5 :     else if (!strcmp(fmt_str, "locale"))
     321                 :           1 :         *format = QOF_DATE_FORMAT_LOCALE;
     322                 :           4 :     else if (!strcmp(fmt_str, "custom"))
     323                 :           1 :         *format = QOF_DATE_FORMAT_CUSTOM;
     324                 :           3 :     else if (!strcmp(fmt_str, "unset"))
     325                 :           1 :         *format = QOF_DATE_FORMAT_UNSET;
     326                 :             :     else
     327                 :           2 :         return TRUE;
     328                 :             : 
     329                 :           8 :     return FALSE;
     330                 :             : }
     331                 :             : 
     332                 :             : const char*
     333                 :           4 : gnc_date_monthformat_to_string(GNCDateMonthFormat format)
     334                 :             : {
     335                 :             :  //avoid UB if format is out of range
     336                 :           4 :     switch (static_cast<uint8_t>(format))
     337                 :             :     {
     338                 :           1 :     case GNCDATE_MONTH_NUMBER:
     339                 :           1 :         return "number";
     340                 :           1 :     case GNCDATE_MONTH_ABBREV:
     341                 :           1 :         return "abbrev";
     342                 :           1 :     case GNCDATE_MONTH_NAME:
     343                 :           1 :         return "name";
     344                 :           1 :     default:
     345                 :           1 :         return nullptr;
     346                 :             :     }
     347                 :             : }
     348                 :             : 
     349                 :             : gboolean
     350                 :           6 : gnc_date_string_to_monthformat(const char *fmt_str, GNCDateMonthFormat *format)
     351                 :             : {
     352                 :           6 :     if (!fmt_str)
     353                 :           1 :         return TRUE;
     354                 :             : 
     355                 :           5 :     if (!strcmp(fmt_str, "number"))
     356                 :           1 :         *format = GNCDATE_MONTH_NUMBER;
     357                 :           4 :     else if (!strcmp(fmt_str, "abbrev"))
     358                 :           1 :         *format = GNCDATE_MONTH_ABBREV;
     359                 :           3 :     else if (!strcmp(fmt_str, "name"))
     360                 :           1 :         *format = GNCDATE_MONTH_NAME;
     361                 :             :     else
     362                 :           2 :         return TRUE;
     363                 :             : 
     364                 :           3 :     return FALSE;
     365                 :             : }
     366                 :             : 
     367                 :             : char*
     368                 :         934 : gnc_print_time64(time64 time, const char* format)
     369                 :             : {
     370                 :             :     try
     371                 :             :     {
     372                 :         934 :         GncDateTime gncdt(time);
     373                 :         934 :         auto sstr = gncdt.format(format);
     374                 :             :         //ugly C allocation so that the ptr can be freed at the other end
     375                 :         934 :         char* cstr = static_cast<char*>(malloc(sstr.length() + 1));
     376                 :         934 :         memset(cstr, 0, sstr.length() + 1);
     377                 :         934 :         strncpy(cstr, sstr.c_str(), sstr.length());
     378                 :         934 :         return cstr;
     379                 :         934 :     }
     380                 :           0 :     catch(std::runtime_error& err)
     381                 :             :     {
     382                 :           0 :         PWARN("Error processing time64 %" PRId64 ": %s", time, err.what());
     383                 :           0 :         return nullptr;
     384                 :           0 :     }
     385                 :           0 :     catch(std::logic_error& err)
     386                 :             :     {
     387                 :           0 :         PWARN("Error processing time64 %" PRId64 ": %s", time, err.what());
     388                 :           0 :         return nullptr;
     389                 :           0 :     }
     390                 :             : }
     391                 :             : 
     392                 :             : /********************************************************************\
     393                 :             : \********************************************************************/
     394                 :             : 
     395                 :             : 
     396                 :             : /* Converts any time on a day to midday that day.
     397                 :             : 
     398                 :             :  * given a timepair contains any time on a certain day (local time)
     399                 :             :  * converts it to be midday that day.
     400                 :             :  */
     401                 :             : time64
     402                 :        4722 : time64CanonicalDayTime (time64 t)
     403                 :             : {
     404                 :             :     struct tm tm;
     405                 :        4722 :     gnc_localtime_r(&t, &tm);
     406                 :        4722 :     gnc_tm_set_day_middle(&tm);
     407                 :        9444 :     return gnc_mktime (&tm);
     408                 :             : }
     409                 :             : 
     410                 :             : /* NB: month is 1-12, year is 0001 - 9999. */
     411                 :       76699 : int gnc_date_get_last_mday (int month, int year)
     412                 :             : {
     413                 :             :     static int last_day_of_month[12] =
     414                 :             :         {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
     415                 :             :   
     416                 :       76699 :     g_assert(month >= 0 && month < 12);
     417                 :             : 
     418                 :             :     // To be a leap year, the year number must be divisible by four,
     419                 :             :     // except for end-of-century years, which must be divisible by 400.
     420                 :             : 
     421                 :       76699 :     return last_day_of_month[month] +
     422                 :       76699 :         (month == 1 && year % 4 == 0 && !(year % 100 == 0 && year % 400 != 0) ?
     423                 :       76699 :         1 : 0);
     424                 :             : }
     425                 :             : 
     426                 :          77 : QofDateFormat qof_date_format_get (void)
     427                 :             : {
     428                 :          77 :     return dateFormat;
     429                 :             : }
     430                 :             : 
     431                 :          30 : void qof_date_format_set(QofDateFormat df)
     432                 :             : {
     433                 :             : //avoid UB if df is out of range
     434                 :          30 :     auto dfi{static_cast<uint8_t>(df)};
     435                 :          30 :     if (dfi >= DATE_FORMAT_FIRST && dfi <= DATE_FORMAT_LAST)
     436                 :             :     {
     437                 :          29 :         prevQofDateFormat = dateFormat;
     438                 :          29 :         dateFormat = df;
     439                 :             :     }
     440                 :             :     else
     441                 :             :     {
     442                 :             :         /* hack alert - Use a neutral default. */
     443                 :           1 :         PERR("non-existent date format set attempted. Setting ISO default");
     444                 :           1 :         prevQofDateFormat = dateFormat;
     445                 :           1 :         dateFormat = QOF_DATE_FORMAT_ISO;
     446                 :             :     }
     447                 :             : 
     448                 :          30 :     return;
     449                 :             : }
     450                 :             : 
     451                 :             : /* set date completion method
     452                 :             : 
     453                 :             : set dateCompletion to one of QOF_DATE_COMPLETION_THISYEAR (for
     454                 :             : completing the year to the current calendar year) or
     455                 :             : QOF_DATE_COMPLETION_SLIDING (for using a sliding 12-month window). The
     456                 :             : sliding window starts 'backmonth' months before the current month (0-11).
     457                 :             : checks to make sure it's a legal value
     458                 :             : 
     459                 :             : param QofDateCompletion: indicates preferred completion method
     460                 :             : param int: the number of months to go back in time (0-11)
     461                 :             : 
     462                 :             : return void
     463                 :             : 
     464                 :             : Globals: dateCompletion dateCompletionBackMonths
     465                 :             : */
     466                 :           3 : void qof_date_completion_set(QofDateCompletion dc, int backmonths)
     467                 :             : {
     468                 :           3 :     if (dc == QOF_DATE_COMPLETION_THISYEAR ||
     469                 :             :             dc == QOF_DATE_COMPLETION_SLIDING)
     470                 :             :     {
     471                 :           3 :         dateCompletion = dc;
     472                 :             :     }
     473                 :             :     else
     474                 :             :     {
     475                 :             :         /* hack alert - Use a neutral default. */
     476                 :           0 :         PERR("non-existent date completion set attempted. Setting current year completion as default");
     477                 :           0 :         dateCompletion = QOF_DATE_COMPLETION_THISYEAR;
     478                 :             :     }
     479                 :             : 
     480                 :           3 :     if (backmonths < 0)
     481                 :             :     {
     482                 :           0 :         backmonths = 0;
     483                 :             :     }
     484                 :           3 :     else if (backmonths > 11)
     485                 :             :     {
     486                 :           0 :         backmonths = 11;
     487                 :             :     }
     488                 :           3 :     dateCompletionBackMonths = backmonths;
     489                 :             : 
     490                 :           3 :     return;
     491                 :             : }
     492                 :             : 
     493                 :             : /*
     494                 :             :  qof_date_format_get_string
     495                 :             :  get the date format string for the current format
     496                 :             :  returns: string
     497                 :             : 
     498                 :             :  Globals: dateFormat
     499                 :             : */
     500                 :       10987 : const gchar *qof_date_format_get_string(QofDateFormat df)
     501                 :             : {
     502                 :       10987 :     switch (df)
     503                 :             :     {
     504                 :          12 :     case QOF_DATE_FORMAT_US:
     505                 :          12 :         return "%m/%d/%Y";
     506                 :          12 :     case QOF_DATE_FORMAT_UK:
     507                 :          12 :         return "%d/%m/%Y";
     508                 :          12 :     case QOF_DATE_FORMAT_CE:
     509                 :          12 :         return "%d.%m.%Y";
     510                 :           0 :     case QOF_DATE_FORMAT_UTC:
     511                 :           0 :         return "%Y-%m-%dT%H:%M:%SZ";
     512                 :         162 :     case QOF_DATE_FORMAT_ISO:
     513                 :         162 :         return "%Y-%m-%d";
     514                 :           0 :     case QOF_DATE_FORMAT_UNSET: // use global
     515                 :           0 :         return qof_date_format_get_string (dateFormat);
     516                 :       10789 :     case QOF_DATE_FORMAT_LOCALE:
     517                 :             :     default:
     518                 :       10789 :         break;
     519                 :             :     };
     520                 :       10789 :     return GNC_D_FMT;
     521                 :             : }
     522                 :             : 
     523                 :           0 : const gchar *qof_date_text_format_get_string(QofDateFormat df)
     524                 :             : {
     525                 :           0 :     switch (df)
     526                 :             :     {
     527                 :           0 :     case QOF_DATE_FORMAT_US:
     528                 :           0 :         return "%b %d, %Y";
     529                 :           0 :     case QOF_DATE_FORMAT_UK:
     530                 :             :     case QOF_DATE_FORMAT_CE:
     531                 :           0 :         return "%d %b %Y";
     532                 :           0 :     case QOF_DATE_FORMAT_UTC:
     533                 :           0 :         return "%Y-%m-%dT%H:%M:%SZ";
     534                 :           0 :     case QOF_DATE_FORMAT_ISO:
     535                 :           0 :         return "%Y-%b-%d";
     536                 :           0 :     case QOF_DATE_FORMAT_UNSET: // use global
     537                 :           0 :         return qof_date_text_format_get_string (dateFormat);
     538                 :           0 :     case QOF_DATE_FORMAT_LOCALE:
     539                 :             :     default:
     540                 :           0 :         break;
     541                 :             :     };
     542                 :           0 :     return GNC_D_FMT;
     543                 :             : }
     544                 :             : 
     545                 :             : size_t
     546                 :          76 : qof_print_date_dmy_buff (char * buff, const size_t len, int day, int month, int year)
     547                 :             : {
     548                 :          76 :     if (!buff) return 0;
     549                 :             : 
     550                 :             :     try
     551                 :             :     {
     552                 :          76 :         GncDate date(year, month, day);
     553                 :          76 :         std::string str = date.format(qof_date_format_get_string(dateFormat));
     554                 :          76 :         strncpy(buff, str.c_str(), len);
     555                 :          76 :         if (str.length() >= len)
     556                 :           0 :             buff[len - 1] = '\0';
     557                 :          76 :     }
     558                 :           0 :     catch(std::logic_error& err)
     559                 :             :     {
     560                 :           0 :         PWARN("Error processing year-month-day %d-%d-%d: %s",
     561                 :             :               year, month, day, err.what());
     562                 :           0 :     }
     563                 :           0 :     catch(std::runtime_error& err)
     564                 :             :     {
     565                 :           0 :         PWARN("Error processing year-month-day %d-%d-%d: %s",
     566                 :             :               year, month, day, err.what());
     567                 :           0 :     }
     568                 :          76 :     return strlen(buff);
     569                 :             : }
     570                 :             : 
     571                 :             : size_t
     572                 :       10820 : qof_print_date_buff (char * buff, const size_t len, time64 t)
     573                 :             : {
     574                 :       10820 :     if (!buff) return 0;
     575                 :             : 
     576                 :             :     try
     577                 :             :     {
     578                 :       10820 :         GncDateTime gncdt(t);
     579                 :       10820 :         std::string str = gncdt.format(qof_date_format_get_string(dateFormat));
     580                 :       10820 :         strncpy(buff, str.c_str(), len);
     581                 :       10820 :         if (str.length() >= len)
     582                 :           0 :             buff[len - 1] = '\0';
     583                 :       10820 :     }
     584                 :           0 :     catch(std::logic_error& err)
     585                 :             :     {
     586                 :           0 :         PWARN("Error processing time64 %" PRId64 ": %s", t, err.what());
     587                 :           0 :     }
     588                 :           0 :     catch(std::runtime_error& err)
     589                 :             :     {
     590                 :           0 :         PWARN("Error processing time64 %" PRId64 ": %s", t, err.what());
     591                 :           0 :     }
     592                 :       10820 :     return strlen(buff);
     593                 :             : }
     594                 :             : 
     595                 :             : size_t
     596                 :          55 : qof_print_gdate( char *buf, size_t len, const GDate *gd )
     597                 :             : {
     598                 :             :     GDate date;
     599                 :          55 :     g_date_clear (&date, 1);
     600                 :          55 :     date = *gd;
     601                 :         220 :     return qof_print_date_dmy_buff( buf, len,
     602                 :          55 :                                     g_date_get_day(&date),
     603                 :          55 :                                     g_date_get_month(&date),
     604                 :         165 :                                     g_date_get_year(&date) );
     605                 :             : }
     606                 :             : 
     607                 :             : char *
     608                 :       10764 : qof_print_date (time64 t)
     609                 :             : {
     610                 :             :     char buff[MAX_DATE_LENGTH + 1];
     611                 :       10764 :     memset (buff, 0, sizeof (buff));
     612                 :       10764 :     qof_print_date_buff (buff, MAX_DATE_LENGTH, t);
     613                 :       21528 :     return g_strdup (buff);
     614                 :             : }
     615                 :             : 
     616                 :             : /* ============================================================== */
     617                 :             : 
     618                 :             : /* return the greatest integer <= a/b; works for b > 0 and positive or
     619                 :             :    negative a. */
     620                 :             : static int
     621                 :           1 : floordiv(int a, int b)
     622                 :             : {
     623                 :           1 :     if (a >= 0)
     624                 :             :     {
     625                 :           1 :         return a / b;
     626                 :             :     }
     627                 :             :     else
     628                 :             :     {
     629                 :           0 :         return - ((-a - 1) / b) - 1;
     630                 :             :     }
     631                 :             : }
     632                 :             : 
     633                 :             : /* Normalize the localized date format to avoid date scanning issues.
     634                 :             :  *
     635                 :             :  *   The 'O' and 'E' format modifiers are for localized input/output
     636                 :             :  *   characters. Remove them as we are always using Arabic numbers.
     637                 :             :  */
     638                 :             : static inline std::string
     639                 :           3 : normalize_format (const std::string& format)
     640                 :             : {
     641                 :           3 :     bool is_pct = false;
     642                 :           3 :     std::string normalized;
     643                 :           3 :     std::remove_copy_if(
     644                 :             :         format.begin(), format.end(), back_inserter(normalized),
     645                 :          24 :         [&is_pct](char e){
     646                 :          24 :             bool r = (is_pct && (e == 'E' || e == 'O' || e == '-'));
     647                 :          24 :             is_pct = e == '%';
     648                 :          24 :             return r;
     649                 :             :         });
     650                 :           6 :     return normalized;
     651                 :           0 : }
     652                 :             : 
     653                 :             : /* Convert a string into  day, month and year integers
     654                 :             : 
     655                 :             :     Convert a string into  day / month / year integers according to
     656                 :             :     the current dateFormat value.
     657                 :             : 
     658                 :             :     This function will always parse a single number as the day of
     659                 :             :     the month, regardless of the ordering of the dateFormat value.
     660                 :             :     Two numbers will always be parsed as the day and the month, in
     661                 :             :     the same order that they appear in the dateFormat value.  Three
     662                 :             :     numbers are parsed exactly as specified in the dateFormat field.
     663                 :             : 
     664                 :             :     Fully formatted UTC timestamp strings are converted separately.
     665                 :             : 
     666                 :             :     param   buff - pointer to date string
     667                 :             :     param     day -  will store day of the month as 1 ... 31
     668                 :             :     param     month - will store month of the year as 1 ... 12
     669                 :             :     param     year - will store the year (4-digit)
     670                 :             : 
     671                 :             :     return TRUE if date appeared to be valid.
     672                 :             : 
     673                 :             :     Globals: global dateFormat value
     674                 :             : */
     675                 :             : static gboolean
     676                 :          19 : qof_scan_date_internal (const char *buff, int *day, int *month, int *year,
     677                 :             :                         QofDateFormat which_format)
     678                 :             : {
     679                 :             :     char *dupe, *tmp, *first_field, *second_field, *third_field;
     680                 :             :     int iday, imonth, iyear;
     681                 :             :     int now_day, now_month, now_year;
     682                 :             :     struct tm *now, utc;
     683                 :             :     time64 secs;
     684                 :             : 
     685                 :          19 :     if (!buff) return(FALSE);
     686                 :             : 
     687                 :          18 :     if (which_format == QOF_DATE_FORMAT_UTC)
     688                 :             :     {
     689                 :           4 :         if (strptime(buff, QOF_UTC_DATE_FORMAT, &utc)
     690                 :           4 :             || strptime (buff, "%Y-%m-%d", &utc))
     691                 :             :         {
     692                 :           3 :             *day = utc.tm_mday;
     693                 :           3 :             *month = utc.tm_mon + 1;
     694                 :           3 :             *year = utc.tm_year + 1900;
     695                 :           3 :             return TRUE;
     696                 :             :         }
     697                 :             :         else
     698                 :             :         {
     699                 :           1 :             return FALSE;
     700                 :             :         }
     701                 :             :     }
     702                 :          14 :     dupe = g_strdup (buff);
     703                 :             : 
     704                 :          14 :     tmp = dupe;
     705                 :          14 :     first_field = nullptr;
     706                 :          14 :     second_field = nullptr;
     707                 :          14 :     third_field = nullptr;
     708                 :             : 
     709                 :             :     /* Use strtok to find delimiters */
     710                 :          14 :     if (tmp)
     711                 :             :     {
     712                 :             :         static const char *delims = ".,-+/\\()년월年月 ";
     713                 :             : 
     714                 :          14 :         first_field = strtok (tmp, delims);
     715                 :          14 :         if (first_field)
     716                 :             :         {
     717                 :          14 :             second_field = strtok (nullptr, delims);
     718                 :          14 :             if (second_field)
     719                 :             :             {
     720                 :          14 :                 third_field = strtok (nullptr, delims);
     721                 :             :             }
     722                 :             :         }
     723                 :             :     }
     724                 :             : 
     725                 :             :     /* today's date */
     726                 :          14 :     gnc_time (&secs);
     727                 :          14 :     now = gnc_localtime (&secs);
     728                 :          14 :     if (!now)
     729                 :           0 :         return FALSE;
     730                 :          14 :     now_day = now->tm_mday;
     731                 :          14 :     now_month = now->tm_mon + 1;
     732                 :          14 :     now_year = now->tm_year + 1900;
     733                 :          14 :     gnc_tm_free (now);
     734                 :             : 
     735                 :             :     /* set defaults: if day or month appear to be blank, use today's date */
     736                 :          14 :     iday = now_day;
     737                 :          14 :     imonth = now_month;
     738                 :          14 :     iyear = -1;
     739                 :             : 
     740                 :             :     /* get numeric values */
     741                 :          14 :     switch (which_format)
     742                 :             :     {
     743                 :           3 :     case QOF_DATE_FORMAT_LOCALE:
     744                 :           3 :         if (buff[0] != '\0')
     745                 :             :         {
     746                 :             :             struct tm thetime;
     747                 :             :             /* Parse time string. */
     748                 :           3 :             memset(&thetime, -1, sizeof(struct tm));
     749                 :           3 :             char *strv = strptime (buff, normalize_format(GNC_D_FMT).c_str(),
     750                 :             :                                    &thetime);
     751                 :             : 
     752                 :           3 :             if (third_field)
     753                 :             :             {
     754                 :           3 :                 if (!strv) // Parse failed, continuing gives the wrong result.
     755                 :           0 :                     return FALSE;
     756                 :             : 
     757                 :             :                 /* Easy.  All three values were parsed. */
     758                 :           3 :                 iyear = thetime.tm_year + 1900;
     759                 :           3 :                 iday = thetime.tm_mday;
     760                 :           3 :                 imonth = thetime.tm_mon + 1;
     761                 :             :             }
     762                 :           0 :             else if (second_field)
     763                 :             :             {
     764                 :             :                 /* Hard. Two values parsed.  Figure out the ordering. */
     765                 :           0 :                 if (thetime.tm_year == -1)
     766                 :             :                 {
     767                 :             :                     /* %m-%d or %d-%m. Don't care. Already parsed correctly. */
     768                 :           0 :                     iday = thetime.tm_mday;
     769                 :           0 :                     imonth = thetime.tm_mon + 1;
     770                 :             :                 }
     771                 :           0 :                 else if (thetime.tm_mon != -1)
     772                 :             :                 {
     773                 :             :                     /* Must be %Y-%m-%d. Reparse as %m-%d.*/
     774                 :           0 :                     imonth = atoi(first_field);
     775                 :           0 :                     iday = atoi(second_field);
     776                 :             :                 }
     777                 :             :                 else
     778                 :             :                 {
     779                 :             :                     /* Must be %Y-%d-%m. Reparse as %d-%m. */
     780                 :           0 :                     iday = atoi(first_field);
     781                 :           0 :                     imonth = atoi(second_field);
     782                 :             :                 }
     783                 :             :             }
     784                 :           0 :             else if (first_field)
     785                 :             :             {
     786                 :           0 :                 iday = atoi(first_field);
     787                 :             :             }
     788                 :             :         }
     789                 :           3 :         break;
     790                 :           3 :     case QOF_DATE_FORMAT_UK:
     791                 :             :     case QOF_DATE_FORMAT_CE:
     792                 :           3 :         if (third_field)
     793                 :             :         {
     794                 :           3 :             iday = atoi(first_field);
     795                 :           3 :             imonth = atoi(second_field);
     796                 :           3 :             iyear = atoi(third_field);
     797                 :             :         }
     798                 :           0 :         else if (second_field)
     799                 :             :         {
     800                 :           0 :             iday = atoi(first_field);
     801                 :           0 :             imonth = atoi(second_field);
     802                 :             :         }
     803                 :           0 :         else if (first_field)
     804                 :             :         {
     805                 :           0 :             iday = atoi(first_field);
     806                 :             :         }
     807                 :           3 :         break;
     808                 :           0 :     case QOF_DATE_FORMAT_ISO:
     809                 :           0 :         if (third_field)
     810                 :             :         {
     811                 :           0 :             iyear = atoi(first_field);
     812                 :           0 :             imonth = atoi(second_field);
     813                 :           0 :             iday = atoi(third_field);
     814                 :             :         }
     815                 :           0 :         else if (second_field)
     816                 :             :         {
     817                 :           0 :             imonth = atoi(first_field);
     818                 :           0 :             iday = atoi(second_field);
     819                 :             :         }
     820                 :           0 :         else if (first_field)
     821                 :             :         {
     822                 :           0 :             iday = atoi(first_field);
     823                 :             :         }
     824                 :           0 :         break;
     825                 :           8 :     case QOF_DATE_FORMAT_US:
     826                 :             :     default:
     827                 :           8 :         if (third_field)
     828                 :             :         {
     829                 :           5 :             imonth = atoi(first_field);
     830                 :           5 :             iday = atoi(second_field);
     831                 :           5 :             iyear = atoi(third_field);
     832                 :             :         }
     833                 :           3 :         else if (second_field)
     834                 :             :         {
     835                 :           3 :             imonth = atoi(first_field);
     836                 :           3 :             iday = atoi(second_field);
     837                 :             :         }
     838                 :           0 :         else if (first_field)
     839                 :             :         {
     840                 :           0 :             iday = atoi(first_field);
     841                 :             :         }
     842                 :           8 :         break;
     843                 :             :     }
     844                 :             : 
     845                 :          14 :     g_free (dupe);
     846                 :             : 
     847                 :          14 :     if ((imonth == 0) || (iday == 0))
     848                 :           0 :         return FALSE;
     849                 :             : 
     850                 :          14 :     if ((12 < imonth) || (31 < iday))
     851                 :             :     {
     852                 :             :         /*
     853                 :             :          * Ack! Thppfft!  Someone just fed this routine a string in the
     854                 :             :          * wrong date format.  This is known to happen if a register
     855                 :             :          * window is open when changing the date format.  Try the
     856                 :             :          * previous date format.  If that doesn't work, see if we can
     857                 :             :          * exchange month and day. If that still doesn't work,
     858                 :             :          * bail and give the caller what they asked for (garbage)
     859                 :             :          * parsed in the new format.
     860                 :             :          *
     861                 :             :          * Note: This test cannot detect any format change that only
     862                 :             :          * swaps month and day field, if the day is 12 or less.  This is
     863                 :             :          * deemed acceptable given the obscurity of this bug.
     864                 :             :          */
     865                 :           2 :         if ((which_format != prevQofDateFormat) &&
     866                 :           1 :                 qof_scan_date_internal(buff, day, month, year, prevQofDateFormat))
     867                 :             :         {
     868                 :           0 :             return(TRUE);
     869                 :             :         }
     870                 :           1 :         if ((12 < imonth) && (12 >= iday))
     871                 :             :         {
     872                 :           1 :             int tmp = imonth;
     873                 :           1 :             imonth = iday;
     874                 :           1 :             iday = tmp;
     875                 :           1 :         }
     876                 :             :         else
     877                 :             :         {
     878                 :           0 :             return FALSE;
     879                 :             :         }
     880                 :             :     }
     881                 :             : 
     882                 :             :     /* if no year was entered, choose a year according to the
     883                 :             :        dateCompletion preference. If it is
     884                 :             :        QOF_DATE_COMPLETION_THISYEAR, use the current year, else if it
     885                 :             :        is QOF_DATE_COMPLETION_SLIDING, use a sliding window that
     886                 :             :        starts dateCompletionBackMonths before the current month.
     887                 :             : 
     888                 :             :        We go by whole months, rather than days, because presumably
     889                 :             :        this is less confusing.
     890                 :             :     */
     891                 :             : 
     892                 :          14 :     if (iyear == -1)
     893                 :             :     {
     894                 :           3 :         if (dateCompletion == QOF_DATE_COMPLETION_THISYEAR)
     895                 :             :         {
     896                 :           2 :             iyear = now_year;  /* use the current year */
     897                 :             :         }
     898                 :             :         else
     899                 :             :         {
     900                 :           1 :             iyear = now_year - floordiv(imonth - now_month +
     901                 :             :                                         dateCompletionBackMonths, 12);
     902                 :             :         }
     903                 :             :     }
     904                 :             : 
     905                 :             :     /* If the year entered is smaller than 100, assume we mean the current
     906                 :             :        century (and are not revising some roman emperor's books) */
     907                 :          14 :     if (iyear < 100)
     908                 :           0 :         iyear += ((int) ((now_year + 50 - iyear) / 100)) * 100;
     909                 :             : 
     910                 :             :     /* Fix up any goofy dates */
     911                 :          14 :     struct tm tm{};
     912                 :          14 :     tm.tm_year = iyear - 1900;
     913                 :          14 :     tm.tm_mon = imonth - 1;
     914                 :          14 :     tm.tm_mday = iday;
     915                 :          14 :     normalize_struct_tm(&tm);
     916                 :             : 
     917                 :          14 :     if (year) *year = tm.tm_year + 1900;
     918                 :          14 :     if (month) *month = tm.tm_mon + 1;
     919                 :          14 :     if (day) *day = tm.tm_mday;
     920                 :          14 :     return(TRUE);
     921                 :             : }
     922                 :             : 
     923                 :             : gboolean
     924                 :          18 : qof_scan_date (const char *buff, int *day, int *month, int *year)
     925                 :             : {
     926                 :          18 :     return qof_scan_date_internal(buff, day, month, year, dateFormat);
     927                 :             : }
     928                 :             : 
     929                 :             : /* Return the field separator for the current date format
     930                 :             : return date character
     931                 :             : */
     932                 :           0 : char dateSeparator (void)
     933                 :             : {
     934                 :             :     static char locale_separator = '\0';
     935                 :             : 
     936                 :           0 :     switch (dateFormat)
     937                 :             :     {
     938                 :           0 :     case QOF_DATE_FORMAT_CE:
     939                 :           0 :         return '.';
     940                 :           0 :     case QOF_DATE_FORMAT_ISO:
     941                 :             :     case QOF_DATE_FORMAT_UTC:
     942                 :           0 :         return '-';
     943                 :           0 :     case QOF_DATE_FORMAT_US:
     944                 :             :     case QOF_DATE_FORMAT_UK:
     945                 :             :     default:
     946                 :           0 :         return '/';
     947                 :           0 :     case QOF_DATE_FORMAT_LOCALE:
     948                 :           0 :         if (locale_separator != '\0')
     949                 :           0 :             return locale_separator;
     950                 :             :         else
     951                 :             :         {
     952                 :             :             /* Make a guess */
     953                 :             :             gchar string[256];
     954                 :             :             struct tm tm;
     955                 :             :             time64 secs;
     956                 :             :             gchar *s;
     957                 :             : 
     958                 :           0 :             secs = gnc_time (nullptr);
     959                 :           0 :             gnc_localtime_r(&secs, &tm);
     960                 :             :             auto normalized_fmt =
     961                 :           0 :                 normalize_format(qof_date_format_get_string(dateFormat));
     962                 :           0 :             qof_strftime(string, sizeof(string), normalized_fmt.c_str(), &tm);
     963                 :             : 
     964                 :           0 :             for (s = string; *s != '\0'; s++)
     965                 :           0 :                 if (!isdigit(*s))
     966                 :           0 :                     return (locale_separator = *s);
     967                 :           0 :         }
     968                 :           0 :         break;
     969                 :             :     }
     970                 :           0 :     return '\0';
     971                 :             : }
     972                 :             : 
     973                 :             : /* The following functions have Win32 forms in qof-win32.c */
     974                 :             : #ifndef G_OS_WIN32
     975                 :             : gchar *
     976                 :        1645 : qof_time_format_from_utf8(const gchar *utf8_format)
     977                 :             : {
     978                 :             :     gchar *retval;
     979                 :        1645 :     GError *error = nullptr;
     980                 :             : 
     981                 :        1645 :     retval = g_locale_from_utf8(utf8_format, -1, nullptr, nullptr, &error);
     982                 :             : 
     983                 :        1645 :     if (!retval)
     984                 :             :     {
     985                 :           0 :         g_warning("Could not convert format '%s' from UTF-8: %s", utf8_format,
     986                 :             :                   error->message);
     987                 :           0 :         g_error_free(error);
     988                 :             :     }
     989                 :        1645 :     return retval;
     990                 :             : }
     991                 :             : 
     992                 :             : gchar *
     993                 :        1645 : qof_formatted_time_to_utf8(const gchar *locale_string)
     994                 :             : {
     995                 :             :     gchar *retval;
     996                 :        1645 :     GError *error = nullptr;
     997                 :             : 
     998                 :        1645 :     retval = g_locale_to_utf8(locale_string, -1, nullptr, nullptr, &error);
     999                 :             : 
    1000                 :        1645 :     if (!retval)
    1001                 :             :     {
    1002                 :           0 :         g_warning("Could not convert '%s' to UTF-8: %s", locale_string,
    1003                 :             :                   error->message);
    1004                 :           0 :         g_error_free(error);
    1005                 :             :     }
    1006                 :        1645 :     return retval;
    1007                 :             : }
    1008                 :             : #endif /* G_OS_WIN32 */
    1009                 :             : 
    1010                 :             : static gchar *
    1011                 :        1645 : qof_format_time(const gchar *format, const struct tm *tm)
    1012                 :             : {
    1013                 :             :     gchar *locale_format, *tmpbuf, *retval;
    1014                 :             :     gsize tmplen, tmpbufsize;
    1015                 :             : 
    1016                 :        1645 :     g_return_val_if_fail(format, 0);
    1017                 :        1645 :     g_return_val_if_fail(tm, 0);
    1018                 :             : 
    1019                 :        1645 :     locale_format = qof_time_format_from_utf8(format);
    1020                 :        1645 :     if (!locale_format)
    1021                 :           0 :         return nullptr;
    1022                 :             : 
    1023                 :        1645 :     tmpbufsize = MAX(128, strlen(locale_format) * 2);
    1024                 :             :     while (TRUE)
    1025                 :             :     {
    1026                 :           0 :         tmpbuf = static_cast<gchar*>(g_malloc(tmpbufsize));
    1027                 :             : 
    1028                 :             :         /* Set the first byte to something other than '\0', to be able to
    1029                 :             :          * recognize whether strftime actually failed or just returned "".
    1030                 :             :          */
    1031                 :        1645 :         tmpbuf[0] = '\1';
    1032                 :        1645 :         tmplen = strftime(tmpbuf, tmpbufsize, locale_format, tm);
    1033                 :             : 
    1034                 :        1645 :         if (tmplen == 0 && tmpbuf[0] != '\0')
    1035                 :             :         {
    1036                 :           0 :             g_free(tmpbuf);
    1037                 :           0 :             tmpbufsize *= 2;
    1038                 :             : 
    1039                 :           0 :             if (tmpbufsize > 65536)
    1040                 :             :             {
    1041                 :           0 :                 g_warning("Maximum buffer size for qof_format_time "
    1042                 :             :                           "exceeded: giving up");
    1043                 :           0 :                 g_free(locale_format);
    1044                 :             : 
    1045                 :           0 :                 return nullptr;
    1046                 :             :             }
    1047                 :             :         }
    1048                 :             :         else
    1049                 :             :         {
    1050                 :             :             break;
    1051                 :             :         }
    1052                 :             :     }
    1053                 :        1645 :     g_free(locale_format);
    1054                 :             : 
    1055                 :        1645 :     retval = qof_formatted_time_to_utf8(tmpbuf);
    1056                 :        1645 :     g_free(tmpbuf);
    1057                 :             : 
    1058                 :        1645 :     return retval;
    1059                 :             : }
    1060                 :             : 
    1061                 :             : gsize
    1062                 :        1645 : qof_strftime(gchar *buf, gsize max, const gchar *format, const struct tm *tm)
    1063                 :             : {
    1064                 :             :     gsize convlen, retval;
    1065                 :             :     gchar *convbuf;
    1066                 :             : 
    1067                 :        1645 :     g_return_val_if_fail(buf, 0);
    1068                 :        1645 :     g_return_val_if_fail(max > 0, 0);
    1069                 :        1645 :     g_return_val_if_fail(format, 0);
    1070                 :        1645 :     g_return_val_if_fail(tm, 0);
    1071                 :             : 
    1072                 :        1645 :     convbuf = qof_format_time(format, tm);
    1073                 :        1645 :     if (!convbuf)
    1074                 :             :     {
    1075                 :           0 :         buf[0] = '\0';
    1076                 :           0 :         return 0;
    1077                 :             :     }
    1078                 :             : 
    1079                 :        1645 :     convlen = strlen(convbuf);
    1080                 :             : 
    1081                 :        1645 :     if (max <= convlen)
    1082                 :             :     {
    1083                 :             :         /* Ensure only whole characters are copied into the buffer. */
    1084                 :           0 :         gchar *end = g_utf8_find_prev_char(convbuf, convbuf + max);
    1085                 :           0 :         g_assert(end != nullptr);
    1086                 :           0 :         convlen = end - convbuf;
    1087                 :             : 
    1088                 :             :         /* Return 0 because the buffer isn't large enough. */
    1089                 :           0 :         retval = 0;
    1090                 :             :     }
    1091                 :             :     else
    1092                 :             :     {
    1093                 :        1645 :         retval = convlen;
    1094                 :             :     }
    1095                 :             : 
    1096                 :        1645 :     memcpy(buf, convbuf, convlen);
    1097                 :        1645 :     buf[convlen] = '\0';
    1098                 :        1645 :     g_free(convbuf);
    1099                 :             : 
    1100                 :        1645 :     return retval;
    1101                 :             : }
    1102                 :             : 
    1103                 :             : /********************************************************************\
    1104                 :             : \********************************************************************/
    1105                 :             : 
    1106                 :             : gchar *
    1107                 :          44 : gnc_date_timestamp (void)
    1108                 :             : {
    1109                 :          44 :     auto timestamp = GncDateTime::timestamp();
    1110                 :         132 :     return g_strdup(timestamp.c_str());
    1111                 :          44 : }
    1112                 :             : 
    1113                 :             : /********************************************************************\
    1114                 :             :  * iso 8601 datetimes should look like 1998-07-02 11:00:00.68-05
    1115                 :             : \********************************************************************/
    1116                 :             : /* Unfortunately, not all strptime or struct tm implementations
    1117                 :             :  * support timezones, so we have to do this with sscanf.
    1118                 :             :  */
    1119                 :             : 
    1120                 :             : #define ISO_DATE_FORMAT "%d-%d-%d %d:%d:%lf%s"
    1121                 :             : time64
    1122                 :        2652 : gnc_iso8601_to_time64_gmt(const char *cstr)
    1123                 :             : {
    1124                 :        2652 :     if (!cstr) return INT64_MAX;
    1125                 :             :     try
    1126                 :             :     {
    1127                 :        2651 :         GncDateTime gncdt(cstr);
    1128                 :        2651 :         return static_cast<time64>(gncdt);
    1129                 :        2651 :     }
    1130                 :           0 :     catch(std::logic_error& err)
    1131                 :             :     {
    1132                 :           0 :         PWARN("Error processing %s: %s", cstr, err.what());
    1133                 :           0 :         return INT64_MAX;
    1134                 :           0 :     }
    1135                 :           0 :     catch(std::runtime_error& err)
    1136                 :             :     {
    1137                 :           0 :         PWARN("Error processing time64 %s: %s", cstr, err.what());
    1138                 :           0 :         return INT64_MAX;
    1139                 :           0 :     }
    1140                 :             : }
    1141                 :             : 
    1142                 :             : /********************************************************************\
    1143                 :             : \********************************************************************/
    1144                 :             : 
    1145                 :             : char *
    1146                 :       19392 : gnc_time64_to_iso8601_buff (time64 time, char * buff)
    1147                 :             : {
    1148                 :       19392 :     if (!buff) return nullptr;
    1149                 :             :     try
    1150                 :             :     {
    1151                 :       19391 :         GncDateTime gncdt(time);
    1152                 :       19391 :         auto sstr = gncdt.format_iso8601();
    1153                 :             : 
    1154                 :       19391 :         memset(buff, 0, sstr.length() + 1);
    1155                 :       19391 :         strncpy(buff, sstr.c_str(), sstr.length());
    1156                 :       19391 :         return buff + sstr.length();
    1157                 :       19391 :     }
    1158                 :           0 :     catch(std::logic_error& err)
    1159                 :             :     {
    1160                 :           0 :         PWARN("Error processing time64 %" PRId64 ": %s", time, err.what());
    1161                 :           0 :         return buff;
    1162                 :           0 :     }
    1163                 :           0 :     catch(std::runtime_error& err)
    1164                 :             :     {
    1165                 :           0 :         PWARN("Error processing time64 %" PRId64 ": %s", time, err.what());
    1166                 :           0 :         return buff;
    1167                 :           0 :     }
    1168                 :             : }
    1169                 :             : 
    1170                 :             : #define THIRTY_TWO_YEARS 0x3c30fc00LL
    1171                 :             : 
    1172                 :             : static time64
    1173                 :       11956 : gnc_dmy2time64_internal (int day, int month, int year, DayPart day_part)
    1174                 :             : {
    1175                 :             :     try
    1176                 :             :     {
    1177                 :       11956 :         auto date = GncDate(year, month, day);
    1178                 :       11939 :         return static_cast<time64>(GncDateTime (date, day_part));
    1179                 :       11939 :     }
    1180                 :          17 :     catch(const std::logic_error& err)
    1181                 :             :     {
    1182                 :          17 :         PWARN("Date computation error from Y-M-D %d-%d-%d: %s",
    1183                 :             :               year, month, day, err.what());
    1184                 :          17 :         return INT64_MAX;
    1185                 :          17 :     }
    1186                 :           0 :     catch(const std::runtime_error& err)
    1187                 :             :     {
    1188                 :           0 :         PWARN("Date computation error from Y-M-D %d-%d-%d: %s",
    1189                 :             :               year, month, day, err.what());
    1190                 :           0 :         return INT64_MAX;
    1191                 :           0 :     }
    1192                 :             : }
    1193                 :             : 
    1194                 :             : time64
    1195                 :        6814 : gnc_dmy2time64 (int day, int month, int year)
    1196                 :             : {
    1197                 :        6814 :     return gnc_dmy2time64_internal (day, month, year, DayPart::start);
    1198                 :             : }
    1199                 :             : 
    1200                 :             : time64
    1201                 :        2836 : gnc_dmy2time64_end (int day, int month, int year)
    1202                 :             : {
    1203                 :        2836 :     return gnc_dmy2time64_internal (day, month, year, DayPart::end);
    1204                 :             : }
    1205                 :             : 
    1206                 :             : time64
    1207                 :        2306 : gnc_dmy2time64_neutral (int day, int month, int year)
    1208                 :             : {
    1209                 :        2306 :     return gnc_dmy2time64_internal (day, month, year, DayPart::neutral);
    1210                 :             : }
    1211                 :             : 
    1212                 :             : 
    1213                 :             : /* The GDate setter functions all in the end use g_date_set_time_t,
    1214                 :             :  * which in turn relies on localtime and is therefore subject to the
    1215                 :             :  * 2038 bug.
    1216                 :             :  */
    1217                 :         208 : GDate time64_to_gdate (time64 t)
    1218                 :             : {
    1219                 :             :     GDate result;
    1220                 :             : 
    1221                 :         208 :     g_date_clear (&result, 1);
    1222                 :         208 :     GncDateTime time(t);
    1223                 :         208 :     auto date = time.date().year_month_day();
    1224                 :         208 :     g_date_set_dmy (&result, date.day, static_cast<GDateMonth>(date.month),
    1225                 :         208 :                     date.year);
    1226                 :         208 :     g_assert(g_date_valid (&result));
    1227                 :             : 
    1228                 :         208 :     return result;
    1229                 :         208 : }
    1230                 :             : 
    1231                 :         587 : GDate* gnc_g_date_new_today ()
    1232                 :             : {
    1233                 :         587 :     GncDate gncd;
    1234                 :         587 :     auto ymd = gncd.year_month_day();
    1235                 :         587 :     auto month = static_cast<GDateMonth>(ymd.month);
    1236                 :         587 :     auto result = g_date_new_dmy (ymd.day, month, ymd.year);
    1237                 :         587 :     g_assert(g_date_valid (result));
    1238                 :         587 :     return result;
    1239                 :         587 : }
    1240                 :             : 
    1241                 :             : void
    1242                 :         570 : gnc_gdate_set_today (GDate* gd)
    1243                 :             : {
    1244                 :         570 :     GDate *today = gnc_g_date_new_today ();
    1245                 :         570 :     g_date_set_julian (gd, g_date_get_julian (today));
    1246                 :         570 :     g_date_free (today);
    1247                 :         570 : }
    1248                 :             : 
    1249                 :             : void
    1250                 :         203 : gnc_gdate_set_time64 (GDate* gd, time64 time)
    1251                 :             : {
    1252                 :             :     struct tm tm;
    1253                 :         203 :     gnc_localtime_r(&time, &tm);
    1254                 :         203 :     g_date_set_dmy (gd, tm.tm_mday,
    1255                 :         203 :                     static_cast<GDateMonth>(tm.tm_mon + 1),
    1256                 :         203 :                     tm.tm_year + 1900);
    1257                 :         203 : }
    1258                 :             : 
    1259                 :        2262 : time64 gdate_to_time64 (GDate d)
    1260                 :             : {
    1261                 :        2262 :     return gnc_dmy2time64_neutral (g_date_get_day(&d),
    1262                 :        2262 :                                      g_date_get_month(&d),
    1263                 :        4524 :                                      g_date_get_year(&d));
    1264                 :             : }
    1265                 :             : 
    1266                 :             : static void
    1267                 :          30 : gnc_tm_get_day_start (struct tm *tm, time64 time_val)
    1268                 :             : {
    1269                 :             :     /* Get the equivalent time structure */
    1270                 :          30 :     if (!gnc_localtime_r(&time_val, tm))
    1271                 :           0 :         return;
    1272                 :          30 :     gnc_tm_set_day_start(tm);
    1273                 :             : }
    1274                 :             : 
    1275                 :             : void
    1276                 :           0 : gnc_tm_set_day_neutral (struct tm *tm)
    1277                 :             : {
    1278                 :           0 :     auto time_val{gnc_dmy2time64_internal(tm->tm_mday, tm->tm_mon + 1,
    1279                 :           0 :                                           tm->tm_year + 1900, DayPart::neutral)};
    1280                 :           0 :     gnc_localtime_r(&time_val, tm);
    1281                 :           0 : }
    1282                 :             : 
    1283                 :             : static void
    1284                 :           0 : gnc_tm_get_day_neutral (struct tm *tm, time64 time_val)
    1285                 :             : {
    1286                 :             :     /* Get the equivalent time structure */
    1287                 :           0 :     if (!gnc_localtime_r(&time_val, tm))
    1288                 :           0 :         return;
    1289                 :           0 :     gnc_tm_set_day_neutral(tm);
    1290                 :             : }
    1291                 :             : 
    1292                 :             : static void
    1293                 :          38 : gnc_tm_get_day_end (struct tm *tm, time64 time_val)
    1294                 :             : {
    1295                 :             :     /* Get the equivalent time structure */
    1296                 :          38 :     if (!gnc_localtime_r(&time_val, tm))
    1297                 :           0 :         return;
    1298                 :          38 :     gnc_tm_set_day_end(tm);
    1299                 :             : }
    1300                 :             : 
    1301                 :             : time64
    1302                 :          30 : gnc_time64_get_day_start (time64 time_val)
    1303                 :             : {
    1304                 :             :     struct tm tm;
    1305                 :             :     time64 new_time;
    1306                 :             : 
    1307                 :          30 :     gnc_tm_get_day_start(&tm, time_val);
    1308                 :          30 :     new_time = gnc_mktime(&tm);
    1309                 :          30 :     return new_time;
    1310                 :             : }
    1311                 :             : 
    1312                 :             : time64
    1313                 :           0 : gnc_time64_get_day_neutral (time64 time_val)
    1314                 :             : {
    1315                 :             :     struct tm tm;
    1316                 :           0 :     gnc_localtime_r(&time_val, &tm);
    1317                 :           0 :     return gnc_dmy2time64_internal(tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900,
    1318                 :           0 :                                    DayPart::neutral);
    1319                 :             : }
    1320                 :             : 
    1321                 :             : time64
    1322                 :          35 : gnc_time64_get_day_end (time64 time_val)
    1323                 :             : {
    1324                 :             :     struct tm tm;
    1325                 :             :     time64 new_time;
    1326                 :             : 
    1327                 :          35 :     gnc_tm_get_day_end(&tm, time_val);
    1328                 :          35 :     new_time = gnc_mktime(&tm);
    1329                 :          35 :     return new_time;
    1330                 :             : }
    1331                 :             : 
    1332                 :             : /* ======================================================== */
    1333                 :             : 
    1334                 :             : void
    1335                 :           0 : gnc_tm_get_today_start (struct tm *tm)
    1336                 :             : {
    1337                 :           0 :     gnc_tm_get_day_start(tm, gnc_time(nullptr));
    1338                 :           0 : }
    1339                 :             : 
    1340                 :             : void
    1341                 :           0 : gnc_tm_get_today_neutral (struct tm *tm)
    1342                 :             : {
    1343                 :           0 :     gnc_tm_get_day_neutral(tm, gnc_time(nullptr));
    1344                 :           0 : }
    1345                 :             : 
    1346                 :             : void
    1347                 :           0 : gnc_tm_get_today_end (struct tm *tm)
    1348                 :             : {
    1349                 :           0 :     gnc_tm_get_day_end(tm, gnc_time(nullptr));
    1350                 :           0 : }
    1351                 :             : 
    1352                 :             : time64
    1353                 :           0 : gnc_time64_get_today_start (void)
    1354                 :             : {
    1355                 :             :     struct tm tm;
    1356                 :             : 
    1357                 :           0 :     gnc_tm_get_day_start(&tm, gnc_time(nullptr));
    1358                 :           0 :     return gnc_mktime(&tm);
    1359                 :             : }
    1360                 :             : 
    1361                 :             : time64
    1362                 :           3 : gnc_time64_get_today_end (void)
    1363                 :             : {
    1364                 :             :     struct tm tm;
    1365                 :             : 
    1366                 :           3 :     gnc_tm_get_day_end(&tm, gnc_time(nullptr));
    1367                 :           6 :     return gnc_mktime(&tm);
    1368                 :             : }
    1369                 :             : 
    1370                 :             : void
    1371                 :           0 : gnc_dow_abbrev(gchar *buf, int buf_len, int dow)
    1372                 :             : {
    1373                 :             :     struct tm my_tm;
    1374                 :             :     int i;
    1375                 :             : 
    1376                 :           0 :     memset(buf, 0, buf_len);
    1377                 :           0 :     memset(&my_tm, 0, sizeof(struct tm));
    1378                 :           0 :     my_tm.tm_wday = dow;
    1379                 :           0 :     i = qof_strftime(buf, buf_len, "%a", &my_tm);
    1380                 :           0 :     buf[i] = 0;
    1381                 :           0 : }
    1382                 :             : 
    1383                 :             : /* *******************************************************************
    1384                 :             :  *  GValue handling
    1385                 :             :  ********************************************************************/
    1386                 :             : 
    1387                 :             : static Time64*
    1388                 :         164 : time64_boxed_copy_func (Time64 *in_time64)
    1389                 :             : {
    1390                 :         164 :     Time64* newvalue = g_new (Time64, 1);
    1391                 :         164 :     *newvalue = *in_time64;
    1392                 :             : 
    1393                 :         164 :     return newvalue;
    1394                 :             : }
    1395                 :             : 
    1396                 :             : static void
    1397                 :         122 : time64_boxed_free_func (Time64 *in_time64)
    1398                 :             : {
    1399                 :         122 :     g_free (in_time64);
    1400                 :         122 : }
    1401                 :             : 
    1402                 :        2638 : G_DEFINE_BOXED_TYPE (Time64, time64, time64_boxed_copy_func, time64_boxed_free_func)
    1403                 :             : 
    1404                 :             : /* ================================================= */
    1405                 :             : 
    1406                 :             : gboolean
    1407                 :           0 : gnc_gdate_equal(gconstpointer gda, gconstpointer gdb)
    1408                 :             : {
    1409                 :           0 :     return (g_date_compare( (GDate*)gda, (GDate*)gdb ) == 0 ? TRUE : FALSE);
    1410                 :             : }
    1411                 :             : 
    1412                 :             : guint
    1413                 :           0 : gnc_gdate_hash( gconstpointer gd )
    1414                 :             : {
    1415                 :           0 :     gint val = (g_date_get_year( (GDate*)gd ) * 10000)
    1416                 :           0 :                + (g_date_get_month( (GDate*)gd ) * 100)
    1417                 :           0 :                + g_date_get_day( (GDate*)gd );
    1418                 :           0 :     return g_int_hash( &val );
    1419                 :             : }
    1420                 :             : 
    1421                 :             : /* ================================================= */
    1422                 :             : 
    1423                 :             : time64
    1424                 :         330 : gnc_time64_get_day_start_gdate (const GDate *date)
    1425                 :             : {
    1426                 :             :     struct tm stm;
    1427                 :             :     time64 secs;
    1428                 :             : 
    1429                 :             :     /* First convert to a 'struct tm' */
    1430                 :         330 :     g_date_to_struct_tm (date, &stm);
    1431                 :             : 
    1432                 :             :     /* Then convert to number of seconds */
    1433                 :         330 :     secs = gnc_mktime (&stm);
    1434                 :         330 :     return secs;
    1435                 :             : }
    1436                 :             : 
    1437                 :             : time64
    1438                 :         213 : gnc_time64_get_day_end_gdate (const GDate *date)
    1439                 :             : {
    1440                 :             :     struct tm stm;
    1441                 :             :     time64 secs;
    1442                 :             : 
    1443                 :             :     /* First convert to a 'struct tm' */
    1444                 :         213 :     g_date_to_struct_tm(date, &stm);
    1445                 :             : 
    1446                 :             :     /* Force to th last second of the day */
    1447                 :         213 :     stm.tm_hour = 23;
    1448                 :         213 :     stm.tm_min = 59;
    1449                 :         213 :     stm.tm_sec = 59;
    1450                 :         213 :     stm.tm_isdst = -1;
    1451                 :             : 
    1452                 :             :     /* Then convert to number of seconds */
    1453                 :         213 :     secs = gnc_mktime (&stm);
    1454                 :         213 :     return secs;
    1455                 :             : }
    1456                 :             : 
    1457                 :             : /* ================================================= */
    1458                 :             : 
    1459                 :             : void
    1460                 :           3 : gnc_gdate_set_month_start (GDate *date)
    1461                 :             : {
    1462                 :           3 :     g_date_set_day(date, 1);
    1463                 :           3 : }
    1464                 :             : 
    1465                 :             : /** Convert a GDate to the last day of the month.  This routine has no
    1466                 :             :  *  knowledge of how many days are in a month, whether its a leap
    1467                 :             :  *  year, etc.  All that information is contained in the glib date
    1468                 :             :  *  functions.
    1469                 :             :  *
    1470                 :             :  *  @param date The GDate to modify.
    1471                 :             :  */
    1472                 :             : void
    1473                 :           2 : gnc_gdate_set_month_end (GDate *date)
    1474                 :             : {
    1475                 :             :     /* First set the start of next month. */
    1476                 :           2 :     g_date_set_day(date, 1);
    1477                 :           2 :     g_date_add_months(date, 1);
    1478                 :             : 
    1479                 :             :     /* Then back up one day */
    1480                 :           2 :     g_date_subtract_days(date, 1);
    1481                 :           2 : }
    1482                 :             : 
    1483                 :             : /** Convert a GDate to the first day of the prebvious month.  This
    1484                 :             :  *  routine has no knowledge of how many days are in a month, whether
    1485                 :             :  *  its a leap year, etc.  All that information is contained in the
    1486                 :             :  *  glib date functions.
    1487                 :             :  *
    1488                 :             :  *  @param date The GDate to modify.
    1489                 :             :  */
    1490                 :             : void
    1491                 :           2 : gnc_gdate_set_prev_month_start (GDate *date)
    1492                 :             : {
    1493                 :           2 :     g_date_set_day(date, 1);
    1494                 :           2 :     g_date_subtract_months(date, 1);
    1495                 :           2 : }
    1496                 :             : 
    1497                 :             : /** Convert a GDate to the last day of the prebvious month.  This
    1498                 :             :  *  routine has no knowledge of how many days are in a month, whether
    1499                 :             :  *  its a leap year, etc.  All that information is contained in the
    1500                 :             :  *  glib date functions.
    1501                 :             :  *
    1502                 :             :  *  @param date The GDate to modify.
    1503                 :             :  */
    1504                 :             : void
    1505                 :           2 : gnc_gdate_set_prev_month_end (GDate *date)
    1506                 :             : {
    1507                 :             :     /* This will correctly handle the varying month lengths */
    1508                 :           2 :     g_date_set_day(date, 1);
    1509                 :           2 :     g_date_subtract_days(date, 1);
    1510                 :           2 : }
    1511                 :             : 
    1512                 :             : /* ================================================= */
    1513                 :             : 
    1514                 :             : void
    1515                 :           4 : gnc_gdate_set_quarter_start (GDate *date)
    1516                 :             : {
    1517                 :             :     gint months;
    1518                 :             : 
    1519                 :             :     /* Set the date to the first day of the specified month. */
    1520                 :           4 :     g_date_set_day(date, 1);
    1521                 :             : 
    1522                 :             :     /* Back up 0-2 months */
    1523                 :           4 :     months = (g_date_get_month(date) - G_DATE_JANUARY) % 3;
    1524                 :           4 :     g_date_subtract_months(date, months);
    1525                 :           4 : }
    1526                 :             : 
    1527                 :             : void
    1528                 :           4 : gnc_gdate_set_quarter_end (GDate *date)
    1529                 :             : {
    1530                 :           4 :      const GDateMonth months[] = {G_DATE_MARCH, G_DATE_JUNE,
    1531                 :             :                                   G_DATE_SEPTEMBER, G_DATE_DECEMBER};
    1532                 :           4 :      const GDateDay days[] = {31, 30, 30, 31};
    1533                 :           4 :      int quarter = (g_date_get_month (date) - 1) / 3;
    1534                 :             : 
    1535                 :           4 :      g_date_set_month (date, months[quarter]);
    1536                 :           4 :      g_date_set_day (date, days[quarter]);
    1537                 :           4 : }
    1538                 :             : 
    1539                 :             : void
    1540                 :           2 : gnc_gdate_set_prev_quarter_start (GDate *date)
    1541                 :             : {
    1542                 :           2 :     g_date_subtract_months(date, 3);
    1543                 :           2 :     gnc_gdate_set_quarter_start(date);
    1544                 :           2 : }
    1545                 :             : 
    1546                 :             : void
    1547                 :           2 : gnc_gdate_set_prev_quarter_end (GDate *date)
    1548                 :             : {
    1549                 :           2 :     g_date_subtract_months(date, 3);
    1550                 :           2 :     gnc_gdate_set_quarter_end(date);
    1551                 :           2 : }
    1552                 :             : 
    1553                 :             : /* ================================================= */
    1554                 :             : 
    1555                 :             : void
    1556                 :          31 : gnc_gdate_set_year_start (GDate *date)
    1557                 :             : {
    1558                 :          31 :     g_date_set_month(date, G_DATE_JANUARY);
    1559                 :          31 :     g_date_set_day(date, 1);
    1560                 :          31 : }
    1561                 :             : 
    1562                 :             : void
    1563                 :           4 : gnc_gdate_set_year_end (GDate *date)
    1564                 :             : {
    1565                 :           4 :     g_date_set_month(date, G_DATE_DECEMBER);
    1566                 :           4 :     g_date_set_day(date, 31);
    1567                 :           4 : }
    1568                 :             : 
    1569                 :             : void
    1570                 :           5 : gnc_gdate_set_prev_year_start (GDate *date)
    1571                 :             : {
    1572                 :           5 :     gnc_gdate_set_year_start(date);
    1573                 :           5 :     g_date_subtract_years(date, 1);
    1574                 :           5 : }
    1575                 :             : 
    1576                 :             : void
    1577                 :           2 : gnc_gdate_set_prev_year_end (GDate *date)
    1578                 :             : {
    1579                 :           2 :     gnc_gdate_set_year_end(date);
    1580                 :           2 :     g_date_subtract_years(date, 1);
    1581                 :           2 : }
    1582                 :             : 
    1583                 :             : /* ================================================= */
    1584                 :             : 
    1585                 :             : void
    1586                 :           0 : gnc_gdate_set_fiscal_year_start (GDate *date,
    1587                 :             :                                  const GDate *fy_end)
    1588                 :             : {
    1589                 :             :     GDate temp;
    1590                 :             :     gboolean new_fy;
    1591                 :             : 
    1592                 :           0 :     g_return_if_fail(date);
    1593                 :           0 :     g_return_if_fail(fy_end);
    1594                 :             : 
    1595                 :             :     /* Compute the FY end that occurred this CY */
    1596                 :           0 :     temp = *fy_end;
    1597                 :           0 :     g_date_set_year(&temp, g_date_get_year(date));
    1598                 :             : 
    1599                 :             :     /* Has it already passed? */
    1600                 :           0 :     new_fy = (g_date_compare(date, &temp) > 0);
    1601                 :             : 
    1602                 :             :     /* Set start date */
    1603                 :           0 :     *date = temp;
    1604                 :           0 :     g_date_add_days(date, 1);
    1605                 :           0 :     if (!new_fy)
    1606                 :           0 :         g_date_subtract_years(date, 1);
    1607                 :             : }
    1608                 :             : 
    1609                 :             : void
    1610                 :           6 : gnc_gdate_set_fiscal_year_end (GDate *date,
    1611                 :             :                                const GDate *fy_end)
    1612                 :             : {
    1613                 :             :     GDate temp;
    1614                 :             :     gboolean new_fy;
    1615                 :             : 
    1616                 :           6 :     g_return_if_fail(date);
    1617                 :           6 :     g_return_if_fail(fy_end);
    1618                 :             : 
    1619                 :             :     /* Compute the FY end that occurred this CY */
    1620                 :           6 :     temp = *fy_end;
    1621                 :           6 :     g_date_set_year(&temp, g_date_get_year(date));
    1622                 :             : 
    1623                 :             :     /* Has it already passed? */
    1624                 :           6 :     new_fy = (g_date_compare(date, &temp) > 0);
    1625                 :             : 
    1626                 :             :     /* Set end date */
    1627                 :           6 :     *date = temp;
    1628                 :           6 :     if (new_fy)
    1629                 :           0 :         g_date_add_years(date, 1);
    1630                 :             : }
    1631                 :             : 
    1632                 :             : void
    1633                 :           0 : gnc_gdate_set_prev_fiscal_year_start (GDate *date,
    1634                 :             :                                       const GDate *fy_end)
    1635                 :             : {
    1636                 :           0 :     g_return_if_fail(date);
    1637                 :           0 :     g_return_if_fail(fy_end);
    1638                 :             : 
    1639                 :           0 :     gnc_gdate_set_fiscal_year_start(date, fy_end);
    1640                 :           0 :     g_date_subtract_years(date, 1);
    1641                 :             : }
    1642                 :             : 
    1643                 :             : void
    1644                 :           0 : gnc_gdate_set_prev_fiscal_year_end (GDate *date,
    1645                 :             :                                     const GDate *fy_end)
    1646                 :             : {
    1647                 :           0 :     g_return_if_fail(date);
    1648                 :           0 :     g_return_if_fail(fy_end);
    1649                 :             : 
    1650                 :           0 :     gnc_gdate_set_fiscal_year_end(date, fy_end);
    1651                 :           0 :     g_date_subtract_years(date, 1);
    1652                 :             : }
    1653                 :             : 
    1654                 :             : Testfuncs*
    1655                 :           0 : gnc_date_load_funcs (void)
    1656                 :             : {
    1657                 :           0 :     Testfuncs *tf = g_slice_new (Testfuncs);
    1658                 :           0 :     return tf;
    1659                 :             : }
        

Generated by: LCOV version 2.0-1