QOF  0.8.7
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
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
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
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 *
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 
289 {
290  return dateFormat;
291 }
292 
293 gboolean
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 *
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
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
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 
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, leap;
415 
416  g_return_val_if_fail (date, NULL);
417 
418  date->qd_sec -= date->qd_gmt_off;
419  /* if value is negative, just add */
420  if ((date->qd_nanosecs >= QOF_NSECS) ||
421  (date->qd_nanosecs <= -QOF_NSECS))
422  {
423  date->qd_sec += date->qd_nanosecs / QOF_NSECS;
424  date->qd_nanosecs = date->qd_nanosecs % QOF_NSECS;
425  if (date->qd_nanosecs < 0)
426  {
427  date->qd_nanosecs += QOF_NSECS;
428  date->qd_sec--;
429  }
430  }
431  if ((date->qd_sec >= 60) || (date->qd_sec <= -60))
432  {
433  date->qd_min += date->qd_sec / 60;
434  date->qd_sec = date->qd_sec % 60;
435  if (date->qd_sec < 0)
436  {
437  date->qd_sec += 60;
438  date->qd_min--;
439  }
440  }
441  if ((date->qd_min >= 60) || (date->qd_min <= -60))
442  {
443  date->qd_hour += date->qd_min / 60;
444  date->qd_min = date->qd_min % 60;
445  if (date->qd_min < 0)
446  {
447  date->qd_min += 60;
448  date->qd_hour--;
449  }
450  }
451  /* Year Zero does not exist, 1BC is immediately followed by 1AD. */
452  if (date->qd_year == 0)
453  date->qd_year = -1;
454  /* qd_mon starts at 1, not zero */
455  if (date->qd_mon == 0)
456  date->qd_mon = 1;
457  /* qd_mday starts at 1, not zero */
458  if (date->qd_mday == 0)
459  date->qd_mday = 1;
460  if ((date->qd_hour >= 24) || (date->qd_hour <= -24))
461  {
462  date->qd_mday += date->qd_hour / 24;
463  date->qd_hour = date->qd_hour % 24;
464  if (date->qd_hour < 0)
465  {
466  date->qd_hour += 24;
467  date->qd_mday--;
468  }
469  }
470  /* yes, [13] is correct == total at end of month_12 */
471  leap = days_in_year[qof_date_isleap(date->qd_year)][13];
472  while (date->qd_mday > leap)
473  {
474  date->qd_year++;
475  leap = days_in_year[qof_date_isleap(date->qd_year)][13];
476  date->qd_mday -= leap;
477  }
478  while (date->qd_mday < (leap*-1))
479  {
480  date->qd_year--;
481  leap = days_in_year[qof_date_isleap(date->qd_year)][13];
482  date->qd_mday += leap;
483  }
484  if ((date->qd_mon > 12) || (date->qd_mon < -12))
485  {
486  gint leap = days_in_year[qof_date_isleap(date->qd_year)][13];
487  date->qd_year += date->qd_mon / 12;
488  date->qd_mon = date->qd_mon % 12;
489  if (date->qd_mday > leap)
490  date->qd_mday -= leap;
491  if (date->qd_mday < (leap*-1))
492  date->qd_mday += leap;
493  if (date->qd_mon < 0)
494  {
495  /* -1 == Dec, -4 == Sep */
496  date->qd_mon += 12 + 1;
497  if (date->qd_year < 0)
498  {
499  date->qd_year++;
500  }
501  else
502  {
503  date->qd_year--;
504  }
505  /*date->qd_year = (date->qd_year < 0) ?
506  date->qd_year++ : date->qd_year--;*/
507  }
508  }
509  days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon];
510  while (date->qd_mday < 0)
511  {
512  date->qd_mday += days;
513  date->qd_mon--;
514  if (date->qd_mon < 1)
515  {
516  date->qd_year -= date->qd_mon / 12;
517  date->qd_mon = date->qd_mon % 12;
518  /* if year was AD and is now zero, reset to BC. */
519  if ((date->qd_year == 0) && (date->qd_mon < 0))
520  date->qd_year = -1;
521  }
522  days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon];
523  }
524  while (date->qd_mday > days)
525  {
526  date->qd_mday -= days;
527  date->qd_mon++;
528  if (date->qd_mon > 11)
529  {
530  date->qd_year += date->qd_mon / 12;
531  date->qd_mon = date->qd_mon % 12;
532  /* if year was BC and is now zero, reset to AD. */
533  if ((date->qd_year == 0) && (date->qd_mon > 0))
534  date->qd_year = +1;
535  }
536  days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon];
537  }
538  /* use sensible defaults */
539  if (date->qd_mday == 0)
540  date->qd_mday = 1;
541  if (date->qd_mon == 0)
542  date->qd_mon = 1;
543  /* use days_in_year to set yday */
544  date->qd_yday = (date->qd_mday - 1) +
545  days_in_year[qof_date_isleap(date->qd_year)][date->qd_mon];
546  set_day_of_the_week (date);
547 
548  /* qd_year has no realistic limits */
549  date->qd_valid = TRUE;
550  date->qd_zone = "GMT";
551  date->qd_is_dst = 0;
552  date->qd_gmt_off = 0L;
553  return date;
554 }
555 
556 QofDate *
557 qof_date_parse (const gchar * str, QofDateFormat df)
558 {
559  const gchar *format;
560  QofDateError error;
561  QofDate *date;
562  gchar * G_GNUC_UNUSED check;
563 
564  check = NULL;
565  error = ERR_NO_ERROR;
566  date = qof_date_new ();
567  format = qof_date_format_get_format (df);
568  check = strptime_internal (str, format, date, &error);
569  check = NULL;
570  if (error != ERR_NO_ERROR)
571  {
572  qof_date_free (date);
573  return NULL;
574  }
575 
576  date = date_normalise (date);
577  return date;
578 }
579 
580 gchar *
582 {
583  size_t result;
584  gchar temp[MAX_DATE_BUFFER];
585  QofDateEntry *d;
586 
587  g_return_val_if_fail (QofDateInit, NULL);
588  g_return_val_if_fail (date, NULL);
589  g_return_val_if_fail (date->qd_valid, NULL);
590  d = g_hash_table_lookup (DateFormatTable,
591  GINT_TO_POINTER (df));
592  g_return_val_if_fail (d, NULL);
593  temp[0] = '\1';
594  result = strftime_case (FALSE, temp, MAX_DATE_BUFFER,
595  d->format, date, 1, date->qd_nanosecs);
596  if (result == 0 && temp[0] != '\0')
597  {
598  PERR (" qof extended strftime failed");
599  return NULL;
600  }
601  return g_strndup(temp, result);
602 }
603 
604 /* QofDate handlers */
605 
606 QofDate *
608 {
609  QofDate *d;
610 
611  d = g_new0 (QofDate, 1);
612  return d;
613 }
614 
615 QofDate *
617 {
618  QofTime *qt;
619  QofDate *qd;
620 
621  qt = qof_time_get_current ();
622  qd = qof_date_from_qtime (qt);
623  qof_time_free (qt);
624  return qd;
625 }
626 
627 QofDate *
628 qof_date_new_dmy (gint day, gint month, gint64 year)
629 {
630  QofDate *qd;
631 
632  qd = g_new0 (QofDate, 1);
633  qd->qd_mday = day;
634  qd->qd_mon = month;
635  qd->qd_year = year;
636  if(!qof_date_valid (qd))
637  return NULL;
638  return qd;
639 }
640 
641 void
643 {
644  g_return_if_fail (date);
645  g_free (date);
646  date = NULL;
647 }
648 
649 gboolean
651 {
652  g_return_val_if_fail (date, FALSE);
653  date = date_normalise (date);
654  if (date->qd_valid == FALSE)
655  {
656  PERR (" unknown QofDate error");
657  return FALSE;
658  }
659  return TRUE;
660 }
661 
662 gboolean
663 qof_date_equal (const QofDate *d1, const QofDate *d2)
664 {
665  if (0 == qof_date_compare (d1, d2))
666  return TRUE;
667  return FALSE;
668 }
669 
670 gint
671 qof_date_compare (const QofDate * d1, const QofDate * d2)
672 {
673  if ((!d1) && (!d2))
674  return 0;
675  if (d1 == d2)
676  return 0;
677  if (!d1)
678  return -1;
679  if (!d2)
680  return 1;
681  if (d1->qd_year < d2->qd_year)
682  return -1;
683  if (d1->qd_year > d2->qd_year)
684  return 1;
685  if (d1->qd_mon < d2->qd_mon)
686  return -1;
687  if (d1->qd_mon > d2->qd_mon)
688  return 1;
689  if (d1->qd_mday < d2->qd_mday)
690  return -1;
691  if (d1->qd_mday > d2->qd_mday)
692  return 1;
693  if (d1->qd_hour < d2->qd_hour)
694  return -1;
695  if (d1->qd_hour > d2->qd_hour)
696  return 1;
697  if (d1->qd_min < d2->qd_min)
698  return -1;
699  if (d1->qd_min > d2->qd_min)
700  return 1;
701  if (d1->qd_sec < d2->qd_sec)
702  return -1;
703  if (d1->qd_sec > d2->qd_sec)
704  return 1;
705  if (d1->qd_nanosecs < d2->qd_nanosecs)
706  return -1;
707  if (d1->qd_nanosecs > d2->qd_nanosecs)
708  return 1;
709  return 0;
710 }
711 
712 QofDate *
713 qof_date_from_struct_tm (const struct tm *stm)
714 {
715  QofDate *d;
716 
717  g_return_val_if_fail (stm, NULL);
718  d = g_new0 (QofDate, 1);
719  d->qd_sec = stm->tm_sec;
720  d->qd_min = stm->tm_min;
721  d->qd_hour = stm->tm_hour;
722  d->qd_mday = stm->tm_mday;
723  d->qd_mon = stm->tm_mon + 1;
724  d->qd_year = stm->tm_year + 1900;
725  d->qd_wday = stm->tm_wday;
726  d->qd_yday = stm->tm_yday;
727  d->qd_is_dst = stm->tm_isdst;
728  d->qd_gmt_off = stm->tm_gmtoff;
729  d->qd_zone = stm->tm_zone;
730  d->qd_valid = TRUE;
731  d = date_normalise(d);
732  return d;
733 }
734 
735 gboolean
736 qof_date_to_struct_tm (const QofDate * qd, struct tm * stm,
737  glong *nanosecs)
738 {
739  g_return_val_if_fail (qd, FALSE);
740  g_return_val_if_fail (stm, FALSE);
741  g_return_val_if_fail (qd->qd_valid, FALSE);
742  if ((qd->qd_year > G_MAXINT) || (qd->qd_year < 1900))
743  {
744  PERR (" date too large for struct tm");
745  return FALSE;
746  }
747  stm->tm_sec = qd->qd_sec;
748  stm->tm_min = qd->qd_min;
749  stm->tm_hour = qd->qd_hour;
750  stm->tm_mday = qd->qd_mday;
751  stm->tm_mon = qd->qd_mon - 1;
752  stm->tm_year = qd->qd_year - 1900;
753  stm->tm_wday = qd->qd_wday;
754  stm->tm_yday = qd->qd_yday;
755  stm->tm_isdst = qd->qd_is_dst;
756  stm->tm_gmtoff = qd->qd_gmt_off;
757  stm->tm_zone = qd->qd_zone;
758  if (nanosecs != NULL)
759  *nanosecs = qd->qd_nanosecs;
760  return TRUE;
761 }
762 
763 gboolean
764 qof_date_to_gdate (const QofDate *qd, GDate *gd)
765 {
766  g_return_val_if_fail (qd, FALSE);
767  g_return_val_if_fail (gd, FALSE);
768  g_return_val_if_fail (qd->qd_valid, FALSE);
769  if (qd->qd_year >= G_MAXUINT16)
770  {
771  PERR (" QofDate out of range of GDate");
772  return FALSE;
773  }
774  if (!g_date_valid_dmy (qd->qd_mday, qd->qd_mon, qd->qd_year))
775  {
776  PERR (" GDate failed to allow day, month and/or year");
777  return FALSE;
778  }
779  g_date_set_dmy (gd, qd->qd_mday, qd->qd_mon, qd->qd_year);
780  return TRUE;
781 }
782 
783 QofDate *
784 qof_date_from_gdate (const GDate *date)
785 {
786  QofDate * qd;
787 
788  g_return_val_if_fail (g_date_valid (date), NULL);
789  qd = qof_date_new ();
790  qd->qd_year = g_date_get_year (date);
791  qd->qd_mon = g_date_get_month (date);
792  qd->qd_mday = g_date_get_day (date);
793  qd = date_normalise (qd);
794  return qd;
795 }
796 
797 static void
798 qof_date_offset (const QofTime *time, glong offset, QofDate *qd)
799 {
800  glong days;
801  gint64 rem, y, yg;
802  const guint16 *ip;
803  QofTimeSecs t;
804 
805  g_return_if_fail (qd);
806  g_return_if_fail (time);
807  t = qof_time_get_secs ((QofTime*)time);
808  days = t / SECS_PER_DAY;
809  rem = t % SECS_PER_DAY;
810  rem += offset;
811  while (rem < 0)
812  {
813  rem += SECS_PER_DAY;
814  --days;
815  }
816  while (rem >= SECS_PER_DAY)
817  {
818  rem -= SECS_PER_DAY;
819  ++days;
820  }
821  qd->qd_hour = rem / SECS_PER_HOUR;
822  rem %= SECS_PER_HOUR;
823  qd->qd_min = rem / 60;
824  qd->qd_sec = rem % 60;
825  /* January 1, 1970 was a Thursday. */
826  qd->qd_wday = (4 + days) % 7;
827  if (qd->qd_wday < 0)
828  qd->qd_wday += 7;
829  y = 1970;
830  while (days < 0 || days >= (__isleap (y) ? 366 : 365))
831  {
832  /* Guess a corrected year, assuming 365 days per year. */
833  yg = y + days / 365 - (days % 365 < 0);
834  /* Adjust DAYS and Y to match the guessed year. */
835  days -= ((yg - y) * 365
836  + LEAPS_THRU_END_OF (yg - 1)
837  - LEAPS_THRU_END_OF (y - 1));
838  y = yg;
839  }
840  qd->qd_year = y;
841  qd->qd_yday = days;
842  ip = days_in_year[qof_date_isleap(y)];
843  for (y = 12; days < (glong) ip[y]; --y)
844  continue;
845  days -= ip[y];
846  qd->qd_mon = y;
847  qd->qd_mday = days + 1;
848 }
849 
850 /* safe to use time_t here because only values
851 within the range of a time_t have any leapseconds. */
852 static gint
853 count_leapseconds (time_t interval)
854 {
855  time_t altered;
856  struct tm utc;
857 
858  altered = interval;
859  utc = *gmtime_r (&interval, &utc);
860  altered = mktime (&utc);
861  return altered - interval;
862 }
863 
864 /*static inline gint*/
865 static gint
866 extract_interval (const QofTime *qt)
867 {
868  gint leap_seconds;
869  QofTimeSecs t, l;
870  const QofTime *now;
871 
872  leap_seconds = 0;
873  t = qof_time_get_secs (qt);
874  now = qof_time_get_current ();
875  l = (qof_time_get_secs (now) > G_MAXINT32) ?
876  G_MAXINT32 : qof_time_get_secs (now);
877  leap_seconds = ((t > l) || (t < 0)) ?
878  count_leapseconds (l) :
879  count_leapseconds (t);
880  return leap_seconds;
881 }
882 
883 QofDate *
885 {
886  QofDate *qd;
887  gint leap_extra_secs;
888 
889  /* may not want to create a new time or date - it
890  complicates memory management. */
891  g_return_val_if_fail (qt, NULL);
892  g_return_val_if_fail (qof_time_is_valid (qt), NULL);
893  qd = qof_date_new ();
894  leap_extra_secs = 0;
895  setenv ("TZ", "GMT", 1);
896  tzset();
897  leap_extra_secs = extract_interval (qt);
898  qof_date_offset (qt, leap_extra_secs, qd);
900  qd->qd_is_dst = 0;
901  qd->qd_zone = "GMT";
902  qd->qd_gmt_off = 0L;
903  if (!qof_date_valid(qd))
904  return NULL;
905  return qd;
906 }
907 
908 gint64
909 days_between (gint64 year1, gint64 year2)
910 {
911  gint64 i, start, end, l;
912 
913  l = 0;
914  if (year1 == year2)
915  return l;
916  start = (year1 < year2) ? year1 : year2;
917  end = (year2 < year1) ? year1: year2;
918  for (i = start; i < end; i++)
919  {
920  l += (qof_date_isleap(i)) ? 366 : 365;
921  }
922  return l;
923 }
924 
925 QofTime*
927 {
928  QofTime *qt;
929  QofTimeSecs c;
930 
931  g_return_val_if_fail (qd, NULL);
932  g_return_val_if_fail (qd->qd_valid, NULL);
933  c = 0;
934  qt = NULL;
935  if (qd->qd_year < 1970)
936  {
937  c = qd->qd_sec;
938  c += QOF_MIN_TO_SEC(qd->qd_min);
939  c += QOF_HOUR_TO_SEC(qd->qd_hour);
940  c += QOF_DAYS_TO_SEC(qd->qd_yday);
941  c -= QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year));
942  c -= qd->qd_gmt_off;
943  qt = qof_time_set (c, qd->qd_nanosecs);
944  }
945  if (qd->qd_year >= 1970)
946  {
947  c = qd->qd_sec;
948  c += QOF_MIN_TO_SEC(qd->qd_min);
949  c += QOF_HOUR_TO_SEC(qd->qd_hour);
950  c += QOF_DAYS_TO_SEC(qd->qd_yday);
951  c += QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year));
952  c -= qd->qd_gmt_off;
953  qt = qof_time_set (c, qd->qd_nanosecs);
954  }
955  return qt;
956 }
957 
958 QofTime *
960  const QofDate * date2)
961 {
962  gint64 days;
963  QofTime *secs;
964 
965  secs = qof_time_new ();
966  days = days_between (date1->qd_year, date2->qd_year);
967  qof_time_add_secs(secs, QOF_DAYS_TO_SEC(days));
968  if (days >= 0)
969  {
970  /* positive value, add date2 secs, subtract date1 */
971  qof_time_add_secs(secs, -1 *
972  (QOF_HOUR_TO_SEC(date1->qd_hour) -
973  QOF_MIN_TO_SEC(date1->qd_min) -
974  (date1->qd_sec)));
975  qof_time_add_secs(secs,
976  QOF_HOUR_TO_SEC(date2->qd_hour) +
977  QOF_MIN_TO_SEC(date2->qd_min) +
978  (date2->qd_sec));
979  qof_time_set_nanosecs(secs,
980  (date1->qd_nanosecs - date2->qd_nanosecs));
981  }
982  if (days < 0)
983  {
984  /* negative value*/
985  qof_time_add_secs (secs,
986  QOF_HOUR_TO_SEC(date1->qd_hour) -
987  QOF_MIN_TO_SEC(date1->qd_min) -
988  (date1->qd_sec));
989  qof_time_add_secs (secs, -1 *
990  (QOF_HOUR_TO_SEC(date2->qd_hour) +
991  QOF_MIN_TO_SEC(date2->qd_min) +
992  (date2->qd_sec)));
993  qof_time_set_nanosecs(secs,
994  (date2->qd_nanosecs - date1->qd_nanosecs));
995  }
996  return secs;
997 }
998 
999 gboolean
1000 qof_date_adddays (QofDate * qd, gint days)
1001 {
1002  g_return_val_if_fail (qd, FALSE);
1003  g_return_val_if_fail (qof_date_valid (qd), FALSE);
1004  qd->qd_mday += days;
1005  return qof_date_valid (qd);
1006 }
1007 
1008 gboolean
1009 qof_date_addmonths (QofDate * qd, gint months,
1010  gboolean track_last_day)
1011 {
1012  g_return_val_if_fail (qd, FALSE);
1013  g_return_val_if_fail (qof_date_valid (qd), FALSE);
1014  qd->qd_mon += months % 12;
1015  qd->qd_year += months / 12;
1016  g_return_val_if_fail (qof_date_valid (qd), FALSE);
1017  if (track_last_day && qof_date_is_last_mday (qd))
1018  {
1019  qd->qd_mday = qof_date_get_mday (qd->qd_mon,
1020  qd->qd_year);
1021  }
1022  return TRUE;
1023 }
1024 
1025 inline gboolean
1026 qof_date_set_day_end (QofDate * qd)
1027 {
1028  qd->qd_hour = 23;
1029  qd->qd_min = 59;
1030  qd->qd_sec = 59;
1031  qd->qd_nanosecs = (QOF_NSECS - 1);
1032  return qof_date_valid (qd);
1033 }
1034 
1035 inline gboolean
1036 qof_date_set_day_start (QofDate * qd)
1037 {
1038  g_return_val_if_fail (qd, FALSE);
1039  qd->qd_hour = 0;
1040  qd->qd_min = 0;
1041  qd->qd_sec = 0;
1042  qd->qd_nanosecs = G_GINT64_CONSTANT(0);
1043  return qof_date_valid (qd);
1044 }
1045 
1046 inline gboolean
1047 qof_date_set_day_middle (QofDate * qd)
1048 {
1049  g_return_val_if_fail (qd, FALSE);
1050  qd->qd_hour = 12;
1051  qd->qd_min = 0;
1052  qd->qd_sec = 0;
1053  qd->qd_nanosecs = G_GINT64_CONSTANT(0);
1054  return qof_date_valid (qd);
1055 }
1056 
1057 /******************** END OF FILE *************************/
gshort qd_yday
Definition: qofdate.h:189
gint64 qd_year
Extended version to cope with full range of dates.
Definition: qofdate.h:181
#define MAX_DATE_BUFFER
The maximum length of a QofDate buffer.
Definition: qofdate.h:92
#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
#define PERR(format, args...)
Definition: qoflog.h:183
gboolean qof_date_format_set_current(QofDateFormat df)
Selects one registered date format as the current default.
Definition: qofdate.c:294
#define QOF_DATE_FORMAT_ISO
Short ISO form. "%F".
Definition: qofdate.h:268
gboolean qd_valid
If the QofDate is valid or merely initialised.
Definition: qofdate.h:217
#define QOF_NSECS
Definition: qoftime.h:97
gboolean qof_date_adddays(QofDate *qd, gint days)
Add a number of days to a QofDate and normalise.
Definition: qofdate.c:1000
void qof_date_free(QofDate *date)
Definition: qofdate.c:642
#define QOF_UTC_DATE_FORMAT
UTC date format string.
Definition: qofdate.h:244
QofDate * qof_date_from_qtime(const QofTime *qt)
Definition: qofdate.c:884
gboolean qof_date_format_set_name(const gchar *name, QofDateFormat format)
Set a shorthand name for a custom date format.
Definition: qofdate.c:269
QofDateFormat qof_date_format_from_name(const gchar *name)
Returns the default date format for a known shorthand name.
Definition: qofdate.c:385
#define SECS_PER_HOUR
Definition: qofdate.h:96
const gchar * qd_zone
Calculated value based on struct tm.tm_zone.
Definition: qofdate.h:208
QofDate * qof_date_get_current(void)
Definition: qofdate.c:616
guint8 qof_date_get_mday(gint month, gint64 year)
Definition: qofdate.c:183
void qof_date_close(void)
close down the QofDate tables
Definition: qofdate.c:156
QofTimeSecs qof_time_get_secs(const QofTime *qt)
Get the number of seconds.
Definition: qoftime.c:133
gchar * qof_date_print(const QofDate *date, QofDateFormat df)
Convert a QofDate to a timestamp according to the specified date format.
Definition: qofdate.c:581
gboolean qof_date_format_set_date_separator(const gchar sep, QofDateFormat df)
Set a locale-specific separator.
Definition: qofdate.c:340
#define QOF_MOD_DATE
Definition: qofdate.h:98
QofTime * qof_time_new(void)
create an empty QofTime
Definition: qoftime.c:46
QofDateFormat qof_date_format_get_current(void)
returns the current date format.
Definition: qofdate.c:288
gshort qd_is_dst
Definition: qofdate.h:193
glong qof_time_get_nanosecs(const QofTime *qt)
Get the number of seconds.
Definition: qoftime.c:141
const gchar * qof_date_format_get_format(QofDateFormat df)
Retrieve the strftime format string for a registered date format.
Definition: qofdate.c:310
#define LEAVE(format, args...)
Definition: qoflog.h:227
QofTime * qof_date_to_qtime(const QofDate *qd)
Definition: qofdate.c:926
Full range replacement for struct tm.
Definition: qofdate.h:138
gchar qof_date_format_get_date_separator(QofDateFormat df)
Return the field separator for the current date format.
Definition: qofdate.c:325
QofTime * qof_time_get_current(void)
Get the current QofTime.
Definition: qoftime.c:362
void qof_date_init(void)
initialise the QofDate tables
Definition: qofdate.c:67
QofDate * qof_date_parse(const gchar *str, QofDateFormat df)
Convert a timestamp to a QofTime.
Definition: qofdate.c:557
#define QOF_DATE_FORMAT_CE
Contintental European default. "%d.%m.%Y".
Definition: qofdate.h:263
glong qd_hour
Signed replacement of struct tm.tm_hour.
Definition: qofdate.h:157
void qof_time_free(QofTime *qt)
Free a QofTime when no longer required.
Definition: qoftime.c:56
glong qd_min
Signed replacement of struct tm.tm_min.
Definition: qofdate.h:150
glong qd_mon
Signed replacement of struct tm.tm_mon.
Definition: qofdate.h:171
guint16 qof_date_get_yday(gint mday, gint month, gint64 year)
Definition: qofdate.c:167
void qof_time_add_secs(QofTime *qt, QofTimeSecs secs)
Add (or subtract) seconds from a QofTime.
Definition: qoftime.c:65
#define DATE_FORMAT_LAST
Definition: qofdate.h:322
#define DEBUG(format, args...)
Definition: qoflog.h:208
#define qof_date_isleap(year)
Definition: qofdate.h:222
gboolean qof_date_to_struct_tm(const QofDate *qd, struct tm *stm, glong *nanosecs)
Convert a QofDate to a struct tm.
Definition: qofdate.c:736
#define QOF_HOUR_TO_SEC(x)
Definition: qofdate.h:327
QofDate * qof_date_new_dmy(gint day, gint month, gint64 year)
Definition: qofdate.c:628
#define QOF_DATE_FORMAT_ISO8601
Definition: qofdate.h:287
#define QOF_DATE_FORMAT_UK
United Kingdom default. "%d/%m/%Y".
Definition: qofdate.h:258
#define QOF_DATE_FORMAT_LOCALE
GNU locale default. "%x".
Definition: qofdate.h:303
void qof_time_set_nanosecs(QofTime *qt, glong nano)
Set the number of seconds.
Definition: qoftime.c:125
gint64 qd_sec
Definition: qofdate.h:143
#define MAX_DATE_LENGTH
The maximum length of a string used for or created by dates.
Definition: qofdate.h:87
gint QofDateFormat
Definition: qofdate.h:335
glong qd_gmt_off
Calculated value based on struct tm.tm_gmtoff.
Definition: qofdate.h:201
QofTime * qof_time_set(QofTimeSecs t, glong nanosecs)
Definition: qoftime.c:210
gboolean qof_date_equal(const QofDate *d1, const QofDate *d2)
Definition: qofdate.c:663
gboolean qof_date_addmonths(QofDate *qd, gint months, gboolean track_last_day)
Definition: qofdate.c:1009
gboolean qof_date_to_gdate(const QofDate *qd, GDate *gd)
Convert a QofDate to a GDate.
Definition: qofdate.c:764
const gchar * qof_date_format_to_name(QofDateFormat format)
Retrieve the shorthand name for the selected date format.
Definition: qofdate.c:254
glong qd_nanosecs
Definition: qofdate.h:141
#define SECS_PER_DAY
Definition: qofdate.h:94
gint64 QofTimeSecs
Replacement for time_t.
Definition: qoftime.h:121
#define QOF_DATE_FORMAT_CUSTOM
Date and time for the current locale "%c".
Definition: qofdate.h:319
struct QofTime64 QofTime
Use a 64-bit signed int QofTime.
Definition: qoftime.h:112
#define QOF_MIN_TO_SEC(x)
Definition: qofdate.h:329
QofDate * qof_date_new(void)
Definition: qofdate.c:607
QofTime * qof_date_time_difference(const QofDate *date1, const QofDate *date2)
Definition: qofdate.c:959
gint qof_date_compare(const QofDate *d1, const QofDate *d2)
Definition: qofdate.c:671
gshort qd_wday
Definition: qofdate.h:185
gint safe_strcmp(const gchar *da, const gchar *db)
Definition: qofutil.c:75
gboolean qof_date_is_last_mday(const QofDate *qd)
Definition: qofdate.c:193
#define QOF_DAYS_TO_SEC(x)
Definition: qofdate.h:331
glong qd_mday
Signed replacement of struct tm.tm_mday.
Definition: qofdate.h:164
QofDate * qof_date_from_gdate(const GDate *date)
Create a QofDate from a GDate.
Definition: qofdate.c:784
#define ENTER(format, args...)
Definition: qoflog.h:217
gboolean qof_date_format_add(const gchar *str, QofDateFormat *identifier)
Add a specific strftime compatible string as a new QofDateFormat.
Definition: qofdate.c:202
#define QOF_DATE_FORMAT_US
Continental US default. "%m/%d/%Y".
Definition: qofdate.h:253
gboolean qof_date_valid(QofDate *date)
Validate a QofDate.
Definition: qofdate.c:650
const gchar * QofLogModule
Definition: qofid.h:85
QofDate * qof_date_from_struct_tm(const struct tm *stm)
Convert a struct tm to a QofDate.
Definition: qofdate.c:713