QOF
0.8.7
|
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 * G_GNUC_UNUSED 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 check = NULL; 00570 if (error != ERR_NO_ERROR) 00571 { 00572 qof_date_free (date); 00573 return NULL; 00574 } 00575 00576 date = date_normalise (date); 00577 return date; 00578 } 00579 00580 gchar * 00581 qof_date_print (const QofDate * date, QofDateFormat df) 00582 { 00583 size_t result; 00584 gchar temp[MAX_DATE_BUFFER]; 00585 QofDateEntry *d; 00586 00587 g_return_val_if_fail (QofDateInit, NULL); 00588 g_return_val_if_fail (date, NULL); 00589 g_return_val_if_fail (date->qd_valid, NULL); 00590 d = g_hash_table_lookup (DateFormatTable, 00591 GINT_TO_POINTER (df)); 00592 g_return_val_if_fail (d, NULL); 00593 temp[0] = '\1'; 00594 result = strftime_case (FALSE, temp, MAX_DATE_BUFFER, 00595 d->format, date, 1, date->qd_nanosecs); 00596 if (result == 0 && temp[0] != '\0') 00597 { 00598 PERR (" qof extended strftime failed"); 00599 return NULL; 00600 } 00601 return g_strndup(temp, result); 00602 } 00603 00604 /* QofDate handlers */ 00605 00606 QofDate * 00607 qof_date_new (void) 00608 { 00609 QofDate *d; 00610 00611 d = g_new0 (QofDate, 1); 00612 return d; 00613 } 00614 00615 QofDate * 00616 qof_date_get_current (void) 00617 { 00618 QofTime *qt; 00619 QofDate *qd; 00620 00621 qt = qof_time_get_current (); 00622 qd = qof_date_from_qtime (qt); 00623 qof_time_free (qt); 00624 return qd; 00625 } 00626 00627 QofDate * 00628 qof_date_new_dmy (gint day, gint month, gint64 year) 00629 { 00630 QofDate *qd; 00631 00632 qd = g_new0 (QofDate, 1); 00633 qd->qd_mday = day; 00634 qd->qd_mon = month; 00635 qd->qd_year = year; 00636 if(!qof_date_valid (qd)) 00637 return NULL; 00638 return qd; 00639 } 00640 00641 void 00642 qof_date_free (QofDate * date) 00643 { 00644 g_return_if_fail (date); 00645 g_free (date); 00646 date = NULL; 00647 } 00648 00649 gboolean 00650 qof_date_valid (QofDate *date) 00651 { 00652 g_return_val_if_fail (date, FALSE); 00653 date = date_normalise (date); 00654 if (date->qd_valid == FALSE) 00655 { 00656 PERR (" unknown QofDate error"); 00657 return FALSE; 00658 } 00659 return TRUE; 00660 } 00661 00662 gboolean 00663 qof_date_equal (const QofDate *d1, const QofDate *d2) 00664 { 00665 if (0 == qof_date_compare (d1, d2)) 00666 return TRUE; 00667 return FALSE; 00668 } 00669 00670 gint 00671 qof_date_compare (const QofDate * d1, const QofDate * d2) 00672 { 00673 if ((!d1) && (!d2)) 00674 return 0; 00675 if (d1 == d2) 00676 return 0; 00677 if (!d1) 00678 return -1; 00679 if (!d2) 00680 return 1; 00681 if (d1->qd_year < d2->qd_year) 00682 return -1; 00683 if (d1->qd_year > d2->qd_year) 00684 return 1; 00685 if (d1->qd_mon < d2->qd_mon) 00686 return -1; 00687 if (d1->qd_mon > d2->qd_mon) 00688 return 1; 00689 if (d1->qd_mday < d2->qd_mday) 00690 return -1; 00691 if (d1->qd_mday > d2->qd_mday) 00692 return 1; 00693 if (d1->qd_hour < d2->qd_hour) 00694 return -1; 00695 if (d1->qd_hour > d2->qd_hour) 00696 return 1; 00697 if (d1->qd_min < d2->qd_min) 00698 return -1; 00699 if (d1->qd_min > d2->qd_min) 00700 return 1; 00701 if (d1->qd_sec < d2->qd_sec) 00702 return -1; 00703 if (d1->qd_sec > d2->qd_sec) 00704 return 1; 00705 if (d1->qd_nanosecs < d2->qd_nanosecs) 00706 return -1; 00707 if (d1->qd_nanosecs > d2->qd_nanosecs) 00708 return 1; 00709 return 0; 00710 } 00711 00712 QofDate * 00713 qof_date_from_struct_tm (const struct tm *stm) 00714 { 00715 QofDate *d; 00716 00717 g_return_val_if_fail (stm, NULL); 00718 d = g_new0 (QofDate, 1); 00719 d->qd_sec = stm->tm_sec; 00720 d->qd_min = stm->tm_min; 00721 d->qd_hour = stm->tm_hour; 00722 d->qd_mday = stm->tm_mday; 00723 d->qd_mon = stm->tm_mon + 1; 00724 d->qd_year = stm->tm_year + 1900; 00725 d->qd_wday = stm->tm_wday; 00726 d->qd_yday = stm->tm_yday; 00727 d->qd_is_dst = stm->tm_isdst; 00728 d->qd_gmt_off = stm->tm_gmtoff; 00729 d->qd_zone = stm->tm_zone; 00730 d->qd_valid = TRUE; 00731 d = date_normalise(d); 00732 return d; 00733 } 00734 00735 gboolean 00736 qof_date_to_struct_tm (const QofDate * qd, struct tm * stm, 00737 glong *nanosecs) 00738 { 00739 g_return_val_if_fail (qd, FALSE); 00740 g_return_val_if_fail (stm, FALSE); 00741 g_return_val_if_fail (qd->qd_valid, FALSE); 00742 if ((qd->qd_year > G_MAXINT) || (qd->qd_year < 1900)) 00743 { 00744 PERR (" date too large for struct tm"); 00745 return FALSE; 00746 } 00747 stm->tm_sec = qd->qd_sec; 00748 stm->tm_min = qd->qd_min; 00749 stm->tm_hour = qd->qd_hour; 00750 stm->tm_mday = qd->qd_mday; 00751 stm->tm_mon = qd->qd_mon - 1; 00752 stm->tm_year = qd->qd_year - 1900; 00753 stm->tm_wday = qd->qd_wday; 00754 stm->tm_yday = qd->qd_yday; 00755 stm->tm_isdst = qd->qd_is_dst; 00756 stm->tm_gmtoff = qd->qd_gmt_off; 00757 stm->tm_zone = qd->qd_zone; 00758 if (nanosecs != NULL) 00759 *nanosecs = qd->qd_nanosecs; 00760 return TRUE; 00761 } 00762 00763 gboolean 00764 qof_date_to_gdate (const QofDate *qd, GDate *gd) 00765 { 00766 g_return_val_if_fail (qd, FALSE); 00767 g_return_val_if_fail (gd, FALSE); 00768 g_return_val_if_fail (qd->qd_valid, FALSE); 00769 if (qd->qd_year >= G_MAXUINT16) 00770 { 00771 PERR (" QofDate out of range of GDate"); 00772 return FALSE; 00773 } 00774 if (!g_date_valid_dmy (qd->qd_mday, qd->qd_mon, qd->qd_year)) 00775 { 00776 PERR (" GDate failed to allow day, month and/or year"); 00777 return FALSE; 00778 } 00779 g_date_set_dmy (gd, qd->qd_mday, qd->qd_mon, qd->qd_year); 00780 return TRUE; 00781 } 00782 00783 QofDate * 00784 qof_date_from_gdate (const GDate *date) 00785 { 00786 QofDate * qd; 00787 00788 g_return_val_if_fail (g_date_valid (date), NULL); 00789 qd = qof_date_new (); 00790 qd->qd_year = g_date_get_year (date); 00791 qd->qd_mon = g_date_get_month (date); 00792 qd->qd_mday = g_date_get_day (date); 00793 qd = date_normalise (qd); 00794 return qd; 00795 } 00796 00797 static void 00798 qof_date_offset (const QofTime *time, glong offset, QofDate *qd) 00799 { 00800 glong days; 00801 gint64 rem, y, yg; 00802 const guint16 *ip; 00803 QofTimeSecs t; 00804 00805 g_return_if_fail (qd); 00806 g_return_if_fail (time); 00807 t = qof_time_get_secs ((QofTime*)time); 00808 days = t / SECS_PER_DAY; 00809 rem = t % SECS_PER_DAY; 00810 rem += offset; 00811 while (rem < 0) 00812 { 00813 rem += SECS_PER_DAY; 00814 --days; 00815 } 00816 while (rem >= SECS_PER_DAY) 00817 { 00818 rem -= SECS_PER_DAY; 00819 ++days; 00820 } 00821 qd->qd_hour = rem / SECS_PER_HOUR; 00822 rem %= SECS_PER_HOUR; 00823 qd->qd_min = rem / 60; 00824 qd->qd_sec = rem % 60; 00825 /* January 1, 1970 was a Thursday. */ 00826 qd->qd_wday = (4 + days) % 7; 00827 if (qd->qd_wday < 0) 00828 qd->qd_wday += 7; 00829 y = 1970; 00830 while (days < 0 || days >= (__isleap (y) ? 366 : 365)) 00831 { 00832 /* Guess a corrected year, assuming 365 days per year. */ 00833 yg = y + days / 365 - (days % 365 < 0); 00834 /* Adjust DAYS and Y to match the guessed year. */ 00835 days -= ((yg - y) * 365 00836 + LEAPS_THRU_END_OF (yg - 1) 00837 - LEAPS_THRU_END_OF (y - 1)); 00838 y = yg; 00839 } 00840 qd->qd_year = y; 00841 qd->qd_yday = days; 00842 ip = days_in_year[qof_date_isleap(y)]; 00843 for (y = 12; days < (glong) ip[y]; --y) 00844 continue; 00845 days -= ip[y]; 00846 qd->qd_mon = y; 00847 qd->qd_mday = days + 1; 00848 } 00849 00850 /* safe to use time_t here because only values 00851 within the range of a time_t have any leapseconds. */ 00852 static gint 00853 count_leapseconds (time_t interval) 00854 { 00855 time_t altered; 00856 struct tm utc; 00857 00858 altered = interval; 00859 utc = *gmtime_r (&interval, &utc); 00860 altered = mktime (&utc); 00861 return altered - interval; 00862 } 00863 00864 /*static inline gint*/ 00865 static gint 00866 extract_interval (const QofTime *qt) 00867 { 00868 gint leap_seconds; 00869 QofTimeSecs t, l; 00870 const QofTime *now; 00871 00872 leap_seconds = 0; 00873 t = qof_time_get_secs (qt); 00874 now = qof_time_get_current (); 00875 l = (qof_time_get_secs (now) > G_MAXINT32) ? 00876 G_MAXINT32 : qof_time_get_secs (now); 00877 leap_seconds = ((t > l) || (t < 0)) ? 00878 count_leapseconds (l) : 00879 count_leapseconds (t); 00880 return leap_seconds; 00881 } 00882 00883 QofDate * 00884 qof_date_from_qtime (const QofTime *qt) 00885 { 00886 QofDate *qd; 00887 gint leap_extra_secs; 00888 00889 /* may not want to create a new time or date - it 00890 complicates memory management. */ 00891 g_return_val_if_fail (qt, NULL); 00892 g_return_val_if_fail (qof_time_is_valid (qt), NULL); 00893 qd = qof_date_new (); 00894 leap_extra_secs = 0; 00895 setenv ("TZ", "GMT", 1); 00896 tzset(); 00897 leap_extra_secs = extract_interval (qt); 00898 qof_date_offset (qt, leap_extra_secs, qd); 00899 qd->qd_nanosecs = qof_time_get_nanosecs (qt); 00900 qd->qd_is_dst = 0; 00901 qd->qd_zone = "GMT"; 00902 qd->qd_gmt_off = 0L; 00903 if (!qof_date_valid(qd)) 00904 return NULL; 00905 return qd; 00906 } 00907 00908 gint64 00909 days_between (gint64 year1, gint64 year2) 00910 { 00911 gint64 i, start, end, l; 00912 00913 l = 0; 00914 if (year1 == year2) 00915 return l; 00916 start = (year1 < year2) ? year1 : year2; 00917 end = (year2 < year1) ? year1: year2; 00918 for (i = start; i < end; i++) 00919 { 00920 l += (qof_date_isleap(i)) ? 366 : 365; 00921 } 00922 return l; 00923 } 00924 00925 QofTime* 00926 qof_date_to_qtime (const QofDate *qd) 00927 { 00928 QofTime *qt; 00929 QofTimeSecs c; 00930 00931 g_return_val_if_fail (qd, NULL); 00932 g_return_val_if_fail (qd->qd_valid, NULL); 00933 c = 0; 00934 qt = NULL; 00935 if (qd->qd_year < 1970) 00936 { 00937 c = qd->qd_sec; 00938 c += QOF_MIN_TO_SEC(qd->qd_min); 00939 c += QOF_HOUR_TO_SEC(qd->qd_hour); 00940 c += QOF_DAYS_TO_SEC(qd->qd_yday); 00941 c -= QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year)); 00942 c -= qd->qd_gmt_off; 00943 qt = qof_time_set (c, qd->qd_nanosecs); 00944 } 00945 if (qd->qd_year >= 1970) 00946 { 00947 c = qd->qd_sec; 00948 c += QOF_MIN_TO_SEC(qd->qd_min); 00949 c += QOF_HOUR_TO_SEC(qd->qd_hour); 00950 c += QOF_DAYS_TO_SEC(qd->qd_yday); 00951 c += QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year)); 00952 c -= qd->qd_gmt_off; 00953 qt = qof_time_set (c, qd->qd_nanosecs); 00954 } 00955 return qt; 00956 } 00957 00958 QofTime * 00959 qof_date_time_difference (const QofDate * date1, 00960 const QofDate * date2) 00961 { 00962 gint64 days; 00963 QofTime *secs; 00964 00965 secs = qof_time_new (); 00966 days = days_between (date1->qd_year, date2->qd_year); 00967 qof_time_add_secs(secs, QOF_DAYS_TO_SEC(days)); 00968 if (days >= 0) 00969 { 00970 /* positive value, add date2 secs, subtract date1 */ 00971 qof_time_add_secs(secs, -1 * 00972 (QOF_HOUR_TO_SEC(date1->qd_hour) - 00973 QOF_MIN_TO_SEC(date1->qd_min) - 00974 (date1->qd_sec))); 00975 qof_time_add_secs(secs, 00976 QOF_HOUR_TO_SEC(date2->qd_hour) + 00977 QOF_MIN_TO_SEC(date2->qd_min) + 00978 (date2->qd_sec)); 00979 qof_time_set_nanosecs(secs, 00980 (date1->qd_nanosecs - date2->qd_nanosecs)); 00981 } 00982 if (days < 0) 00983 { 00984 /* negative value*/ 00985 qof_time_add_secs (secs, 00986 QOF_HOUR_TO_SEC(date1->qd_hour) - 00987 QOF_MIN_TO_SEC(date1->qd_min) - 00988 (date1->qd_sec)); 00989 qof_time_add_secs (secs, -1 * 00990 (QOF_HOUR_TO_SEC(date2->qd_hour) + 00991 QOF_MIN_TO_SEC(date2->qd_min) + 00992 (date2->qd_sec))); 00993 qof_time_set_nanosecs(secs, 00994 (date2->qd_nanosecs - date1->qd_nanosecs)); 00995 } 00996 return secs; 00997 } 00998 00999 gboolean 01000 qof_date_adddays (QofDate * qd, gint days) 01001 { 01002 g_return_val_if_fail (qd, FALSE); 01003 g_return_val_if_fail (qof_date_valid (qd), FALSE); 01004 qd->qd_mday += days; 01005 return qof_date_valid (qd); 01006 } 01007 01008 gboolean 01009 qof_date_addmonths (QofDate * qd, gint months, 01010 gboolean track_last_day) 01011 { 01012 g_return_val_if_fail (qd, FALSE); 01013 g_return_val_if_fail (qof_date_valid (qd), FALSE); 01014 qd->qd_mon += months % 12; 01015 qd->qd_year += months / 12; 01016 g_return_val_if_fail (qof_date_valid (qd), FALSE); 01017 if (track_last_day && qof_date_is_last_mday (qd)) 01018 { 01019 qd->qd_mday = qof_date_get_mday (qd->qd_mon, 01020 qd->qd_year); 01021 } 01022 return TRUE; 01023 } 01024 01025 inline gboolean 01026 qof_date_set_day_end (QofDate * qd) 01027 { 01028 qd->qd_hour = 23; 01029 qd->qd_min = 59; 01030 qd->qd_sec = 59; 01031 qd->qd_nanosecs = (QOF_NSECS - 1); 01032 return qof_date_valid (qd); 01033 } 01034 01035 inline gboolean 01036 qof_date_set_day_start (QofDate * qd) 01037 { 01038 g_return_val_if_fail (qd, FALSE); 01039 qd->qd_hour = 0; 01040 qd->qd_min = 0; 01041 qd->qd_sec = 0; 01042 qd->qd_nanosecs = G_GINT64_CONSTANT(0); 01043 return qof_date_valid (qd); 01044 } 01045 01046 inline gboolean 01047 qof_date_set_day_middle (QofDate * qd) 01048 { 01049 g_return_val_if_fail (qd, FALSE); 01050 qd->qd_hour = 12; 01051 qd->qd_min = 0; 01052 qd->qd_sec = 0; 01053 qd->qd_nanosecs = G_GINT64_CONSTANT(0); 01054 return qof_date_valid (qd); 01055 } 01056 01057 /******************** END OF FILE *************************/