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