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