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

Generated by: LCOV version 2.0-1