QOF: qofdate.c Source File

QOF 0.7.5

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

qofdate.c
1 /********************************************************************
2 * qofdate.c - QofDate, 64bit UTC date handling.
3 * Rewritten from scratch for QOF 0.7.0
4 *
5 * Fri May 5 15:05:24 2006
6 * Copyright (C) 1991, 1993, 1997, 1998, 2002, 2006
7 * Free Software Foundation, Inc.
8 * This file contains routines modified from the GNU C Library.
9 ********************************************************************/
10 /*
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
24 */
25
26 #include "config.h"
27 #include <glib.h>
28 #include <glib/gprintf.h>
29 #include <stdlib.h>
30 #include <time.h>
31 #include "qof.h"
32 #include "qofdate-p.h"
33
34 /* from gnu libc */
35 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
36 #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
37
38 static GHashTable *DateFormatTable = NULL;
39 static gboolean QofDateInit = FALSE;
40 static QofLogModule log_module = QOF_MOD_DATE;
41 static gchar locale_separator = '\0';
42 static QofDateFormat dateFormat = QOF_DATE_FORMAT_LOCALE;
43
44 /* copied from glib */
45 static const guint16 days_in_year[2][14] =
46 { /* 0, jan feb mar apr may jun jul aug sep oct nov dec */
47 { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
48 { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
49 };
50 static const guint8 days_in_months[2][13] =
51 { /* error, jan feb mar apr may jun jul aug sep oct nov dec */
52 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
53 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
54 };
55
56 /* A single Date Format Entry. */
57 typedef struct QofDateEntry_s
58 {
59 const gchar *format;
60 const gchar *name;
61 gchar separator;
62 QofDateFormat df;
63 gboolean locale_specific;
64 } QofDateEntry;
65
66 void
67 qof_date_init (void)
68 {
69 if (!QofDateInit)
70 {
71 DateFormatTable = g_hash_table_new (g_direct_hash, g_direct_equal);
72 }
73 {
74 QofDateEntry *d = g_new0 (QofDateEntry, 1);
75 d->format = "%m/%d/%Y";
76 d->name = "us";
77 d->separator = '/';
78 d->df = QOF_DATE_FORMAT_US;
79 d->locale_specific = FALSE;
80 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
81 }
82 {
83 QofDateEntry *d = g_new0 (QofDateEntry, 1);
84 d->format = "%d/%m/%Y";
85 d->name = "uk";
86 d->separator = '/';
87 d->df = QOF_DATE_FORMAT_UK;
88 d->locale_specific = FALSE;
89 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
90 }
91 {
92 QofDateEntry *d = g_new0 (QofDateEntry, 1);
93 d->format = "%d.%m.%Y";
94 d->name = "ce";
95 d->separator = '.';
96 d->df = QOF_DATE_FORMAT_CE;
97 d->locale_specific = FALSE;
98 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
99 }
100 {
101 QofDateEntry *d = g_new0 (QofDateEntry, 1);
102 d->format = "%F";
103 d->name = "iso";
104 d->separator = '-';
105 d->df = QOF_DATE_FORMAT_ISO;
106 d->locale_specific = FALSE;
107 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
108 }
109 {
110 QofDateEntry *d = g_new0 (QofDateEntry, 1);
111 d->format = QOF_UTC_DATE_FORMAT;
112 d->name = "utc";
113 d->separator = '-';
114 d->df = QOF_DATE_FORMAT_UTC;
115 d->locale_specific = FALSE;
116 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
117 }
118 {
119 QofDateEntry *d = g_new0 (QofDateEntry, 1);
120 d->format = "%x";
121 d->name = "locale";
122 d->separator = locale_separator;
123 d->df = QOF_DATE_FORMAT_LOCALE;
124 d->locale_specific = TRUE;
125 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
126 }
127 {
128 QofDateEntry *d = g_new0 (QofDateEntry, 1);
129 d->format = "%c";
130 d->name = "custom";
131 d->separator = locale_separator;
132 d->df = QOF_DATE_FORMAT_CUSTOM;
133 d->locale_specific = TRUE;
134 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
135 }
136 {
137 QofDateEntry *d = g_new0(QofDateEntry,1);
138 d->format = "%Y-%m-%d %H:%M:%S.%N %z";
139 d->name = "iso8601";
140 d->separator = '-';
141 d->df = QOF_DATE_FORMAT_ISO8601;
142 d->locale_specific = FALSE;
143 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER(d->df), d);
144 }
145 QofDateInit = TRUE;
146 }
147
148 static void
149 hash_value_free (gpointer key __attribute__ ((unused)), gpointer value,
150 gpointer data __attribute__ ((unused)))
151 {
152 g_free (value);
153 }
154
155 void
156 qof_date_close (void)
157 {
158 if (QofDateInit)
159 {
160 g_hash_table_foreach (DateFormatTable, hash_value_free, NULL);
161 g_hash_table_destroy (DateFormatTable);
162 }
163 QofDateInit = FALSE;
164 }
165
166 guint16
167 qof_date_get_yday (gint mday, gint month, gint64 year)
168 {
169 guint8 leap;
170
171 g_return_val_if_fail (mday != 0, 0);
172 g_return_val_if_fail (month != 0, 0);
173 g_return_val_if_fail (month <= 12, 0);
174 g_return_val_if_fail (month >= 1, 0);
175 g_return_val_if_fail (year != 0, 0);
176 leap = qof_date_isleap (year);
177 g_return_val_if_fail (mday <=
178 qof_date_get_mday (month, year), 0);
179 return days_in_year[leap][month] + mday;
180 }
181
182 guint8
183 qof_date_get_mday (gint month, gint64 year)
184 {
185 g_return_val_if_fail (month != 0, 0);
186 g_return_val_if_fail (month <= 12, 0);
187 g_return_val_if_fail (month >= 1, 0);
188 g_return_val_if_fail (year != 0, 0);
189 return days_in_months[qof_date_isleap (year)][month];
190 }
191
192 gboolean
193 qof_date_is_last_mday (const QofDate *qd)
194 {
195 g_return_val_if_fail (qd, FALSE);
196 g_return_val_if_fail (qd->qd_valid, FALSE);
197 return (qd->qd_mday ==
198 qof_date_get_mday (qd->qd_mon, qd->qd_year));
199 }
200
201 gboolean
202 qof_date_format_add (const gchar * str, QofDateFormat * identifier)
203 {
204 struct tm check;
205 gint len;
206 time_t now;
207 gchar test[MAX_DATE_BUFFER];
208
210 g_return_val_if_fail (QofDateInit, FALSE);
211 g_return_val_if_fail (str, FALSE);
212 g_return_val_if_fail (strlen (str) != 0, FALSE);
213 /* prevent really long strings being passed */
214 ENTER (" str=%s", str);
215 if (strlen (str) > MAX_DATE_LENGTH)
216 {
217 LEAVE (" '%s' is too long! Max=%d str_len=%d",
218 str, MAX_DATE_LENGTH, (gint) strlen (str));
219 return FALSE;
220 }
221 /* test the incoming string using the current time. */
222 now = time (NULL);
223 test[0] = '\1';
224 check = *gmtime_r (&now, &check);
225 /* need to allow time related formats -
226 don't use g_date_strftime here. */
227 len = strftime (test, (MAX_DATE_BUFFER - 1), str, &check);
228 if (len == 0 && test[0] != '\0')
229 {
230 LEAVE (" strftime could not understand '%s'", str);
231 return FALSE;
232 }
233 len = strlen (test);
234 if (len > MAX_DATE_LENGTH)
235 {
236 LEAVE (" %s creates a string '%s' that is too long!"
237 " Max=%d str_len=%d", str, test, MAX_DATE_LENGTH, len);
238 return FALSE;
239 }
240 *identifier = g_hash_table_size (DateFormatTable) + 1;
241 {
242 QofDateEntry *d = g_new0 (QofDateEntry, 1);
243 d->format = str;
244 d->name = str;
245 d->separator = locale_separator;
246 d->df = *identifier;
247 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
248 }
249 LEAVE (" successful");
250 return TRUE;
251 }
252
253 const gchar *
254 qof_date_format_to_name (QofDateFormat format)
255 {
256 QofDateEntry *d;
257
258 g_return_val_if_fail (QofDateInit, NULL);
259 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (format));
260 if (!d)
261 {
262 PERR (" unknown format: '%d'", format);
263 return NULL;
264 }
265 return d->name;
266 }
267
268 gboolean
269 qof_date_format_set_name (const gchar * name, QofDateFormat format)
270 {
271 QofDateEntry *d;
272
273 g_return_val_if_fail (QofDateInit, FALSE);
274 if (format <= DATE_FORMAT_LAST)
275 return FALSE;
276 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (format));
277 if (!d)
278 {
279 PERR (" unknown format: '%d'", format);
280 return FALSE;
281 }
282 d->name = name;
283 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (format), d);
284 return TRUE;
285 }
286
287 QofDateFormat
288 qof_date_format_get_current (void)
289 {
290 return dateFormat;
291 }
292
293 gboolean
294 qof_date_format_set_current (QofDateFormat df)
295 {
296 QofDateEntry *d;
297
298 g_return_val_if_fail (QofDateInit, FALSE);
299 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
300 if (!d)
301 {
302 PERR (" unknown format: '%d'", df);
303 return FALSE;
304 }
305 dateFormat = d->df;
306 return TRUE;
307 }
308
309 const gchar *
310 qof_date_format_get_format (QofDateFormat df)
311 {
312 QofDateEntry *d;
313
314 g_return_val_if_fail (QofDateInit, NULL);
315 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
316 if (!d)
317 {
318 PERR (" unknown format: '%d'", df);
319 return NULL;
320 }
321 return d->format;
322 }
323
324 gchar
325 qof_date_format_get_date_separator (QofDateFormat df)
326 {
327 QofDateEntry *d;
328
329 g_return_val_if_fail (QofDateInit, locale_separator);
330 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
331 if (!d)
332 {
333 PERR (" unknown format: '%d'", df);
334 return locale_separator;
335 }
336 return d->separator;
337 }
338
339 gboolean
340 qof_date_format_set_date_separator (const gchar sep, QofDateFormat df)
341 {
342 QofDateEntry *d;
343
344 g_return_val_if_fail (QofDateInit, FALSE);
345 if (df < DATE_FORMAT_LAST)
346 {
347 DEBUG (" Prevented attempt to override a default format");
348 return FALSE;
349 }
350 if (g_ascii_isdigit (sep))
351 return FALSE;
352 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
353 if (!d)
354 {
355 PERR (" unknown format: '%d'", df);
356 return FALSE;
357 }
358 d->separator = sep;
359 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (df), d);
360 return TRUE;
361 }
362
363 struct iter
364 {
365 const gchar *name;
366 QofDateFormat df;
367 };
368
369 static void
370 lookup_name (gpointer key __attribute__ ((unused)), gpointer value,
371 gpointer data)
372 {
373 struct iter *i;
374 QofDateEntry *d;
375
376 i = (struct iter *) data;
377 d = (QofDateEntry *) value;
378 if (0 == safe_strcmp (d->name, i->name))
379 {
380 i->df = d->df;
381 }
382 }
383
384 QofDateFormat
385 qof_date_format_from_name (const gchar * name)
386 {
387 struct iter i;
388
389 if (!name)
390 return -1;
391 if (0 == safe_strcmp (name, "us"))
392 return QOF_DATE_FORMAT_US;
393 if (0 == safe_strcmp (name, "uk"))
394 return QOF_DATE_FORMAT_UK;
395 if (0 == safe_strcmp (name, "ce"))
396 return QOF_DATE_FORMAT_CE;
397 if (0 == safe_strcmp (name, "utc"))
398 return QOF_DATE_FORMAT_UTC;
399 if (0 == safe_strcmp (name, "iso"))
400 return QOF_DATE_FORMAT_ISO;
401 if (0 == safe_strcmp (name, "locale"))
402 return QOF_DATE_FORMAT_LOCALE;
403 if (0 == safe_strcmp (name, "custom"))
404 return QOF_DATE_FORMAT_CUSTOM;
405 i.name = name;
406 i.df = -1;
407 g_hash_table_foreach (DateFormatTable, lookup_name, &i);
408 return i.df;
409 }
410
411 static QofDate*
412 date_normalise (QofDate * date)
413 {
414 gint days;
415
416 g_return_val_if_fail (date, NULL);
417 date->qd_sec -= date->qd_gmt_off;
418 /* if value is negative, just add */
419 if ((date->qd_nanosecs >= QOF_NSECS) ||
420 (date->qd_nanosecs <= -QOF_NSECS))
421 {
422 date->qd_sec += date->qd_nanosecs / QOF_NSECS;
423 date->qd_nanosecs = date->qd_nanosecs % QOF_NSECS;
424 if (date->qd_nanosecs < 0)
425 {
426 date->qd_nanosecs += QOF_NSECS;
427 date->qd_sec--;
428 }
429 }
430 if ((date->qd_sec >= 60) || (date->qd_sec <= -60))
431 {
432 date->qd_min += date->qd_sec / 60;
433 date->qd_sec = date->qd_sec % 60;
434 if (date->qd_sec < 0)
435 {
436 date->qd_sec += 60;
437 date->qd_min--;
438 }
439 }
440 if ((date->qd_min >= 60) || (date->qd_min <= -60))
441 {
442 date->qd_hour += date->qd_min / 60;
443 date->qd_min = date->qd_min % 60;
444 if (date->qd_min < 0)
445 {
446 date->qd_min += 60;
447 date->qd_hour--;
448 }
449 }
450 if ((date->qd_hour >= 24) || (date->qd_hour <= -24))
451 {
452 date->qd_mday += date->qd_hour / 24;
453 date->qd_hour = date->qd_hour % 24;
454 if (date->qd_hour < 0)
455 {
456 date->qd_hour += 24;
457 date->qd_mday--;
458 }
459 }
460 if ((date->qd_mon > 12) || (date->qd_mon < -12))
461 {
462 date->qd_year += date->qd_mon / 12;
463 date->qd_mon = date->qd_mon % 12;
464 if (date->qd_mon < 0)
465 {
466 /* -1 == Dec, -4 == Sep */
467 date->qd_mon += 12 + 1;
468 date->qd_year = (date->qd_year < 0) ?
469 date->qd_year++ : date->qd_year--;
470 }
471 }
472 /* qd_mon starts at 1, not zero */
473 if (date->qd_mon == 0)
474 date->qd_mon = 1;
475 /* Year Zero does not exist, 1BC is immediately followed by 1AD. */
476 if (date->qd_year == 0)
477 date->qd_year = -1;
478 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon];
479 while (date->qd_mday < 0)
480 {
481 date->qd_mday += days;
482 date->qd_mon--;
483 if (date->qd_mon < 1)
484 {
485 date->qd_year -= date->qd_mon / 12;
486 date->qd_mon = date->qd_mon % 12;
487 /* if year was AD and is now zero, reset to BC. */
488 if ((date->qd_year == 0) && (date->qd_mon < 0))
489 date->qd_year = -1;
490 }
491 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon];
492 }
493 while (date->qd_mday > days)
494 {
495 date->qd_mday -= days;
496 date->qd_mon++;
497 if (date->qd_mon > 12)
498 {
499 date->qd_year += date->qd_mon / 12;
500 date->qd_mon = date->qd_mon % 12;
501 /* if year was BC and is now zero, reset to AD. */
502 if ((date->qd_year == 0) && (date->qd_mon > 0))
503 date->qd_year = +1;
504 }
505 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon];
506 }
507 /* use sensible defaults */
508 if (date->qd_mday == 0)
509 date->qd_mday = 1;
510 if (date->qd_mon == 0)
511 date->qd_mon = 1;
512 /* use days_in_year to set yday */
513 date->qd_yday = (date->qd_mday - 1) +
514 days_in_year[qof_date_isleap(date->qd_year)][date->qd_mon];
515 set_day_of_the_week (date);
516 /* qd_year has no realistic limits */
517 date->qd_valid = TRUE;
518 date->qd_zone = "GMT";
519 date->qd_is_dst = 0;
520 date->qd_gmt_off = 0L;
521 return date;
522 }
523
524 QofDate *
525 qof_date_parse (const gchar * str, QofDateFormat df)
526 {
527 const gchar *format;
528 QofDateError error;
529 QofDate *date;
530 gchar *check;
531
532 check = NULL;
533 error = ERR_NO_ERROR;
534 date = qof_date_new ();
535 format = qof_date_format_get_format (df);
536 check = strptime_internal (str, format, date, &error);
537 if (error != ERR_NO_ERROR)
538 {
539 qof_date_free (date);
540 return NULL;
541 }
542 date = date_normalise (date);
543 return date;
544 }
545
546 gchar *
547 qof_date_print (const QofDate * date, QofDateFormat df)
548 {
549 size_t result;
550 gchar temp[MAX_DATE_BUFFER];
551 QofDateEntry *d;
552
553 g_return_val_if_fail (QofDateInit, NULL);
554 g_return_val_if_fail (date, NULL);
555 g_return_val_if_fail (date->qd_valid, NULL);
556 d = g_hash_table_lookup (DateFormatTable,
557 GINT_TO_POINTER (df));
558 g_return_val_if_fail (d, NULL);
559 temp[0] = '\1';
560 result = strftime_case (FALSE, temp, MAX_DATE_BUFFER,
561 d->format, date, 1, date->qd_nanosecs);
562 if (result == 0 && temp[0] != '\0')
563 {
564 PERR (" qof extended strftime failed");
565 return NULL;
566 }
567 return g_strndup(temp, result);
568 }
569
570 /* QofDate handlers */
571
572 QofDate *
573 qof_date_new (void)
574 {
575 QofDate *d;
576
577 d = g_new0 (QofDate, 1);
578 return d;
579 }
580
581 QofDate *
582 qof_date_get_current (void)
583 {
584 QofTime *qt;
585 QofDate *qd;
586
587 qt = qof_time_get_current ();
588 qd = qof_date_from_qtime (qt);
589 qof_time_free (qt);
590 return qd;
591 }
592
593 QofDate *
594 qof_date_new_dmy (gint day, gint month, gint64 year)
595 {
596 QofDate *qd;
597
598 qd = g_new0 (QofDate, 1);
599 qd->qd_mday = day;
600 qd->qd_mon = month;
601 qd->qd_year = year;
602 if(!qof_date_valid (qd))
603 return NULL;
604 return qd;
605 }
606
607 void
608 qof_date_free (QofDate * date)
609 {
610 g_return_if_fail (date);
611 g_free (date);
612 date = NULL;
613 }
614
615 gboolean
616 qof_date_valid (QofDate *date)
617 {
618 g_return_val_if_fail (date, FALSE);
619 date = date_normalise (date);
620 if (date->qd_valid == FALSE)
621 {
622 PERR (" unknown QofDate error");
623 return FALSE;
624 }
625 return TRUE;
626 }
627
628 gboolean
629 qof_date_equal (const QofDate *d1, const QofDate *d2)
630 {
631 if (0 == qof_date_compare (d1, d2))
632 return TRUE;
633 return FALSE;
634 }
635
636 gint
637 qof_date_compare (const QofDate * d1, const QofDate * d2)
638 {
639 if ((!d1) && (!d2))
640 return 0;
641 if (d1 == d2)
642 return 0;
643 if (!d1)
644 return -1;
645 if (!d2)
646 return 1;
647 if (d1->qd_year < d2->qd_year)
648 return -1;
649 if (d1->qd_year > d2->qd_year)
650 return 1;
651 if (d1->qd_mon < d2->qd_mon)
652 return -1;
653 if (d1->qd_mon > d2->qd_mon)
654 return 1;
655 if (d1->qd_mday < d2->qd_mday)
656 return -1;
657 if (d1->qd_mday > d2->qd_mday)
658 return 1;
659 if (d1->qd_hour < d2->qd_hour)
660 return -1;
661 if (d1->qd_hour > d2->qd_hour)
662 return 1;
663 if (d1->qd_min < d2->qd_min)
664 return -1;
665 if (d1->qd_min > d2->qd_min)
666 return 1;
667 if (d1->qd_sec < d2->qd_sec)
668 return -1;
669 if (d1->qd_sec > d2->qd_sec)
670 return 1;
671 if (d1->qd_nanosecs < d2->qd_nanosecs)
672 return -1;
673 if (d1->qd_nanosecs > d2->qd_nanosecs)
674 return 1;
675 return 0;
676 }
677
678 QofDate *
679 qof_date_from_struct_tm (const struct tm *stm)
680 {
681 QofDate *d;
682
683 g_return_val_if_fail (stm, NULL);
684 d = g_new0 (QofDate, 1);
685 d->qd_sec = stm->tm_sec;
686 d->qd_min = stm->tm_min;
687 d->qd_hour = stm->tm_hour;
688 d->qd_mday = stm->tm_mday;
689 d->qd_mon = stm->tm_mon + 1;
690 d->qd_year = stm->tm_year + 1900;
691 d->qd_wday = stm->tm_wday;
692 d->qd_yday = stm->tm_yday;
693 d->qd_is_dst = stm->tm_isdst;
694 d->qd_gmt_off = stm->tm_gmtoff;
695 d->qd_zone = stm->tm_zone;
696 d->qd_valid = TRUE;
697 d = date_normalise(d);
698 return d;
699 }
700
701 gboolean
702 qof_date_to_struct_tm (const QofDate * qd, struct tm * stm,
703 glong *nanosecs)
704 {
705 g_return_val_if_fail (qd, FALSE);
706 g_return_val_if_fail (stm, FALSE);
707 g_return_val_if_fail (qd->qd_valid, FALSE);
708 if ((qd->qd_year > G_MAXINT) || (qd->qd_year < 1900))
709 {
710 PERR (" date too large for struct tm");
711 return FALSE;
712 }
713 stm->tm_sec = qd->qd_sec;
714 stm->tm_min = qd->qd_min;
715 stm->tm_hour = qd->qd_hour;
716 stm->tm_mday = qd->qd_mday;
717 stm->tm_mon = qd->qd_mon - 1;
718 stm->tm_year = qd->qd_year - 1900;
719 stm->tm_wday = qd->qd_wday;
720 stm->tm_yday = qd->qd_yday;
721 stm->tm_isdst = qd->qd_is_dst;
722 stm->tm_gmtoff = qd->qd_gmt_off;
723 stm->tm_zone = qd->qd_zone;
724 if (nanosecs != NULL)
725 *nanosecs = qd->qd_nanosecs;
726 return TRUE;
727 }
728
729 gboolean
730 qof_date_to_gdate (const QofDate *qd, GDate *gd)
731 {
732 g_return_val_if_fail (qd, FALSE);
733 g_return_val_if_fail (gd, FALSE);
734 g_return_val_if_fail (qd->qd_valid, FALSE);
735 if (qd->qd_year >= G_MAXUINT16)
736 {
737 PERR (" QofDate out of range of GDate");
738 return FALSE;
739 }
740 if (!g_date_valid_dmy (qd->qd_mday, qd->qd_mon, qd->qd_year))
741 {
742 PERR (" GDate failed to allow day, month and/or year");
743 return FALSE;
744 }
745 g_date_set_dmy (gd, qd->qd_mday, qd->qd_mon, qd->qd_year);
746 return TRUE;
747 }
748
749 QofDate *
750 qof_date_from_gdate (const GDate *date)
751 {
752 QofDate * qd;
753
754 g_return_val_if_fail (g_date_valid (date), NULL);
755 qd = qof_date_new ();
756 qd->qd_year = g_date_get_year (date);
757 qd->qd_mon = g_date_get_month (date);
758 qd->qd_mday = g_date_get_day (date);
759 qd = date_normalise (qd);
760 return qd;
761 }
762
763 static void
764 qof_date_offset (const QofTime *time, glong offset, QofDate *qd)
765 {
766 glong days;
767 gint64 rem, y, yg;
768 const guint16 *ip;
769 QofTimeSecs t;
770
771 g_return_if_fail (qd);
772 g_return_if_fail (time);
773 t = qof_time_get_secs ((QofTime*)time);
774 days = t / SECS_PER_DAY;
775 rem = t % SECS_PER_DAY;
776 rem += offset;
777 while (rem < 0)
778 {
779 rem += SECS_PER_DAY;
780 --days;
781 }
782 while (rem >= SECS_PER_DAY)
783 {
784 rem -= SECS_PER_DAY;
785 ++days;
786 }
787 qd->qd_hour = rem / SECS_PER_HOUR;
788 rem %= SECS_PER_HOUR;
789 qd->qd_min = rem / 60;
790 qd->qd_sec = rem % 60;
791 /* January 1, 1970 was a Thursday. */
792 qd->qd_wday = (4 + days) % 7;
793 if (qd->qd_wday < 0)
794 qd->qd_wday += 7;
795 y = 1970;
796 while (days < 0 || days >= (qof_date_isleap(y) ? 366 : 365))
797 {
798 /* Guess a corrected year, assuming 365 days per year. */
799 yg = y + days / 365 - (days % 365 < 0);
800 /* Adjust DAYS and Y to match the guessed year. */
801 days -= ((yg - y) * 365
802 + LEAPS_THRU_END_OF (yg - 1)
803 - LEAPS_THRU_END_OF (y - 1));
804 y = yg;
805 }
806 qd->qd_year = y;
807 qd->qd_yday = days;
808 ip = days_in_year[qof_date_isleap(y)];
809 for (y = 12; days < (glong) ip[y]; --y)
810 continue;
811 days -= ip[y];
812 qd->qd_mon = y;
813 qd->qd_mday = days + 1;
814 }
815
816 /* safe to use time_t here because only values
817 within the range of a time_t have any leapseconds. */
818 static gint
819 count_leapseconds (time_t interval)
820 {
821 time_t altered;
822 struct tm utc;
823
824 altered = interval;
825 utc = *gmtime_r (&interval, &utc);
826 altered = mktime (&utc);
827 return altered - interval;
828 }
829
830 /*static inline gint*/
831 static gint
832 extract_interval (const QofTime *qt)
833 {
834 gint leap_seconds;
835 QofTimeSecs t, l;
836 const QofTime *now;
837
838 leap_seconds = 0;
839 t = qof_time_get_secs (qt);
840 now = qof_time_get_current ();
841 l = (qof_time_get_secs (now) > G_MAXINT32) ?
842 G_MAXINT32 : qof_time_get_secs (now);
843 leap_seconds = ((t > l) || (t < 0)) ?
844 count_leapseconds (l) :
845 count_leapseconds (t);
846 return leap_seconds;
847 }
848
849 QofDate *
850 qof_date_from_qtime (const QofTime *qt)
851 {
852 QofDate *qd;
853 gint leap_extra_secs;
854
855 /* may not want to create a new time or date - it
856 complicates memory management. */
857 g_return_val_if_fail (qt, NULL);
858 g_return_val_if_fail (qof_time_is_valid (qt), NULL);
859 qd = qof_date_new ();
860 leap_extra_secs = 0;
861 setenv ("TZ", "GMT", 1);
862 tzset();
863 leap_extra_secs = extract_interval (qt);
864 qof_date_offset (qt, leap_extra_secs, qd);
865 qd->qd_nanosecs = qof_time_get_nanosecs (qt);
866 qd->qd_is_dst = 0;
867 qd->qd_zone = "GMT";
868 qd->qd_gmt_off = 0L;
869 if (!qof_date_valid(qd))
870 return NULL;
871 return qd;
872 }
873
874 gint64
875 days_between (gint64 year1, gint64 year2)
876 {
877 gint64 i, start, end, l;
878
879 l = 0;
880 if (year1 == year2)
881 return l;
882 start = (year1 < year2) ? year1 : year2;
883 end = (year2 < year1) ? year1: year2;
884 for (i = start; i < end; i++)
885 {
886 l += (qof_date_isleap(i)) ? 366 : 365;
887 }
888 return l;
889 }
890
891 QofTime*
892 qof_date_to_qtime (const QofDate *qd)
893 {
894 QofTime *qt;
895 QofTimeSecs c;
896
897 g_return_val_if_fail (qd, NULL);
898 g_return_val_if_fail (qd->qd_valid, NULL);
899 c = 0;
900 qt = NULL;
901 if (qd->qd_year < 1970)
902 {
903 c = qd->qd_sec;
904 c += QOF_MIN_TO_SEC(qd->qd_min);
905 c += QOF_HOUR_TO_SEC(qd->qd_hour);
906 c += QOF_DAYS_TO_SEC(qd->qd_yday);
907 c -= QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year));
908 c -= qd->qd_gmt_off;
909 qt = qof_time_set (c, qd->qd_nanosecs);
910 }
911 if (qd->qd_year >= 1970)
912 {
913 c = qd->qd_sec;
914 c += QOF_MIN_TO_SEC(qd->qd_min);
915 c += QOF_HOUR_TO_SEC(qd->qd_hour);
916 c += QOF_DAYS_TO_SEC(qd->qd_yday);
917 c += QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year));
918 c -= qd->qd_gmt_off;
919 qt = qof_time_set (c, qd->qd_nanosecs);
920 }
921 return qt;
922 }
923
924 QofTime *
925 qof_date_time_difference (const QofDate * date1,
926 const QofDate * date2)
927 {
928 gint64 days;
929 QofTime *secs;
930
931 secs = qof_time_new ();
932 days = days_between (date1->qd_year, date2->qd_year);
933 qof_time_add_secs(secs, QOF_DAYS_TO_SEC(days));
934 if (days >= 0)
935 {
936 /* positive value, add date2 secs, subtract date1 */
937 qof_time_add_secs(secs, -1 *
938 (QOF_HOUR_TO_SEC(date1->qd_hour) -
939 QOF_MIN_TO_SEC(date1->qd_min) -
940 (date1->qd_sec)));
941 qof_time_add_secs(secs,
942 QOF_HOUR_TO_SEC(date2->qd_hour) +
943 QOF_MIN_TO_SEC(date2->qd_min) +
944 (date2->qd_sec));
945 qof_time_set_nanosecs(secs,
946 (date1->qd_nanosecs - date2->qd_nanosecs));
947 }
948 if (days < 0)
949 {
950 /* negative value*/
951 qof_time_add_secs (secs,
952 QOF_HOUR_TO_SEC(date1->qd_hour) -
953 QOF_MIN_TO_SEC(date1->qd_min) -
954 (date1->qd_sec));
955 qof_time_add_secs (secs, -1 *
956 (QOF_HOUR_TO_SEC(date2->qd_hour) +
957 QOF_MIN_TO_SEC(date2->qd_min) +
958 (date2->qd_sec)));
959 qof_time_set_nanosecs(secs,
960 (date2->qd_nanosecs - date1->qd_nanosecs));
961 }
962 return secs;
963 }
964
965 gboolean
966 qof_date_adddays (QofDate * qd, gint days)
967 {
968 g_return_val_if_fail (qd, FALSE);
969 g_return_val_if_fail (qof_date_valid (qd), FALSE);
970 qd->qd_mday += days;
971 return qof_date_valid (qd);
972 }
973
974 gboolean
975 qof_date_addmonths (QofDate * qd, gint months,
976 gboolean track_last_day)
977 {
978 g_return_val_if_fail (qd, FALSE);
979 g_return_val_if_fail (qof_date_valid (qd), FALSE);
980 qd->qd_mon += months % 12;
981 qd->qd_year += months / 12;
982 g_return_val_if_fail (qof_date_valid (qd), FALSE);
983 if (track_last_day && qof_date_is_last_mday (qd))
984 {
985 qd->qd_mday = qof_date_get_mday (qd->qd_mon,
986 qd->qd_year);
987 }
988 return TRUE;
989 }
990
991 inline gboolean
992 qof_date_set_day_end (QofDate * qd)
993 {
994 qd->qd_hour = 23;
995 qd->qd_min = 59;
996 qd->qd_sec = 59;
997 qd->qd_nanosecs = (QOF_NSECS - 1);
998 return qof_date_valid (qd);
999 }
1000
1001 inline gboolean
1002 qof_date_set_day_start (QofDate * qd)
1003 {
1004 g_return_val_if_fail (qd, FALSE);
1005 qd->qd_hour = 0;
1006 qd->qd_min = 0;
1007 qd->qd_sec = 0;
1008 qd->qd_nanosecs = G_GINT64_CONSTANT(0);
1009 return qof_date_valid (qd);
1010 }
1011
1012 inline gboolean
1013 qof_date_set_day_middle (QofDate * qd)
1014 {
1015 g_return_val_if_fail (qd, FALSE);
1016 qd->qd_hour = 12;
1017 qd->qd_min = 0;
1018 qd->qd_sec = 0;
1019 qd->qd_nanosecs = G_GINT64_CONSTANT(0);
1020 return qof_date_valid (qd);
1021 }
1022
1023 /******************** END OF FILE *************************/
QofDate_s::qd_yday
gshort qd_yday
Definition: qofdate.h:189
QofDate_s::qd_year
gint64 qd_year
Extended version to cope with full range of dates.
Definition: qofdate.h:181
MAX_DATE_BUFFER
#define MAX_DATE_BUFFER
The maximum length of a QofDate buffer.
Definition: qofdate.h:92
QOF_DATE_FORMAT_UTC
#define QOF_DATE_FORMAT_UTC
QOF UTC format, xsd:date compatible. QOF_UTC_DATE_FORMAT "%Y-%m-%dT%H:%M:%SZ".
Definition: qofdate.h:277
PERR
#define PERR(format, args...)
Definition: qoflog.h:183
qof_date_format_set_current
gboolean qof_date_format_set_current(QofDateFormat df)
Selects one registered date format as the current default.
Definition: qofdate.c:294
QOF_DATE_FORMAT_ISO
#define QOF_DATE_FORMAT_ISO
Short ISO form. "%F".
Definition: qofdate.h:268
QofDate_s::qd_valid
gboolean qd_valid
If the QofDate is valid or merely initialised.
Definition: qofdate.h:217
QOF_NSECS
#define QOF_NSECS
Definition: qoftime.h:105
qof_date_adddays
gboolean qof_date_adddays(QofDate *qd, gint days)
Add a number of days to a QofDate and normalise.
Definition: qofdate.c:966
qof_date_free
void qof_date_free(QofDate *date)
Definition: qofdate.c:608
QOF_UTC_DATE_FORMAT
#define QOF_UTC_DATE_FORMAT
UTC date format string.
Definition: qofdate.h:244
qof_date_from_qtime
QofDate * qof_date_from_qtime(const QofTime *qt)
Definition: qofdate.c:850
qof_date_format_set_name
gboolean qof_date_format_set_name(const gchar *name, QofDateFormat format)
Set a shorthand name for a custom date format.
Definition: qofdate.c:269
qof_date_format_from_name
QofDateFormat qof_date_format_from_name(const gchar *name)
Returns the default date format for a known shorthand name.
Definition: qofdate.c:385
SECS_PER_HOUR
#define SECS_PER_HOUR
Definition: qofdate.h:96
QofDate_s::qd_zone
const gchar * qd_zone
Calculated value based on struct tm.tm_zone.
Definition: qofdate.h:208
qof_date_get_current
QofDate * qof_date_get_current(void)
Definition: qofdate.c:582
qof_date_get_mday
guint8 qof_date_get_mday(gint month, gint64 year)
Definition: qofdate.c:183
qof_date_close
void qof_date_close(void)
close down the QofDate tables
Definition: qofdate.c:156
qof_time_get_secs
QofTimeSecs qof_time_get_secs(const QofTime *qt)
Get the number of seconds.
Definition: qoftime.c:133
qof_date_print
gchar * qof_date_print(const QofDate *date, QofDateFormat df)
Convert a QofDate to a timestamp according to the specified date format.
Definition: qofdate.c:547
qof_date_format_set_date_separator
gboolean qof_date_format_set_date_separator(const gchar sep, QofDateFormat df)
Set a locale-specific separator.
Definition: qofdate.c:340
QOF_MOD_DATE
#define QOF_MOD_DATE
Definition: qofdate.h:98
qof_time_new
QofTime * qof_time_new(void)
create an empty QofTime
Definition: qoftime.c:46
qof_date_format_get_current
QofDateFormat qof_date_format_get_current(void)
returns the current date format.
Definition: qofdate.c:288
QofDate_s::qd_is_dst
gshort qd_is_dst
Definition: qofdate.h:193
qof_time_get_nanosecs
glong qof_time_get_nanosecs(const QofTime *qt)
Get the number of seconds.
Definition: qoftime.c:141
qof_date_format_get_format
const gchar * qof_date_format_get_format(QofDateFormat df)
Retrieve the strftime format string for a registered date format.
Definition: qofdate.c:310
LEAVE
#define LEAVE(format, args...)
Definition: qoflog.h:227
qof_date_to_qtime
QofTime * qof_date_to_qtime(const QofDate *qd)
Definition: qofdate.c:892
QofDate_s
Full range replacement for struct tm.
Definition: qofdate.h:138
qof_date_format_get_date_separator
gchar qof_date_format_get_date_separator(QofDateFormat df)
Return the field separator for the current date format.
Definition: qofdate.c:325
qof_time_get_current
QofTime * qof_time_get_current(void)
Get the current QofTime.
Definition: qoftime.c:362
qof_date_init
void qof_date_init(void)
initialise the QofDate tables
Definition: qofdate.c:67
qof_date_parse
QofDate * qof_date_parse(const gchar *str, QofDateFormat df)
Convert a timestamp to a QofTime.
Definition: qofdate.c:525
QOF_DATE_FORMAT_CE
#define QOF_DATE_FORMAT_CE
Contintental European default. "%d.%m.%Y".
Definition: qofdate.h:263
QofDate_s::qd_hour
glong qd_hour
Signed replacement of struct tm.tm_hour.
Definition: qofdate.h:157
qof_time_free
void qof_time_free(QofTime *qt)
Free a QofTime when no longer required.
Definition: qoftime.c:56
QofDate_s::qd_min
glong qd_min
Signed replacement of struct tm.tm_min.
Definition: qofdate.h:150
QofDate_s::qd_mon
glong qd_mon
Signed replacement of struct tm.tm_mon.
Definition: qofdate.h:171
qof_date_get_yday
guint16 qof_date_get_yday(gint mday, gint month, gint64 year)
Definition: qofdate.c:167
qof_time_add_secs
void qof_time_add_secs(QofTime *qt, QofTimeSecs secs)
Add (or subtract) seconds from a QofTime.
Definition: qoftime.c:65
DATE_FORMAT_LAST
#define DATE_FORMAT_LAST
Definition: qofdate.h:322
DEBUG
#define DEBUG(format, args...)
Definition: qoflog.h:208
qof_date_isleap
#define qof_date_isleap(year)
Definition: qofdate.h:222
qof_date_to_struct_tm
gboolean qof_date_to_struct_tm(const QofDate *qd, struct tm *stm, glong *nanosecs)
Convert a QofDate to a struct tm.
Definition: qofdate.c:702
QOF_HOUR_TO_SEC
#define QOF_HOUR_TO_SEC(x)
Definition: qofdate.h:327
qof_date_new_dmy
QofDate * qof_date_new_dmy(gint day, gint month, gint64 year)
Definition: qofdate.c:594
QOF_DATE_FORMAT_ISO8601
#define QOF_DATE_FORMAT_ISO8601
Definition: qofdate.h:287
QOF_DATE_FORMAT_UK
#define QOF_DATE_FORMAT_UK
United Kingdom default. "%d/%m/%Y".
Definition: qofdate.h:258
QOF_DATE_FORMAT_LOCALE
#define QOF_DATE_FORMAT_LOCALE
GNU locale default. "%x".
Definition: qofdate.h:303
qof_time_set_nanosecs
void qof_time_set_nanosecs(QofTime *qt, glong nano)
Set the number of seconds.
Definition: qoftime.c:125
QofDate_s::qd_sec
gint64 qd_sec
Definition: qofdate.h:143
MAX_DATE_LENGTH
#define MAX_DATE_LENGTH
The maximum length of a string used for or created by dates.
Definition: qofdate.h:87
QofDateFormat
gint QofDateFormat
Definition: qofdate.h:335
QofDate_s::qd_gmt_off
glong qd_gmt_off
Calculated value based on struct tm.tm_gmtoff.
Definition: qofdate.h:201
qof_time_set
QofTime * qof_time_set(QofTimeSecs t, glong nanosecs)
Definition: qoftime.c:210
qof_date_equal
gboolean qof_date_equal(const QofDate *d1, const QofDate *d2)
Definition: qofdate.c:629
qof_date_addmonths
gboolean qof_date_addmonths(QofDate *qd, gint months, gboolean track_last_day)
Definition: qofdate.c:975
qof_date_to_gdate
gboolean qof_date_to_gdate(const QofDate *qd, GDate *gd)
Convert a QofDate to a GDate.
Definition: qofdate.c:730
qof_date_format_to_name
const gchar * qof_date_format_to_name(QofDateFormat format)
Retrieve the shorthand name for the selected date format.
Definition: qofdate.c:254
QofDate_s::qd_nanosecs
glong qd_nanosecs
Definition: qofdate.h:141
SECS_PER_DAY
#define SECS_PER_DAY
Definition: qofdate.h:94
QofTimeSecs
gint64 QofTimeSecs
Replacement for time_t.
Definition: qoftime.h:129
QOF_DATE_FORMAT_CUSTOM
#define QOF_DATE_FORMAT_CUSTOM
Date and time for the current locale "%c".
Definition: qofdate.h:319
QofTime
struct QofTime64 QofTime
Use a 64-bit signed int QofTime.
Definition: qoftime.h:120
QOF_MIN_TO_SEC
#define QOF_MIN_TO_SEC(x)
Definition: qofdate.h:329
qof_date_new
QofDate * qof_date_new(void)
Definition: qofdate.c:573
qof_date_time_difference
QofTime * qof_date_time_difference(const QofDate *date1, const QofDate *date2)
Definition: qofdate.c:925
qof_date_compare
gint qof_date_compare(const QofDate *d1, const QofDate *d2)
Definition: qofdate.c:637
QofDate_s::qd_wday
gshort qd_wday
Definition: qofdate.h:185
safe_strcmp
gint safe_strcmp(const gchar *da, const gchar *db)
Definition: qofutil.c:72
qof_date_is_last_mday
gboolean qof_date_is_last_mday(const QofDate *qd)
Definition: qofdate.c:193
QOF_DAYS_TO_SEC
#define QOF_DAYS_TO_SEC(x)
Definition: qofdate.h:331
QofDate_s::qd_mday
glong qd_mday
Signed replacement of struct tm.tm_mday.
Definition: qofdate.h:164
qof_date_from_gdate
QofDate * qof_date_from_gdate(const GDate *date)
Create a QofDate from a GDate.
Definition: qofdate.c:750
ENTER
#define ENTER(format, args...)
Definition: qoflog.h:217
qof_date_format_add
gboolean qof_date_format_add(const gchar *str, QofDateFormat *identifier)
Add a specific strftime compatible string as a new QofDateFormat.
Definition: qofdate.c:202
QOF_DATE_FORMAT_US
#define QOF_DATE_FORMAT_US
Continental US default. "%m/%d/%Y".
Definition: qofdate.h:253
qof_date_valid
gboolean qof_date_valid(QofDate *date)
Validate a QofDate.
Definition: qofdate.c:616
QofLogModule
const gchar * QofLogModule
Definition: qofid.h:85
qof_date_from_struct_tm
QofDate * qof_date_from_struct_tm(const struct tm *stm)
Convert a struct tm to a QofDate.
Definition: qofdate.c:679