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