00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "config.h"
00027 #include <glib.h>
00028 #include <glib/gprintf.h>
00029 #include <time.h>
00030 #include "qof.h"
00031 #include "qofdate-p.h"
00032
00033
00034 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
00035 #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
00036
00037 static GHashTable *DateFormatTable = NULL;
00038 static gboolean QofDateInit = FALSE;
00039 static QofLogModule log_module = QOF_MOD_DATE;
00040 static gchar locale_separator = '\0';
00041 static QofDateFormat dateFormat = QOF_DATE_FORMAT_LOCALE;
00042
00043
00044 static const guint16 days_in_year[2][14] =
00045 {
00046 { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
00047 { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
00048 };
00049 static const guint8 days_in_months[2][13] =
00050 {
00051 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
00052 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
00053 };
00054
00055
00056 typedef struct QofDateEntry_s
00057 {
00058 const gchar *format;
00059 const gchar *name;
00060 gchar separator;
00061 QofDateFormat df;
00062 gboolean locale_specific;
00063 } QofDateEntry;
00064
00065 void
00066 qof_date_init (void)
00067 {
00068 if (!QofDateInit)
00069 {
00070 DateFormatTable = g_hash_table_new (g_direct_hash, g_direct_equal);
00071 }
00072 {
00073 QofDateEntry *d = g_new0 (QofDateEntry, 1);
00074 d->format = "%m/%d/%Y";
00075 d->name = "us";
00076 d->separator = '/';
00077 d->df = QOF_DATE_FORMAT_US;
00078 d->locale_specific = FALSE;
00079 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
00080 }
00081 {
00082 QofDateEntry *d = g_new0 (QofDateEntry, 1);
00083 d->format = "%d/%m/%Y";
00084 d->name = "uk";
00085 d->separator = '/';
00086 d->df = QOF_DATE_FORMAT_UK;
00087 d->locale_specific = FALSE;
00088 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
00089 }
00090 {
00091 QofDateEntry *d = g_new0 (QofDateEntry, 1);
00092 d->format = "%d.%m.%Y";
00093 d->name = "ce";
00094 d->separator = '.';
00095 d->df = QOF_DATE_FORMAT_CE;
00096 d->locale_specific = FALSE;
00097 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
00098 }
00099 {
00100 QofDateEntry *d = g_new0 (QofDateEntry, 1);
00101 d->format = "%F";
00102 d->name = "iso";
00103 d->separator = '-';
00104 d->df = QOF_DATE_FORMAT_ISO;
00105 d->locale_specific = FALSE;
00106 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
00107 }
00108 {
00109 QofDateEntry *d = g_new0 (QofDateEntry, 1);
00110 d->format = QOF_UTC_DATE_FORMAT;
00111 d->name = "utc";
00112 d->separator = '-';
00113 d->df = QOF_DATE_FORMAT_UTC;
00114 d->locale_specific = FALSE;
00115 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
00116 }
00117 {
00118 QofDateEntry *d = g_new0 (QofDateEntry, 1);
00119 d->format = "%x";
00120 d->name = "locale";
00121 d->separator = locale_separator;
00122 d->df = QOF_DATE_FORMAT_LOCALE;
00123 d->locale_specific = TRUE;
00124 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
00125 }
00126 {
00127 QofDateEntry *d = g_new0 (QofDateEntry, 1);
00128 d->format = "%c";
00129 d->name = "custom";
00130 d->separator = locale_separator;
00131 d->df = QOF_DATE_FORMAT_CUSTOM;
00132 d->locale_specific = TRUE;
00133 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
00134 }
00135 {
00136 QofDateEntry *d = g_new0(QofDateEntry,1);
00137 d->format = "%Y-%m-%d %H:%M:%S.%N %z";
00138 d->name = "iso8601";
00139 d->separator = '-';
00140 d->df = QOF_DATE_FORMAT_ISO8601;
00141 d->locale_specific = FALSE;
00142 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER(d->df), d);
00143 }
00144 QofDateInit = TRUE;
00145 }
00146
00147 static void
00148 hash_value_free (gpointer key, gpointer value, gpointer data)
00149 {
00150 g_free (value);
00151 }
00152
00153 void
00154 qof_date_close (void)
00155 {
00156 if (QofDateInit)
00157 {
00158 g_hash_table_foreach (DateFormatTable, hash_value_free, NULL);
00159 g_hash_table_destroy (DateFormatTable);
00160 }
00161 QofDateInit = FALSE;
00162 }
00163
00164 guint16
00165 qof_date_get_yday (gint mday, gint month, gint64 year)
00166 {
00167 guint8 leap;
00168
00169 g_return_val_if_fail (mday != 0, 0);
00170 g_return_val_if_fail (month != 0, 0);
00171 g_return_val_if_fail (month <= 12, 0);
00172 g_return_val_if_fail (month >= 1, 0);
00173 g_return_val_if_fail (year != 0, 0);
00174 leap = qof_date_isleap (year);
00175 g_return_val_if_fail (mday <=
00176 qof_date_get_mday (month, year), 0);
00177 return days_in_year[leap][month] + mday;
00178 }
00179
00180 guint8
00181 qof_date_get_mday (gint month, gint64 year)
00182 {
00183 g_return_val_if_fail (month != 0, 0);
00184 g_return_val_if_fail (month <= 12, 0);
00185 g_return_val_if_fail (month >= 1, 0);
00186 g_return_val_if_fail (year != 0, 0);
00187 return days_in_months[qof_date_isleap (year)][month];
00188 }
00189
00190 gboolean
00191 qof_date_is_last_mday (const QofDate *qd)
00192 {
00193 g_return_val_if_fail (qd, FALSE);
00194 g_return_val_if_fail (qd->qd_valid, FALSE);
00195 return (qd->qd_mday ==
00196 qof_date_get_mday (qd->qd_mon, qd->qd_year));
00197 }
00198
00199 gboolean
00200 qof_date_format_add (const gchar * str, QofDateFormat * identifier)
00201 {
00202 struct tm check;
00203 gint len;
00204 time_t now;
00205 gchar test[MAX_DATE_BUFFER];
00206
00208 g_return_val_if_fail (QofDateInit, FALSE);
00209 g_return_val_if_fail (str, FALSE);
00210 g_return_val_if_fail (strlen (str) != 0, FALSE);
00211
00212 ENTER (" str=%s", str);
00213 if (strlen (str) > MAX_DATE_LENGTH)
00214 {
00215 LEAVE (" '%s' is too long! Max=%d str_len=%d",
00216 str, MAX_DATE_LENGTH, (gint) strlen (str));
00217 return FALSE;
00218 }
00219
00220 now = time (NULL);
00221 test[0] = '\1';
00222 check = *gmtime_r (&now, &check);
00223
00224
00225 len = strftime (test, (MAX_DATE_BUFFER - 1), str, &check);
00226 if (len == 0 && test[0] != '\0')
00227 {
00228 LEAVE (" strftime could not understand '%s'", str);
00229 return FALSE;
00230 }
00231 len = strlen (test);
00232 if (len > MAX_DATE_LENGTH)
00233 {
00234 LEAVE (" %s creates a string '%s' that is too long!"
00235 " Max=%d str_len=%d", str, test, MAX_DATE_LENGTH, len);
00236 return FALSE;
00237 }
00238 *identifier = g_hash_table_size (DateFormatTable) + 1;
00239 {
00240 QofDateEntry *d = g_new0 (QofDateEntry, 1);
00241 d->format = str;
00242 d->name = str;
00243 d->separator = locale_separator;
00244 d->df = *identifier;
00245 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
00246 }
00247 LEAVE (" successful");
00248 return TRUE;
00249 }
00250
00251 const gchar *
00252 qof_date_format_to_name (QofDateFormat format)
00253 {
00254 QofDateEntry *d;
00255
00256 g_return_val_if_fail (QofDateInit, NULL);
00257 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (format));
00258 if (!d)
00259 {
00260 PERR (" unknown format: '%d'", format);
00261 return NULL;
00262 }
00263 return d->name;
00264 }
00265
00266 gboolean
00267 qof_date_format_set_name (const gchar * name, QofDateFormat format)
00268 {
00269 QofDateEntry *d;
00270
00271 g_return_val_if_fail (QofDateInit, FALSE);
00272 if (format <= DATE_FORMAT_LAST)
00273 return FALSE;
00274 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (format));
00275 if (!d)
00276 {
00277 PERR (" unknown format: '%d'", format);
00278 return FALSE;
00279 }
00280 d->name = name;
00281 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (format), d);
00282 return TRUE;
00283 }
00284
00285 QofDateFormat
00286 qof_date_format_get_current (void)
00287 {
00288 return dateFormat;
00289 }
00290
00291 gboolean
00292 qof_date_format_set_current (QofDateFormat df)
00293 {
00294 QofDateEntry *d;
00295
00296 g_return_val_if_fail (QofDateInit, FALSE);
00297 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
00298 if (!d)
00299 {
00300 PERR (" unknown format: '%d'", df);
00301 return FALSE;
00302 }
00303 dateFormat = d->df;
00304 return TRUE;
00305 }
00306
00307 const gchar *
00308 qof_date_format_get_format (QofDateFormat df)
00309 {
00310 QofDateEntry *d;
00311
00312 g_return_val_if_fail (QofDateInit, NULL);
00313 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
00314 if (!d)
00315 {
00316 PERR (" unknown format: '%d'", df);
00317 return NULL;
00318 }
00319 return d->format;
00320 }
00321
00322 gchar
00323 qof_date_format_get_date_separator (QofDateFormat df)
00324 {
00325 QofDateEntry *d;
00326
00327 g_return_val_if_fail (QofDateInit, locale_separator);
00328 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
00329 if (!d)
00330 {
00331 PERR (" unknown format: '%d'", df);
00332 return locale_separator;
00333 }
00334 return d->separator;
00335 }
00336
00337 gboolean
00338 qof_date_format_set_date_separator (const gchar sep, QofDateFormat df)
00339 {
00340 QofDateEntry *d;
00341
00342 g_return_val_if_fail (QofDateInit, FALSE);
00343 if (df < DATE_FORMAT_LAST)
00344 {
00345 DEBUG (" Prevented attempt to override a default format");
00346 return FALSE;
00347 }
00348 if (g_ascii_isdigit (sep))
00349 return FALSE;
00350 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
00351 if (!d)
00352 {
00353 PERR (" unknown format: '%d'", df);
00354 return FALSE;
00355 }
00356 d->separator = sep;
00357 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (df), d);
00358 return TRUE;
00359 }
00360
00361 struct iter
00362 {
00363 const gchar *name;
00364 QofDateFormat df;
00365 };
00366
00367 static void
00368 lookup_name (gpointer key, gpointer value, gpointer data)
00369 {
00370 struct iter *i;
00371 QofDateEntry *d;
00372
00373 i = (struct iter *) data;
00374 d = (QofDateEntry *) value;
00375 if (0 == safe_strcmp (d->name, i->name))
00376 {
00377 i->df = d->df;
00378 }
00379 }
00380
00381 QofDateFormat
00382 qof_date_format_from_name (const gchar * name)
00383 {
00384 struct iter i;
00385
00386 if (!name)
00387 return -1;
00388 if (0 == safe_strcmp (name, "us"))
00389 return QOF_DATE_FORMAT_US;
00390 if (0 == safe_strcmp (name, "uk"))
00391 return QOF_DATE_FORMAT_UK;
00392 if (0 == safe_strcmp (name, "ce"))
00393 return QOF_DATE_FORMAT_CE;
00394 if (0 == safe_strcmp (name, "utc"))
00395 return QOF_DATE_FORMAT_UTC;
00396 if (0 == safe_strcmp (name, "iso"))
00397 return QOF_DATE_FORMAT_ISO;
00398 if (0 == safe_strcmp (name, "locale"))
00399 return QOF_DATE_FORMAT_LOCALE;
00400 if (0 == safe_strcmp (name, "custom"))
00401 return QOF_DATE_FORMAT_CUSTOM;
00402 i.name = name;
00403 i.df = -1;
00404 g_hash_table_foreach (DateFormatTable, lookup_name, &i);
00405 return i.df;
00406 }
00407
00408 static QofDate*
00409 date_normalise (QofDate * date)
00410 {
00411 gint days;
00412
00413 g_return_val_if_fail (date, NULL);
00414 date->qd_sec -= date->qd_gmt_off;
00415
00416 if ((date->qd_nanosecs >= QOF_NSECS) ||
00417 (date->qd_nanosecs <= -QOF_NSECS))
00418 {
00419 date->qd_sec += date->qd_nanosecs / QOF_NSECS;
00420 date->qd_nanosecs = date->qd_nanosecs % QOF_NSECS;
00421 if (date->qd_nanosecs < 0)
00422 {
00423 date->qd_nanosecs += QOF_NSECS;
00424 date->qd_sec--;
00425 }
00426 }
00427 if ((date->qd_sec >= 60) || (date->qd_sec <= -60))
00428 {
00429 date->qd_min += date->qd_sec / 60;
00430 date->qd_sec = date->qd_sec % 60;
00431 if (date->qd_sec < 0)
00432 {
00433 date->qd_sec += 60;
00434 date->qd_min--;
00435 }
00436 }
00437 if ((date->qd_min >= 60) || (date->qd_min <= -60))
00438 {
00439 date->qd_hour += date->qd_min / 60;
00440 date->qd_min = date->qd_min % 60;
00441 if (date->qd_min < 0)
00442 {
00443 date->qd_min += 60;
00444 date->qd_hour--;
00445 }
00446 }
00447 if ((date->qd_hour >= 24) || (date->qd_hour <= -24))
00448 {
00449 date->qd_mday += date->qd_hour / 24;
00450 date->qd_hour = date->qd_hour % 24;
00451 if (date->qd_hour < 0)
00452 {
00453 date->qd_hour += 24;
00454 date->qd_mday--;
00455 }
00456 }
00457 if ((date->qd_mon > 12) || (date->qd_mon < -12))
00458 {
00459 date->qd_year += date->qd_mon / 12;
00460 date->qd_mon = date->qd_mon % 12;
00461 if (date->qd_mon < 0)
00462 {
00463
00464 date->qd_mon += 12 + 1;
00465 date->qd_year = (date->qd_year < 0) ?
00466 date->qd_year++ : date->qd_year--;
00467 }
00468 }
00469
00470 if (date->qd_mon == 0)
00471 date->qd_mon = 1;
00472
00473 if (date->qd_year == 0)
00474 date->qd_year = -1;
00475 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon];
00476 while (date->qd_mday < 0)
00477 {
00478 date->qd_mday += days;
00479 date->qd_mon--;
00480 if (date->qd_mon < 1)
00481 {
00482 date->qd_year -= date->qd_mon / 12;
00483 date->qd_mon = date->qd_mon % 12;
00484
00485 if ((date->qd_year == 0) && (date->qd_mon < 0))
00486 date->qd_year = -1;
00487 }
00488 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon];
00489 }
00490 while (date->qd_mday > days)
00491 {
00492 date->qd_mday -= days;
00493 date->qd_mon++;
00494 if (date->qd_mon > 12)
00495 {
00496 date->qd_year += date->qd_mon / 12;
00497 date->qd_mon = date->qd_mon % 12;
00498
00499 if ((date->qd_year == 0) && (date->qd_mon > 0))
00500 date->qd_year = +1;
00501 }
00502 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon];
00503 }
00504
00505 if (date->qd_mday == 0)
00506 date->qd_mday = 1;
00507 if (date->qd_mon == 0)
00508 date->qd_mon = 1;
00509
00510 date->qd_yday = (date->qd_mday - 1) +
00511 days_in_year[qof_date_isleap(date->qd_year)][date->qd_mon];
00512 set_day_of_the_week (date);
00513
00514 date->qd_valid = TRUE;
00515 date->qd_zone = "GMT";
00516 date->qd_is_dst = 0;
00517 date->qd_gmt_off = 0L;
00518 return date;
00519 }
00520
00521 QofDate *
00522 qof_date_parse (const gchar * str, QofDateFormat df)
00523 {
00524 const gchar *format;
00525 QofDateError error;
00526 QofDate *date;
00527 gchar *check;
00528
00529 check = NULL;
00530 error = ERR_NO_ERROR;
00531 date = qof_date_new ();
00532 format = qof_date_format_get_format (df);
00533 check = strptime_internal (str, format, date, &error);
00534 if (error != ERR_NO_ERROR)
00535 {
00536 qof_date_free (date);
00537 return NULL;
00538 }
00539 date = date_normalise (date);
00540 return date;
00541 }
00542
00543 gchar *
00544 qof_date_print (const QofDate * date, QofDateFormat df)
00545 {
00546 size_t result;
00547 gchar temp[MAX_DATE_BUFFER];
00548 QofDateEntry *d;
00549
00550 g_return_val_if_fail (QofDateInit, NULL);
00551 g_return_val_if_fail (date, NULL);
00552 g_return_val_if_fail (date->qd_valid, NULL);
00553 d = g_hash_table_lookup (DateFormatTable,
00554 GINT_TO_POINTER (df));
00555 g_return_val_if_fail (d, NULL);
00556 temp[0] = '\1';
00557 result = strftime_case (FALSE, temp, MAX_DATE_BUFFER,
00558 d->format, date, 1, date->qd_nanosecs);
00559 if (result == 0 && temp[0] != '\0')
00560 {
00561 PERR (" qof extended strftime failed");
00562 return NULL;
00563 }
00564 return g_strndup(temp, result);
00565 }
00566
00567
00568
00569 QofDate *
00570 qof_date_new (void)
00571 {
00572 QofDate *d;
00573
00574 d = g_new0 (QofDate, 1);
00575 return d;
00576 }
00577
00578 QofDate *
00579 qof_date_get_current (void)
00580 {
00581 QofTime *qt;
00582 QofDate *qd;
00583
00584 qt = qof_time_get_current ();
00585 qd = qof_date_from_qtime (qt);
00586 qof_time_free (qt);
00587 return qd;
00588 }
00589
00590 QofDate *
00591 qof_date_new_dmy (gint day, gint month, gint64 year)
00592 {
00593 QofDate *qd;
00594
00595 qd = g_new0 (QofDate, 1);
00596 qd->qd_mday = day;
00597 qd->qd_mon = month;
00598 qd->qd_year = year;
00599 if(!qof_date_valid (qd))
00600 return NULL;
00601 return qd;
00602 }
00603
00604 void
00605 qof_date_free (QofDate * date)
00606 {
00607 g_return_if_fail (date);
00608 g_free (date);
00609 date = NULL;
00610 }
00611
00612 gboolean
00613 qof_date_valid (QofDate *date)
00614 {
00615 g_return_val_if_fail (date, FALSE);
00616 date = date_normalise (date);
00617 if (date->qd_valid == FALSE)
00618 {
00619 PERR (" unknown QofDate error");
00620 return FALSE;
00621 }
00622 return TRUE;
00623 }
00624
00625 gboolean
00626 qof_date_equal (const QofDate *d1, const QofDate *d2)
00627 {
00628 if (0 == qof_date_compare (d1, d2))
00629 return TRUE;
00630 return FALSE;
00631 }
00632
00633 gint
00634 qof_date_compare (const QofDate * d1, const QofDate * d2)
00635 {
00636 if ((!d1) && (!d2))
00637 return 0;
00638 if (d1 == d2)
00639 return 0;
00640 if (!d1)
00641 return -1;
00642 if (!d2)
00643 return 1;
00644 if (d1->qd_year < d2->qd_year)
00645 return -1;
00646 if (d1->qd_year > d2->qd_year)
00647 return 1;
00648 if (d1->qd_mon < d2->qd_mon)
00649 return -1;
00650 if (d1->qd_mon > d2->qd_mon)
00651 return 1;
00652 if (d1->qd_mday < d2->qd_mday)
00653 return -1;
00654 if (d1->qd_mday > d2->qd_mday)
00655 return 1;
00656 if (d1->qd_hour < d2->qd_hour)
00657 return -1;
00658 if (d1->qd_hour > d2->qd_hour)
00659 return 1;
00660 if (d1->qd_min < d2->qd_min)
00661 return -1;
00662 if (d1->qd_min > d2->qd_min)
00663 return 1;
00664 if (d1->qd_sec < d2->qd_sec)
00665 return -1;
00666 if (d1->qd_sec > d2->qd_sec)
00667 return 1;
00668 if (d1->qd_nanosecs < d2->qd_nanosecs)
00669 return -1;
00670 if (d1->qd_nanosecs > d2->qd_nanosecs)
00671 return 1;
00672 return 0;
00673 }
00674
00675 QofDate *
00676 qof_date_from_struct_tm (const struct tm *stm)
00677 {
00678 QofDate *d;
00679
00680 g_return_val_if_fail (stm, NULL);
00681 d = g_new0 (QofDate, 1);
00682 d->qd_sec = stm->tm_sec;
00683 d->qd_min = stm->tm_min;
00684 d->qd_hour = stm->tm_hour;
00685 d->qd_mday = stm->tm_mday;
00686 d->qd_mon = stm->tm_mon + 1;
00687 d->qd_year = stm->tm_year + 1900;
00688 d->qd_wday = stm->tm_wday;
00689 d->qd_yday = stm->tm_yday;
00690 d->qd_is_dst = stm->tm_isdst;
00691 d->qd_gmt_off = stm->tm_gmtoff;
00692 d->qd_zone = stm->tm_zone;
00693 d->qd_valid = TRUE;
00694 d = date_normalise(d);
00695 return d;
00696 }
00697
00698 gboolean
00699 qof_date_to_struct_tm (const QofDate * qd, struct tm * stm,
00700 glong *nanosecs)
00701 {
00702 g_return_val_if_fail (qd, FALSE);
00703 g_return_val_if_fail (stm, FALSE);
00704 g_return_val_if_fail (qd->qd_valid, FALSE);
00705 if ((qd->qd_year > G_MAXINT) || (qd->qd_year < 1900))
00706 {
00707 PERR (" date too large for struct tm");
00708 return FALSE;
00709 }
00710 stm->tm_sec = qd->qd_sec;
00711 stm->tm_min = qd->qd_min;
00712 stm->tm_hour = qd->qd_hour;
00713 stm->tm_mday = qd->qd_mday;
00714 stm->tm_mon = qd->qd_mon - 1;
00715 stm->tm_year = qd->qd_year - 1900;
00716 stm->tm_wday = qd->qd_wday;
00717 stm->tm_yday = qd->qd_yday;
00718 stm->tm_isdst = qd->qd_is_dst;
00719 stm->tm_gmtoff = qd->qd_gmt_off;
00720 stm->tm_zone = qd->qd_zone;
00721 if (nanosecs != NULL)
00722 *nanosecs = qd->qd_nanosecs;
00723 return TRUE;
00724 }
00725
00726 gboolean
00727 qof_date_to_gdate (const QofDate *qd, GDate *gd)
00728 {
00729 g_return_val_if_fail (qd, FALSE);
00730 g_return_val_if_fail (gd, FALSE);
00731 g_return_val_if_fail (qd->qd_valid, FALSE);
00732 if (qd->qd_year >= G_MAXUINT16)
00733 {
00734 PERR (" QofDate out of range of GDate");
00735 return FALSE;
00736 }
00737 if (!g_date_valid_dmy (qd->qd_mday, qd->qd_mon, qd->qd_year))
00738 {
00739 PERR (" GDate failed to allow day, month and/or year");
00740 return FALSE;
00741 }
00742 g_date_set_dmy (gd, qd->qd_mday, qd->qd_mon, qd->qd_year);
00743 return TRUE;
00744 }
00745
00746 QofDate *
00747 qof_date_from_gdate (const GDate *date)
00748 {
00749 QofDate * qd;
00750
00751 g_return_val_if_fail (g_date_valid (date), NULL);
00752 qd = qof_date_new ();
00753 qd->qd_year = g_date_get_year (date);
00754 qd->qd_mon = g_date_get_month (date);
00755 qd->qd_mday = g_date_get_day (date);
00756 qd = date_normalise (qd);
00757 return qd;
00758 }
00759
00760 static void
00761 qof_date_offset (const QofTime *time, glong offset, QofDate *qd)
00762 {
00763 glong days;
00764 gint64 rem, y, yg;
00765 const guint16 *ip;
00766 QofTimeSecs t;
00767
00768 g_return_if_fail (qd);
00769 g_return_if_fail (time);
00770 t = qof_time_get_secs ((QofTime*)time);
00771 days = t / SECS_PER_DAY;
00772 rem = t % SECS_PER_DAY;
00773 rem += offset;
00774 while (rem < 0)
00775 {
00776 rem += SECS_PER_DAY;
00777 --days;
00778 }
00779 while (rem >= SECS_PER_DAY)
00780 {
00781 rem -= SECS_PER_DAY;
00782 ++days;
00783 }
00784 qd->qd_hour = rem / SECS_PER_HOUR;
00785 rem %= SECS_PER_HOUR;
00786 qd->qd_min = rem / 60;
00787 qd->qd_sec = rem % 60;
00788
00789 qd->qd_wday = (4 + days) % 7;
00790 if (qd->qd_wday < 0)
00791 qd->qd_wday += 7;
00792 y = 1970;
00793 while (days < 0 || days >= (__isleap (y) ? 366 : 365))
00794 {
00795
00796 yg = y + days / 365 - (days % 365 < 0);
00797
00798 days -= ((yg - y) * 365
00799 + LEAPS_THRU_END_OF (yg - 1)
00800 - LEAPS_THRU_END_OF (y - 1));
00801 y = yg;
00802 }
00803 qd->qd_year = y;
00804 qd->qd_yday = days;
00805 ip = days_in_year[qof_date_isleap(y)];
00806 for (y = 12; days < (glong) ip[y]; --y)
00807 continue;
00808 days -= ip[y];
00809 qd->qd_mon = y;
00810 qd->qd_mday = days + 1;
00811 }
00812
00813
00814
00815 static gint
00816 count_leapseconds (time_t interval)
00817 {
00818 time_t altered;
00819 struct tm utc;
00820
00821 altered = interval;
00822 utc = *gmtime_r (&interval, &utc);
00823 altered = mktime (&utc);
00824 return altered - interval;
00825 }
00826
00827
00828 static gint
00829 extract_interval (const QofTime *qt)
00830 {
00831 gint leap_seconds;
00832 QofTimeSecs t, l;
00833 const QofTime *now;
00834
00835 leap_seconds = 0;
00836 t = qof_time_get_secs (qt);
00837 now = qof_time_get_current ();
00838 l = (qof_time_get_secs (now) > G_MAXINT32) ?
00839 G_MAXINT32 : qof_time_get_secs (now);
00840 leap_seconds = ((t > l) || (t < 0)) ?
00841 count_leapseconds (l) :
00842 count_leapseconds (t);
00843 return leap_seconds;
00844 }
00845
00846 QofDate *
00847 qof_date_from_qtime (const QofTime *qt)
00848 {
00849 QofDate *qd;
00850 gint leap_extra_secs;
00851
00852
00853
00854 g_return_val_if_fail (qt, NULL);
00855 g_return_val_if_fail (qof_time_is_valid (qt), NULL);
00856 qd = qof_date_new ();
00857 leap_extra_secs = 0;
00858 tzset();
00859 leap_extra_secs = extract_interval (qt);
00860 qof_date_offset (qt, leap_extra_secs, qd);
00861 qd->qd_nanosecs = qof_time_get_nanosecs (qt);
00862 qd->qd_is_dst = 0;
00863 qd->qd_zone = "GMT";
00864 qd->qd_gmt_off = 0L;
00865 if (!qof_date_valid(qd))
00866 return NULL;
00867 return qd;
00868 }
00869
00870 gint64
00871 days_between (gint64 year1, gint64 year2)
00872 {
00873 gint64 i, start, end, l;
00874
00875 l = 0;
00876 if (year1 == year2)
00877 return l;
00878 start = (year1 < year2) ? year1 : year2;
00879 end = (year2 < year1) ? year1: year2;
00880 for (i = start; i < end; i++)
00881 {
00882 l += (qof_date_isleap(i)) ? 366 : 365;
00883 }
00884 return l;
00885 }
00886
00887 QofTime*
00888 qof_date_to_qtime (const QofDate *qd)
00889 {
00890 QofTime *qt;
00891 QofTimeSecs c;
00892
00893 g_return_val_if_fail (qd, NULL);
00894 g_return_val_if_fail (qd->qd_valid, NULL);
00895 c = 0;
00896 qt = NULL;
00897 if (qd->qd_year < 1970)
00898 {
00899 c = qd->qd_sec;
00900 c += QOF_MIN_TO_SEC(qd->qd_min);
00901 c += QOF_HOUR_TO_SEC(qd->qd_hour);
00902 c += QOF_DAYS_TO_SEC(qd->qd_yday);
00903 c -= QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year));
00904 c -= qd->qd_gmt_off;
00905 qt = qof_time_set (c, qd->qd_nanosecs);
00906 }
00907 if (qd->qd_year >= 1970)
00908 {
00909 c = qd->qd_sec;
00910 c += QOF_MIN_TO_SEC(qd->qd_min);
00911 c += QOF_HOUR_TO_SEC(qd->qd_hour);
00912 c += QOF_DAYS_TO_SEC(qd->qd_yday);
00913 c += QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year));
00914 c -= qd->qd_gmt_off;
00915 qt = qof_time_set (c, qd->qd_nanosecs);
00916 }
00917 return qt;
00918 }
00919
00920 QofTime *
00921 qof_date_time_difference (const QofDate * date1, const QofDate * date2)
00922 {
00923 gint64 days;
00924 QofTime *secs;
00925
00926 secs = qof_time_new ();
00927 days = days_between (date1->qd_year, date2->qd_year);
00928 qof_time_add_secs(secs, QOF_DAYS_TO_SEC(days));
00929 if (days_between >= 0)
00930 {
00931
00932 qof_time_add_secs(secs, -1 *
00933 (QOF_HOUR_TO_SEC(date1->qd_hour) -
00934 QOF_MIN_TO_SEC(date1->qd_min) -
00935 (date1->qd_sec)));
00936 qof_time_add_secs(secs,
00937 QOF_HOUR_TO_SEC(date2->qd_hour) +
00938 QOF_MIN_TO_SEC(date2->qd_min) +
00939 (date2->qd_sec));
00940 qof_time_set_nanosecs(secs,
00941 (date1->qd_nanosecs - date2->qd_nanosecs));
00942 }
00943 if (days_between < 0)
00944 {
00945
00946 qof_time_add_secs (secs,
00947 QOF_HOUR_TO_SEC(date1->qd_hour) -
00948 QOF_MIN_TO_SEC(date1->qd_min) -
00949 (date1->qd_sec));
00950 qof_time_add_secs (secs, -1 *
00951 (QOF_HOUR_TO_SEC(date2->qd_hour) +
00952 QOF_MIN_TO_SEC(date2->qd_min) +
00953 (date2->qd_sec)));
00954 qof_time_set_nanosecs(secs,
00955 (date2->qd_nanosecs - date1->qd_nanosecs));
00956 }
00957 return secs;
00958 }
00959
00960 gboolean
00961 qof_date_adddays (QofDate * qd, gint days)
00962 {
00963 g_return_val_if_fail (qd, FALSE);
00964 g_return_val_if_fail (qof_date_valid (qd), FALSE);
00965 qd->qd_mday += days;
00966 return qof_date_valid (qd);
00967 }
00968
00969 gboolean
00970 qof_date_addmonths (QofDate * qd, gint months,
00971 gboolean track_last_day)
00972 {
00973 g_return_val_if_fail (qd, FALSE);
00974 g_return_val_if_fail (qof_date_valid (qd), FALSE);
00975 qd->qd_mon += months % 12;
00976 qd->qd_year += months / 12;
00977 g_return_val_if_fail (qof_date_valid (qd), FALSE);
00978 if (track_last_day && qof_date_is_last_mday (qd))
00979 {
00980 qd->qd_mday = qof_date_get_mday (qd->qd_mon,
00981 qd->qd_year);
00982 }
00983 return TRUE;
00984 }
00985
00986 inline gboolean
00987 qof_date_set_day_end (QofDate * qd)
00988 {
00989 qd->qd_hour = 23;
00990 qd->qd_min = 59;
00991 qd->qd_sec = 59;
00992 qd->qd_nanosecs = (QOF_NSECS - 1);
00993 return qof_date_valid (qd);
00994 }
00995
00996 inline gboolean
00997 qof_date_set_day_start (QofDate * qd)
00998 {
00999 g_return_val_if_fail (qd, FALSE);
01000 qd->qd_hour = 0;
01001 qd->qd_min = 0;
01002 qd->qd_sec = 0;
01003 qd->qd_nanosecs = G_GINT64_CONSTANT(0);
01004 return qof_date_valid (qd);
01005 }
01006
01007 inline gboolean
01008 qof_date_set_day_middle (QofDate * qd)
01009 {
01010 g_return_val_if_fail (qd, FALSE);
01011 qd->qd_hour = 12;
01012 qd->qd_min = 0;
01013 qd->qd_sec = 0;
01014 qd->qd_nanosecs = G_GINT64_CONSTANT(0);
01015 return qof_date_valid (qd);
01016 }
01017
01018