QOF 0.8.2
|
00001 /******************************************************************** 00002 * qofdate.c - QofDate, 64bit UTC date handling. 00003 * Rewritten from scratch for QOF 0.7.0 00004 * 00005 * Fri May 5 15:05:24 2006 00006 * Copyright (C) 1991, 1993, 1997, 1998, 2002, 2006 00007 * Free Software Foundation, Inc. 00008 * This file contains routines modified from the GNU C Library. 00009 ********************************************************************/ 00010 /* 00011 * This program is free software; you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation; either version 2 of the License, or 00014 * (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software 00023 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA 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 /* from gnu libc */ 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 /* copied from glib */ 00045 static const guint16 days_in_year[2][14] = 00046 { /* 0, jan feb mar apr may jun jul aug sep oct nov dec */ 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 { /* error, jan feb mar apr may jun jul aug sep oct nov dec */ 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 } /* leap year */ 00054 }; 00055 00056 /* A single Date Format Entry. */ 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 /* prevent really long strings being passed */ 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 /* test the incoming string using the current time. */ 00222 now = time (NULL); 00223 test[0] = '\1'; 00224 check = *gmtime_r (&now, &check); 00225 /* need to allow time related formats - 00226 don't use g_date_strftime here. */ 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 /* if value is negative, just add */ 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 /* Year Zero does not exist, 1BC is immediately followed by 1AD. */ 00452 if (date->qd_year == 0) 00453 date->qd_year = -1; 00454 /* qd_mon starts at 1, not zero */ 00455 if (date->qd_mon == 0) 00456 date->qd_mon = 1; 00457 /* qd_mday starts at 1, not zero */ 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 /* yes, [13] is correct == total at end of month_12 */ 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 /* -1 == Dec, -4 == Sep */ 00496 date->qd_mon += 12 + 1; 00497 if (date->qd_year < 0) 00498 { 00499 date->qd_year++; 00500 } 00501 else 00502 { 00503 date->qd_year--; 00504 } 00505 /*date->qd_year = (date->qd_year < 0) ? 00506 date->qd_year++ : date->qd_year--;*/ 00507 } 00508 } 00509 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon]; 00510 while (date->qd_mday < 0) 00511 { 00512 date->qd_mday += days; 00513 date->qd_mon--; 00514 if (date->qd_mon < 1) 00515 { 00516 date->qd_year -= date->qd_mon / 12; 00517 date->qd_mon = date->qd_mon % 12; 00518 /* if year was AD and is now zero, reset to BC. */ 00519 if ((date->qd_year == 0) && (date->qd_mon < 0)) 00520 date->qd_year = -1; 00521 } 00522 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon]; 00523 } 00524 while (date->qd_mday > days) 00525 { 00526 date->qd_mday -= days; 00527 date->qd_mon++; 00528 if (date->qd_mon > 11) 00529 { 00530 date->qd_year += date->qd_mon / 12; 00531 date->qd_mon = date->qd_mon % 12; 00532 /* if year was BC and is now zero, reset to AD. */ 00533 if ((date->qd_year == 0) && (date->qd_mon > 0)) 00534 date->qd_year = +1; 00535 } 00536 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon]; 00537 } 00538 /* use sensible defaults */ 00539 if (date->qd_mday == 0) 00540 date->qd_mday = 1; 00541 if (date->qd_mon == 0) 00542 date->qd_mon = 1; 00543 /* use days_in_year to set yday */ 00544 date->qd_yday = (date->qd_mday - 1) + 00545 days_in_year[qof_date_isleap(date->qd_year)][date->qd_mon]; 00546 set_day_of_the_week (date); 00547 00548 /* qd_year has no realistic limits */ 00549 date->qd_valid = TRUE; 00550 date->qd_zone = "GMT"; 00551 date->qd_is_dst = 0; 00552 date->qd_gmt_off = 0L; 00553 return date; 00554 } 00555 00556 QofDate * 00557 qof_date_parse (const gchar * str, QofDateFormat df) 00558 { 00559 const gchar *format; 00560 QofDateError error; 00561 QofDate *date; 00562 gchar *check; 00563 00564 check = NULL; 00565 error = ERR_NO_ERROR; 00566 date = qof_date_new (); 00567 format = qof_date_format_get_format (df); 00568 check = strptime_internal (str, format, date, &error); 00569 if (error != ERR_NO_ERROR) 00570 { 00571 qof_date_free (date); 00572 return NULL; 00573 } 00574 00575 date = date_normalise (date); 00576 return date; 00577 } 00578 00579 gchar * 00580 qof_date_print (const QofDate * date, QofDateFormat df) 00581 { 00582 size_t result; 00583 gchar temp[MAX_DATE_BUFFER]; 00584 QofDateEntry *d; 00585 00586 g_return_val_if_fail (QofDateInit, NULL); 00587 g_return_val_if_fail (date, NULL); 00588 g_return_val_if_fail (date->qd_valid, NULL); 00589 d = g_hash_table_lookup (DateFormatTable, 00590 GINT_TO_POINTER (df)); 00591 g_return_val_if_fail (d, NULL); 00592 temp[0] = '\1'; 00593 result = strftime_case (FALSE, temp, MAX_DATE_BUFFER, 00594 d->format, date, 1, date->qd_nanosecs); 00595 if (result == 0 && temp[0] != '\0') 00596 { 00597 PERR (" qof extended strftime failed"); 00598 return NULL; 00599 } 00600 return g_strndup(temp, result); 00601 } 00602 00603 /* QofDate handlers */ 00604 00605 QofDate * 00606 qof_date_new (void) 00607 { 00608 QofDate *d; 00609 00610 d = g_new0 (QofDate, 1); 00611 return d; 00612 } 00613 00614 QofDate * 00615 qof_date_get_current (void) 00616 { 00617 QofTime *qt; 00618 QofDate *qd; 00619 00620 qt = qof_time_get_current (); 00621 qd = qof_date_from_qtime (qt); 00622 qof_time_free (qt); 00623 return qd; 00624 } 00625 00626 QofDate * 00627 qof_date_new_dmy (gint day, gint month, gint64 year) 00628 { 00629 QofDate *qd; 00630 00631 qd = g_new0 (QofDate, 1); 00632 qd->qd_mday = day; 00633 qd->qd_mon = month; 00634 qd->qd_year = year; 00635 if(!qof_date_valid (qd)) 00636 return NULL; 00637 return qd; 00638 } 00639 00640 void 00641 qof_date_free (QofDate * date) 00642 { 00643 g_return_if_fail (date); 00644 g_free (date); 00645 date = NULL; 00646 } 00647 00648 gboolean 00649 qof_date_valid (QofDate *date) 00650 { 00651 g_return_val_if_fail (date, FALSE); 00652 date = date_normalise (date); 00653 if (date->qd_valid == FALSE) 00654 { 00655 PERR (" unknown QofDate error"); 00656 return FALSE; 00657 } 00658 return TRUE; 00659 } 00660 00661 gboolean 00662 qof_date_equal (const QofDate *d1, const QofDate *d2) 00663 { 00664 if (0 == qof_date_compare (d1, d2)) 00665 return TRUE; 00666 return FALSE; 00667 } 00668 00669 gint 00670 qof_date_compare (const QofDate * d1, const QofDate * d2) 00671 { 00672 if ((!d1) && (!d2)) 00673 return 0; 00674 if (d1 == d2) 00675 return 0; 00676 if (!d1) 00677 return -1; 00678 if (!d2) 00679 return 1; 00680 if (d1->qd_year < d2->qd_year) 00681 return -1; 00682 if (d1->qd_year > d2->qd_year) 00683 return 1; 00684 if (d1->qd_mon < d2->qd_mon) 00685 return -1; 00686 if (d1->qd_mon > d2->qd_mon) 00687 return 1; 00688 if (d1->qd_mday < d2->qd_mday) 00689 return -1; 00690 if (d1->qd_mday > d2->qd_mday) 00691 return 1; 00692 if (d1->qd_hour < d2->qd_hour) 00693 return -1; 00694 if (d1->qd_hour > d2->qd_hour) 00695 return 1; 00696 if (d1->qd_min < d2->qd_min) 00697 return -1; 00698 if (d1->qd_min > d2->qd_min) 00699 return 1; 00700 if (d1->qd_sec < d2->qd_sec) 00701 return -1; 00702 if (d1->qd_sec > d2->qd_sec) 00703 return 1; 00704 if (d1->qd_nanosecs < d2->qd_nanosecs) 00705 return -1; 00706 if (d1->qd_nanosecs > d2->qd_nanosecs) 00707 return 1; 00708 return 0; 00709 } 00710 00711 QofDate * 00712 qof_date_from_struct_tm (const struct tm *stm) 00713 { 00714 QofDate *d; 00715 00716 g_return_val_if_fail (stm, NULL); 00717 d = g_new0 (QofDate, 1); 00718 d->qd_sec = stm->tm_sec; 00719 d->qd_min = stm->tm_min; 00720 d->qd_hour = stm->tm_hour; 00721 d->qd_mday = stm->tm_mday; 00722 d->qd_mon = stm->tm_mon + 1; 00723 d->qd_year = stm->tm_year + 1900; 00724 d->qd_wday = stm->tm_wday; 00725 d->qd_yday = stm->tm_yday; 00726 d->qd_is_dst = stm->tm_isdst; 00727 d->qd_gmt_off = stm->tm_gmtoff; 00728 d->qd_zone = stm->tm_zone; 00729 d->qd_valid = TRUE; 00730 d = date_normalise(d); 00731 return d; 00732 } 00733 00734 gboolean 00735 qof_date_to_struct_tm (const QofDate * qd, struct tm * stm, 00736 glong *nanosecs) 00737 { 00738 g_return_val_if_fail (qd, FALSE); 00739 g_return_val_if_fail (stm, FALSE); 00740 g_return_val_if_fail (qd->qd_valid, FALSE); 00741 if ((qd->qd_year > G_MAXINT) || (qd->qd_year < 1900)) 00742 { 00743 PERR (" date too large for struct tm"); 00744 return FALSE; 00745 } 00746 stm->tm_sec = qd->qd_sec; 00747 stm->tm_min = qd->qd_min; 00748 stm->tm_hour = qd->qd_hour; 00749 stm->tm_mday = qd->qd_mday; 00750 stm->tm_mon = qd->qd_mon - 1; 00751 stm->tm_year = qd->qd_year - 1900; 00752 stm->tm_wday = qd->qd_wday; 00753 stm->tm_yday = qd->qd_yday; 00754 stm->tm_isdst = qd->qd_is_dst; 00755 stm->tm_gmtoff = qd->qd_gmt_off; 00756 stm->tm_zone = qd->qd_zone; 00757 if (nanosecs != NULL) 00758 *nanosecs = qd->qd_nanosecs; 00759 return TRUE; 00760 } 00761 00762 gboolean 00763 qof_date_to_gdate (const QofDate *qd, GDate *gd) 00764 { 00765 g_return_val_if_fail (qd, FALSE); 00766 g_return_val_if_fail (gd, FALSE); 00767 g_return_val_if_fail (qd->qd_valid, FALSE); 00768 if (qd->qd_year >= G_MAXUINT16) 00769 { 00770 PERR (" QofDate out of range of GDate"); 00771 return FALSE; 00772 } 00773 if (!g_date_valid_dmy (qd->qd_mday, qd->qd_mon, qd->qd_year)) 00774 { 00775 PERR (" GDate failed to allow day, month and/or year"); 00776 return FALSE; 00777 } 00778 g_date_set_dmy (gd, qd->qd_mday, qd->qd_mon, qd->qd_year); 00779 return TRUE; 00780 } 00781 00782 QofDate * 00783 qof_date_from_gdate (const GDate *date) 00784 { 00785 QofDate * qd; 00786 00787 g_return_val_if_fail (g_date_valid (date), NULL); 00788 qd = qof_date_new (); 00789 qd->qd_year = g_date_get_year (date); 00790 qd->qd_mon = g_date_get_month (date); 00791 qd->qd_mday = g_date_get_day (date); 00792 qd = date_normalise (qd); 00793 return qd; 00794 } 00795 00796 static void 00797 qof_date_offset (const QofTime *time, glong offset, QofDate *qd) 00798 { 00799 glong days; 00800 gint64 rem, y, yg; 00801 const guint16 *ip; 00802 QofTimeSecs t; 00803 00804 g_return_if_fail (qd); 00805 g_return_if_fail (time); 00806 t = qof_time_get_secs ((QofTime*)time); 00807 days = t / SECS_PER_DAY; 00808 rem = t % SECS_PER_DAY; 00809 rem += offset; 00810 while (rem < 0) 00811 { 00812 rem += SECS_PER_DAY; 00813 --days; 00814 } 00815 while (rem >= SECS_PER_DAY) 00816 { 00817 rem -= SECS_PER_DAY; 00818 ++days; 00819 } 00820 qd->qd_hour = rem / SECS_PER_HOUR; 00821 rem %= SECS_PER_HOUR; 00822 qd->qd_min = rem / 60; 00823 qd->qd_sec = rem % 60; 00824 /* January 1, 1970 was a Thursday. */ 00825 qd->qd_wday = (4 + days) % 7; 00826 if (qd->qd_wday < 0) 00827 qd->qd_wday += 7; 00828 y = 1970; 00829 while (days < 0 || days >= (__isleap (y) ? 366 : 365)) 00830 { 00831 /* Guess a corrected year, assuming 365 days per year. */ 00832 yg = y + days / 365 - (days % 365 < 0); 00833 /* Adjust DAYS and Y to match the guessed year. */ 00834 days -= ((yg - y) * 365 00835 + LEAPS_THRU_END_OF (yg - 1) 00836 - LEAPS_THRU_END_OF (y - 1)); 00837 y = yg; 00838 } 00839 qd->qd_year = y; 00840 qd->qd_yday = days; 00841 ip = days_in_year[qof_date_isleap(y)]; 00842 for (y = 12; days < (glong) ip[y]; --y) 00843 continue; 00844 days -= ip[y]; 00845 qd->qd_mon = y; 00846 qd->qd_mday = days + 1; 00847 } 00848 00849 /* safe to use time_t here because only values 00850 within the range of a time_t have any leapseconds. */ 00851 static gint 00852 count_leapseconds (time_t interval) 00853 { 00854 time_t altered; 00855 struct tm utc; 00856 00857 altered = interval; 00858 utc = *gmtime_r (&interval, &utc); 00859 altered = mktime (&utc); 00860 return altered - interval; 00861 } 00862 00863 /*static inline gint*/ 00864 static gint 00865 extract_interval (const QofTime *qt) 00866 { 00867 gint leap_seconds; 00868 QofTimeSecs t, l; 00869 const QofTime *now; 00870 00871 leap_seconds = 0; 00872 t = qof_time_get_secs (qt); 00873 now = qof_time_get_current (); 00874 l = (qof_time_get_secs (now) > G_MAXINT32) ? 00875 G_MAXINT32 : qof_time_get_secs (now); 00876 leap_seconds = ((t > l) || (t < 0)) ? 00877 count_leapseconds (l) : 00878 count_leapseconds (t); 00879 return leap_seconds; 00880 } 00881 00882 QofDate * 00883 qof_date_from_qtime (const QofTime *qt) 00884 { 00885 QofDate *qd; 00886 gint leap_extra_secs; 00887 00888 /* may not want to create a new time or date - it 00889 complicates memory management. */ 00890 g_return_val_if_fail (qt, NULL); 00891 g_return_val_if_fail (qof_time_is_valid (qt), NULL); 00892 qd = qof_date_new (); 00893 leap_extra_secs = 0; 00894 setenv ("TZ", "GMT", 1); 00895 tzset(); 00896 leap_extra_secs = extract_interval (qt); 00897 qof_date_offset (qt, leap_extra_secs, qd); 00898 qd->qd_nanosecs = qof_time_get_nanosecs (qt); 00899 qd->qd_is_dst = 0; 00900 qd->qd_zone = "GMT"; 00901 qd->qd_gmt_off = 0L; 00902 if (!qof_date_valid(qd)) 00903 return NULL; 00904 return qd; 00905 } 00906 00907 gint64 00908 days_between (gint64 year1, gint64 year2) 00909 { 00910 gint64 i, start, end, l; 00911 00912 l = 0; 00913 if (year1 == year2) 00914 return l; 00915 start = (year1 < year2) ? year1 : year2; 00916 end = (year2 < year1) ? year1: year2; 00917 for (i = start; i < end; i++) 00918 { 00919 l += (qof_date_isleap(i)) ? 366 : 365; 00920 } 00921 return l; 00922 } 00923 00924 QofTime* 00925 qof_date_to_qtime (const QofDate *qd) 00926 { 00927 QofTime *qt; 00928 QofTimeSecs c; 00929 00930 g_return_val_if_fail (qd, NULL); 00931 g_return_val_if_fail (qd->qd_valid, NULL); 00932 c = 0; 00933 qt = NULL; 00934 if (qd->qd_year < 1970) 00935 { 00936 c = qd->qd_sec; 00937 c += QOF_MIN_TO_SEC(qd->qd_min); 00938 c += QOF_HOUR_TO_SEC(qd->qd_hour); 00939 c += QOF_DAYS_TO_SEC(qd->qd_yday); 00940 c -= QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year)); 00941 c -= qd->qd_gmt_off; 00942 qt = qof_time_set (c, qd->qd_nanosecs); 00943 } 00944 if (qd->qd_year >= 1970) 00945 { 00946 c = qd->qd_sec; 00947 c += QOF_MIN_TO_SEC(qd->qd_min); 00948 c += QOF_HOUR_TO_SEC(qd->qd_hour); 00949 c += QOF_DAYS_TO_SEC(qd->qd_yday); 00950 c += QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year)); 00951 c -= qd->qd_gmt_off; 00952 qt = qof_time_set (c, qd->qd_nanosecs); 00953 } 00954 return qt; 00955 } 00956 00957 QofTime * 00958 qof_date_time_difference (const QofDate * date1, 00959 const QofDate * date2) 00960 { 00961 gint64 days; 00962 QofTime *secs; 00963 00964 secs = qof_time_new (); 00965 days = days_between (date1->qd_year, date2->qd_year); 00966 qof_time_add_secs(secs, QOF_DAYS_TO_SEC(days)); 00967 if (days >= 0) 00968 { 00969 /* positive value, add date2 secs, subtract date1 */ 00970 qof_time_add_secs(secs, -1 * 00971 (QOF_HOUR_TO_SEC(date1->qd_hour) - 00972 QOF_MIN_TO_SEC(date1->qd_min) - 00973 (date1->qd_sec))); 00974 qof_time_add_secs(secs, 00975 QOF_HOUR_TO_SEC(date2->qd_hour) + 00976 QOF_MIN_TO_SEC(date2->qd_min) + 00977 (date2->qd_sec)); 00978 qof_time_set_nanosecs(secs, 00979 (date1->qd_nanosecs - date2->qd_nanosecs)); 00980 } 00981 if (days < 0) 00982 { 00983 /* negative value*/ 00984 qof_time_add_secs (secs, 00985 QOF_HOUR_TO_SEC(date1->qd_hour) - 00986 QOF_MIN_TO_SEC(date1->qd_min) - 00987 (date1->qd_sec)); 00988 qof_time_add_secs (secs, -1 * 00989 (QOF_HOUR_TO_SEC(date2->qd_hour) + 00990 QOF_MIN_TO_SEC(date2->qd_min) + 00991 (date2->qd_sec))); 00992 qof_time_set_nanosecs(secs, 00993 (date2->qd_nanosecs - date1->qd_nanosecs)); 00994 } 00995 return secs; 00996 } 00997 00998 gboolean 00999 qof_date_adddays (QofDate * qd, gint days) 01000 { 01001 g_return_val_if_fail (qd, FALSE); 01002 g_return_val_if_fail (qof_date_valid (qd), FALSE); 01003 qd->qd_mday += days; 01004 return qof_date_valid (qd); 01005 } 01006 01007 gboolean 01008 qof_date_addmonths (QofDate * qd, gint months, 01009 gboolean track_last_day) 01010 { 01011 g_return_val_if_fail (qd, FALSE); 01012 g_return_val_if_fail (qof_date_valid (qd), FALSE); 01013 qd->qd_mon += months % 12; 01014 qd->qd_year += months / 12; 01015 g_return_val_if_fail (qof_date_valid (qd), FALSE); 01016 if (track_last_day && qof_date_is_last_mday (qd)) 01017 { 01018 qd->qd_mday = qof_date_get_mday (qd->qd_mon, 01019 qd->qd_year); 01020 } 01021 return TRUE; 01022 } 01023 01024 inline gboolean 01025 qof_date_set_day_end (QofDate * qd) 01026 { 01027 qd->qd_hour = 23; 01028 qd->qd_min = 59; 01029 qd->qd_sec = 59; 01030 qd->qd_nanosecs = (QOF_NSECS - 1); 01031 return qof_date_valid (qd); 01032 } 01033 01034 inline gboolean 01035 qof_date_set_day_start (QofDate * qd) 01036 { 01037 g_return_val_if_fail (qd, FALSE); 01038 qd->qd_hour = 0; 01039 qd->qd_min = 0; 01040 qd->qd_sec = 0; 01041 qd->qd_nanosecs = G_GINT64_CONSTANT(0); 01042 return qof_date_valid (qd); 01043 } 01044 01045 inline gboolean 01046 qof_date_set_day_middle (QofDate * qd) 01047 { 01048 g_return_val_if_fail (qd, FALSE); 01049 qd->qd_hour = 12; 01050 qd->qd_min = 0; 01051 qd->qd_sec = 0; 01052 qd->qd_nanosecs = G_GINT64_CONSTANT(0); 01053 return qof_date_valid (qd); 01054 } 01055 01056 /******************** END OF FILE *************************/