QOF
0.8.0
|
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 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 /* if year was AD and is now zero, reset to BC. */ 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 /* if year was BC and is now zero, reset to AD. */ 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 /* use sensible defaults */ 00531 if (date->qd_mday == 0) 00532 date->qd_mday = 1; 00533 if (date->qd_mon == 0) 00534 date->qd_mon = 1; 00535 /* use days_in_year to set yday */ 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 /* qd_year has no realistic limits */ 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 /* QofDate handlers */ 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 /* January 1, 1970 was a Thursday. */ 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 /* Guess a corrected year, assuming 365 days per year. */ 00824 yg = y + days / 365 - (days % 365 < 0); 00825 /* Adjust DAYS and Y to match the guessed year. */ 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 /* safe to use time_t here because only values 00842 within the range of a time_t have any leapseconds. */ 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 /*static inline gint*/ 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 /* may not want to create a new time or date - it 00881 complicates memory management. */ 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 /* positive value, add date2 secs, subtract date1 */ 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 /* negative value*/ 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 /******************** END OF FILE *************************/