QOF 0.7.5
|
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; 00415 00416 g_return_val_if_fail (date, NULL); 00417 date->qd_sec -= date->qd_gmt_off; 00418 /* if value is negative, just add */ 00419 if ((date->qd_nanosecs >= QOF_NSECS) || 00420 (date->qd_nanosecs <= -QOF_NSECS)) 00421 { 00422 date->qd_sec += date->qd_nanosecs / QOF_NSECS; 00423 date->qd_nanosecs = date->qd_nanosecs % QOF_NSECS; 00424 if (date->qd_nanosecs < 0) 00425 { 00426 date->qd_nanosecs += QOF_NSECS; 00427 date->qd_sec--; 00428 } 00429 } 00430 if ((date->qd_sec >= 60) || (date->qd_sec <= -60)) 00431 { 00432 date->qd_min += date->qd_sec / 60; 00433 date->qd_sec = date->qd_sec % 60; 00434 if (date->qd_sec < 0) 00435 { 00436 date->qd_sec += 60; 00437 date->qd_min--; 00438 } 00439 } 00440 if ((date->qd_min >= 60) || (date->qd_min <= -60)) 00441 { 00442 date->qd_hour += date->qd_min / 60; 00443 date->qd_min = date->qd_min % 60; 00444 if (date->qd_min < 0) 00445 { 00446 date->qd_min += 60; 00447 date->qd_hour--; 00448 } 00449 } 00450 if ((date->qd_hour >= 24) || (date->qd_hour <= -24)) 00451 { 00452 date->qd_mday += date->qd_hour / 24; 00453 date->qd_hour = date->qd_hour % 24; 00454 if (date->qd_hour < 0) 00455 { 00456 date->qd_hour += 24; 00457 date->qd_mday--; 00458 } 00459 } 00460 if ((date->qd_mon > 12) || (date->qd_mon < -12)) 00461 { 00462 date->qd_year += date->qd_mon / 12; 00463 date->qd_mon = date->qd_mon % 12; 00464 if (date->qd_mon < 0) 00465 { 00466 /* -1 == Dec, -4 == Sep */ 00467 date->qd_mon += 12 + 1; 00468 date->qd_year = (date->qd_year < 0) ? 00469 date->qd_year++ : date->qd_year--; 00470 } 00471 } 00472 /* qd_mon starts at 1, not zero */ 00473 if (date->qd_mon == 0) 00474 date->qd_mon = 1; 00475 /* Year Zero does not exist, 1BC is immediately followed by 1AD. */ 00476 if (date->qd_year == 0) 00477 date->qd_year = -1; 00478 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon]; 00479 while (date->qd_mday < 0) 00480 { 00481 date->qd_mday += days; 00482 date->qd_mon--; 00483 if (date->qd_mon < 1) 00484 { 00485 date->qd_year -= date->qd_mon / 12; 00486 date->qd_mon = date->qd_mon % 12; 00487 /* if year was AD and is now zero, reset to BC. */ 00488 if ((date->qd_year == 0) && (date->qd_mon < 0)) 00489 date->qd_year = -1; 00490 } 00491 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon]; 00492 } 00493 while (date->qd_mday > days) 00494 { 00495 date->qd_mday -= days; 00496 date->qd_mon++; 00497 if (date->qd_mon > 12) 00498 { 00499 date->qd_year += date->qd_mon / 12; 00500 date->qd_mon = date->qd_mon % 12; 00501 /* if year was BC and is now zero, reset to AD. */ 00502 if ((date->qd_year == 0) && (date->qd_mon > 0)) 00503 date->qd_year = +1; 00504 } 00505 days = days_in_months[qof_date_isleap(date->qd_year)][date->qd_mon]; 00506 } 00507 /* use sensible defaults */ 00508 if (date->qd_mday == 0) 00509 date->qd_mday = 1; 00510 if (date->qd_mon == 0) 00511 date->qd_mon = 1; 00512 /* use days_in_year to set yday */ 00513 date->qd_yday = (date->qd_mday - 1) + 00514 days_in_year[qof_date_isleap(date->qd_year)][date->qd_mon]; 00515 set_day_of_the_week (date); 00516 /* qd_year has no realistic limits */ 00517 date->qd_valid = TRUE; 00518 date->qd_zone = "GMT"; 00519 date->qd_is_dst = 0; 00520 date->qd_gmt_off = 0L; 00521 return date; 00522 } 00523 00524 QofDate * 00525 qof_date_parse (const gchar * str, QofDateFormat df) 00526 { 00527 const gchar *format; 00528 QofDateError error; 00529 QofDate *date; 00530 gchar *check; 00531 00532 check = NULL; 00533 error = ERR_NO_ERROR; 00534 date = qof_date_new (); 00535 format = qof_date_format_get_format (df); 00536 check = strptime_internal (str, format, date, &error); 00537 if (error != ERR_NO_ERROR) 00538 { 00539 qof_date_free (date); 00540 return NULL; 00541 } 00542 date = date_normalise (date); 00543 return date; 00544 } 00545 00546 gchar * 00547 qof_date_print (const QofDate * date, QofDateFormat df) 00548 { 00549 size_t result; 00550 gchar temp[MAX_DATE_BUFFER]; 00551 QofDateEntry *d; 00552 00553 g_return_val_if_fail (QofDateInit, NULL); 00554 g_return_val_if_fail (date, NULL); 00555 g_return_val_if_fail (date->qd_valid, NULL); 00556 d = g_hash_table_lookup (DateFormatTable, 00557 GINT_TO_POINTER (df)); 00558 g_return_val_if_fail (d, NULL); 00559 temp[0] = '\1'; 00560 result = strftime_case (FALSE, temp, MAX_DATE_BUFFER, 00561 d->format, date, 1, date->qd_nanosecs); 00562 if (result == 0 && temp[0] != '\0') 00563 { 00564 PERR (" qof extended strftime failed"); 00565 return NULL; 00566 } 00567 return g_strndup(temp, result); 00568 } 00569 00570 /* QofDate handlers */ 00571 00572 QofDate * 00573 qof_date_new (void) 00574 { 00575 QofDate *d; 00576 00577 d = g_new0 (QofDate, 1); 00578 return d; 00579 } 00580 00581 QofDate * 00582 qof_date_get_current (void) 00583 { 00584 QofTime *qt; 00585 QofDate *qd; 00586 00587 qt = qof_time_get_current (); 00588 qd = qof_date_from_qtime (qt); 00589 qof_time_free (qt); 00590 return qd; 00591 } 00592 00593 QofDate * 00594 qof_date_new_dmy (gint day, gint month, gint64 year) 00595 { 00596 QofDate *qd; 00597 00598 qd = g_new0 (QofDate, 1); 00599 qd->qd_mday = day; 00600 qd->qd_mon = month; 00601 qd->qd_year = year; 00602 if(!qof_date_valid (qd)) 00603 return NULL; 00604 return qd; 00605 } 00606 00607 void 00608 qof_date_free (QofDate * date) 00609 { 00610 g_return_if_fail (date); 00611 g_free (date); 00612 date = NULL; 00613 } 00614 00615 gboolean 00616 qof_date_valid (QofDate *date) 00617 { 00618 g_return_val_if_fail (date, FALSE); 00619 date = date_normalise (date); 00620 if (date->qd_valid == FALSE) 00621 { 00622 PERR (" unknown QofDate error"); 00623 return FALSE; 00624 } 00625 return TRUE; 00626 } 00627 00628 gboolean 00629 qof_date_equal (const QofDate *d1, const QofDate *d2) 00630 { 00631 if (0 == qof_date_compare (d1, d2)) 00632 return TRUE; 00633 return FALSE; 00634 } 00635 00636 gint 00637 qof_date_compare (const QofDate * d1, const QofDate * d2) 00638 { 00639 if ((!d1) && (!d2)) 00640 return 0; 00641 if (d1 == d2) 00642 return 0; 00643 if (!d1) 00644 return -1; 00645 if (!d2) 00646 return 1; 00647 if (d1->qd_year < d2->qd_year) 00648 return -1; 00649 if (d1->qd_year > d2->qd_year) 00650 return 1; 00651 if (d1->qd_mon < d2->qd_mon) 00652 return -1; 00653 if (d1->qd_mon > d2->qd_mon) 00654 return 1; 00655 if (d1->qd_mday < d2->qd_mday) 00656 return -1; 00657 if (d1->qd_mday > d2->qd_mday) 00658 return 1; 00659 if (d1->qd_hour < d2->qd_hour) 00660 return -1; 00661 if (d1->qd_hour > d2->qd_hour) 00662 return 1; 00663 if (d1->qd_min < d2->qd_min) 00664 return -1; 00665 if (d1->qd_min > d2->qd_min) 00666 return 1; 00667 if (d1->qd_sec < d2->qd_sec) 00668 return -1; 00669 if (d1->qd_sec > d2->qd_sec) 00670 return 1; 00671 if (d1->qd_nanosecs < d2->qd_nanosecs) 00672 return -1; 00673 if (d1->qd_nanosecs > d2->qd_nanosecs) 00674 return 1; 00675 return 0; 00676 } 00677 00678 QofDate * 00679 qof_date_from_struct_tm (const struct tm *stm) 00680 { 00681 QofDate *d; 00682 00683 g_return_val_if_fail (stm, NULL); 00684 d = g_new0 (QofDate, 1); 00685 d->qd_sec = stm->tm_sec; 00686 d->qd_min = stm->tm_min; 00687 d->qd_hour = stm->tm_hour; 00688 d->qd_mday = stm->tm_mday; 00689 d->qd_mon = stm->tm_mon + 1; 00690 d->qd_year = stm->tm_year + 1900; 00691 d->qd_wday = stm->tm_wday; 00692 d->qd_yday = stm->tm_yday; 00693 d->qd_is_dst = stm->tm_isdst; 00694 d->qd_gmt_off = stm->tm_gmtoff; 00695 d->qd_zone = stm->tm_zone; 00696 d->qd_valid = TRUE; 00697 d = date_normalise(d); 00698 return d; 00699 } 00700 00701 gboolean 00702 qof_date_to_struct_tm (const QofDate * qd, struct tm * stm, 00703 glong *nanosecs) 00704 { 00705 g_return_val_if_fail (qd, FALSE); 00706 g_return_val_if_fail (stm, FALSE); 00707 g_return_val_if_fail (qd->qd_valid, FALSE); 00708 if ((qd->qd_year > G_MAXINT) || (qd->qd_year < 1900)) 00709 { 00710 PERR (" date too large for struct tm"); 00711 return FALSE; 00712 } 00713 stm->tm_sec = qd->qd_sec; 00714 stm->tm_min = qd->qd_min; 00715 stm->tm_hour = qd->qd_hour; 00716 stm->tm_mday = qd->qd_mday; 00717 stm->tm_mon = qd->qd_mon - 1; 00718 stm->tm_year = qd->qd_year - 1900; 00719 stm->tm_wday = qd->qd_wday; 00720 stm->tm_yday = qd->qd_yday; 00721 stm->tm_isdst = qd->qd_is_dst; 00722 stm->tm_gmtoff = qd->qd_gmt_off; 00723 stm->tm_zone = qd->qd_zone; 00724 if (nanosecs != NULL) 00725 *nanosecs = qd->qd_nanosecs; 00726 return TRUE; 00727 } 00728 00729 gboolean 00730 qof_date_to_gdate (const QofDate *qd, GDate *gd) 00731 { 00732 g_return_val_if_fail (qd, FALSE); 00733 g_return_val_if_fail (gd, FALSE); 00734 g_return_val_if_fail (qd->qd_valid, FALSE); 00735 if (qd->qd_year >= G_MAXUINT16) 00736 { 00737 PERR (" QofDate out of range of GDate"); 00738 return FALSE; 00739 } 00740 if (!g_date_valid_dmy (qd->qd_mday, qd->qd_mon, qd->qd_year)) 00741 { 00742 PERR (" GDate failed to allow day, month and/or year"); 00743 return FALSE; 00744 } 00745 g_date_set_dmy (gd, qd->qd_mday, qd->qd_mon, qd->qd_year); 00746 return TRUE; 00747 } 00748 00749 QofDate * 00750 qof_date_from_gdate (const GDate *date) 00751 { 00752 QofDate * qd; 00753 00754 g_return_val_if_fail (g_date_valid (date), NULL); 00755 qd = qof_date_new (); 00756 qd->qd_year = g_date_get_year (date); 00757 qd->qd_mon = g_date_get_month (date); 00758 qd->qd_mday = g_date_get_day (date); 00759 qd = date_normalise (qd); 00760 return qd; 00761 } 00762 00763 static void 00764 qof_date_offset (const QofTime *time, glong offset, QofDate *qd) 00765 { 00766 glong days; 00767 gint64 rem, y, yg; 00768 const guint16 *ip; 00769 QofTimeSecs t; 00770 00771 g_return_if_fail (qd); 00772 g_return_if_fail (time); 00773 t = qof_time_get_secs ((QofTime*)time); 00774 days = t / SECS_PER_DAY; 00775 rem = t % SECS_PER_DAY; 00776 rem += offset; 00777 while (rem < 0) 00778 { 00779 rem += SECS_PER_DAY; 00780 --days; 00781 } 00782 while (rem >= SECS_PER_DAY) 00783 { 00784 rem -= SECS_PER_DAY; 00785 ++days; 00786 } 00787 qd->qd_hour = rem / SECS_PER_HOUR; 00788 rem %= SECS_PER_HOUR; 00789 qd->qd_min = rem / 60; 00790 qd->qd_sec = rem % 60; 00791 /* January 1, 1970 was a Thursday. */ 00792 qd->qd_wday = (4 + days) % 7; 00793 if (qd->qd_wday < 0) 00794 qd->qd_wday += 7; 00795 y = 1970; 00796 while (days < 0 || days >= (__isleap (y) ? 366 : 365)) 00797 { 00798 /* Guess a corrected year, assuming 365 days per year. */ 00799 yg = y + days / 365 - (days % 365 < 0); 00800 /* Adjust DAYS and Y to match the guessed year. */ 00801 days -= ((yg - y) * 365 00802 + LEAPS_THRU_END_OF (yg - 1) 00803 - LEAPS_THRU_END_OF (y - 1)); 00804 y = yg; 00805 } 00806 qd->qd_year = y; 00807 qd->qd_yday = days; 00808 ip = days_in_year[qof_date_isleap(y)]; 00809 for (y = 12; days < (glong) ip[y]; --y) 00810 continue; 00811 days -= ip[y]; 00812 qd->qd_mon = y; 00813 qd->qd_mday = days + 1; 00814 } 00815 00816 /* safe to use time_t here because only values 00817 within the range of a time_t have any leapseconds. */ 00818 static gint 00819 count_leapseconds (time_t interval) 00820 { 00821 time_t altered; 00822 struct tm utc; 00823 00824 altered = interval; 00825 utc = *gmtime_r (&interval, &utc); 00826 altered = mktime (&utc); 00827 return altered - interval; 00828 } 00829 00830 /*static inline gint*/ 00831 static gint 00832 extract_interval (const QofTime *qt) 00833 { 00834 gint leap_seconds; 00835 QofTimeSecs t, l; 00836 const QofTime *now; 00837 00838 leap_seconds = 0; 00839 t = qof_time_get_secs (qt); 00840 now = qof_time_get_current (); 00841 l = (qof_time_get_secs (now) > G_MAXINT32) ? 00842 G_MAXINT32 : qof_time_get_secs (now); 00843 leap_seconds = ((t > l) || (t < 0)) ? 00844 count_leapseconds (l) : 00845 count_leapseconds (t); 00846 return leap_seconds; 00847 } 00848 00849 QofDate * 00850 qof_date_from_qtime (const QofTime *qt) 00851 { 00852 QofDate *qd; 00853 gint leap_extra_secs; 00854 00855 /* may not want to create a new time or date - it 00856 complicates memory management. */ 00857 g_return_val_if_fail (qt, NULL); 00858 g_return_val_if_fail (qof_time_is_valid (qt), NULL); 00859 qd = qof_date_new (); 00860 leap_extra_secs = 0; 00861 setenv ("TZ", "GMT", 1); 00862 tzset(); 00863 leap_extra_secs = extract_interval (qt); 00864 qof_date_offset (qt, leap_extra_secs, qd); 00865 qd->qd_nanosecs = qof_time_get_nanosecs (qt); 00866 qd->qd_is_dst = 0; 00867 qd->qd_zone = "GMT"; 00868 qd->qd_gmt_off = 0L; 00869 if (!qof_date_valid(qd)) 00870 return NULL; 00871 return qd; 00872 } 00873 00874 gint64 00875 days_between (gint64 year1, gint64 year2) 00876 { 00877 gint64 i, start, end, l; 00878 00879 l = 0; 00880 if (year1 == year2) 00881 return l; 00882 start = (year1 < year2) ? year1 : year2; 00883 end = (year2 < year1) ? year1: year2; 00884 for (i = start; i < end; i++) 00885 { 00886 l += (qof_date_isleap(i)) ? 366 : 365; 00887 } 00888 return l; 00889 } 00890 00891 QofTime* 00892 qof_date_to_qtime (const QofDate *qd) 00893 { 00894 QofTime *qt; 00895 QofTimeSecs c; 00896 00897 g_return_val_if_fail (qd, NULL); 00898 g_return_val_if_fail (qd->qd_valid, NULL); 00899 c = 0; 00900 qt = NULL; 00901 if (qd->qd_year < 1970) 00902 { 00903 c = qd->qd_sec; 00904 c += QOF_MIN_TO_SEC(qd->qd_min); 00905 c += QOF_HOUR_TO_SEC(qd->qd_hour); 00906 c += QOF_DAYS_TO_SEC(qd->qd_yday); 00907 c -= QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year)); 00908 c -= qd->qd_gmt_off; 00909 qt = qof_time_set (c, qd->qd_nanosecs); 00910 } 00911 if (qd->qd_year >= 1970) 00912 { 00913 c = qd->qd_sec; 00914 c += QOF_MIN_TO_SEC(qd->qd_min); 00915 c += QOF_HOUR_TO_SEC(qd->qd_hour); 00916 c += QOF_DAYS_TO_SEC(qd->qd_yday); 00917 c += QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year)); 00918 c -= qd->qd_gmt_off; 00919 qt = qof_time_set (c, qd->qd_nanosecs); 00920 } 00921 return qt; 00922 } 00923 00924 QofTime * 00925 qof_date_time_difference (const QofDate * date1, 00926 const QofDate * date2) 00927 { 00928 gint64 days; 00929 QofTime *secs; 00930 00931 secs = qof_time_new (); 00932 days = days_between (date1->qd_year, date2->qd_year); 00933 qof_time_add_secs(secs, QOF_DAYS_TO_SEC(days)); 00934 if (days >= 0) 00935 { 00936 /* positive value, add date2 secs, subtract date1 */ 00937 qof_time_add_secs(secs, -1 * 00938 (QOF_HOUR_TO_SEC(date1->qd_hour) - 00939 QOF_MIN_TO_SEC(date1->qd_min) - 00940 (date1->qd_sec))); 00941 qof_time_add_secs(secs, 00942 QOF_HOUR_TO_SEC(date2->qd_hour) + 00943 QOF_MIN_TO_SEC(date2->qd_min) + 00944 (date2->qd_sec)); 00945 qof_time_set_nanosecs(secs, 00946 (date1->qd_nanosecs - date2->qd_nanosecs)); 00947 } 00948 if (days < 0) 00949 { 00950 /* negative value*/ 00951 qof_time_add_secs (secs, 00952 QOF_HOUR_TO_SEC(date1->qd_hour) - 00953 QOF_MIN_TO_SEC(date1->qd_min) - 00954 (date1->qd_sec)); 00955 qof_time_add_secs (secs, -1 * 00956 (QOF_HOUR_TO_SEC(date2->qd_hour) + 00957 QOF_MIN_TO_SEC(date2->qd_min) + 00958 (date2->qd_sec))); 00959 qof_time_set_nanosecs(secs, 00960 (date2->qd_nanosecs - date1->qd_nanosecs)); 00961 } 00962 return secs; 00963 } 00964 00965 gboolean 00966 qof_date_adddays (QofDate * qd, gint days) 00967 { 00968 g_return_val_if_fail (qd, FALSE); 00969 g_return_val_if_fail (qof_date_valid (qd), FALSE); 00970 qd->qd_mday += days; 00971 return qof_date_valid (qd); 00972 } 00973 00974 gboolean 00975 qof_date_addmonths (QofDate * qd, gint months, 00976 gboolean track_last_day) 00977 { 00978 g_return_val_if_fail (qd, FALSE); 00979 g_return_val_if_fail (qof_date_valid (qd), FALSE); 00980 qd->qd_mon += months % 12; 00981 qd->qd_year += months / 12; 00982 g_return_val_if_fail (qof_date_valid (qd), FALSE); 00983 if (track_last_day && qof_date_is_last_mday (qd)) 00984 { 00985 qd->qd_mday = qof_date_get_mday (qd->qd_mon, 00986 qd->qd_year); 00987 } 00988 return TRUE; 00989 } 00990 00991 inline gboolean 00992 qof_date_set_day_end (QofDate * qd) 00993 { 00994 qd->qd_hour = 23; 00995 qd->qd_min = 59; 00996 qd->qd_sec = 59; 00997 qd->qd_nanosecs = (QOF_NSECS - 1); 00998 return qof_date_valid (qd); 00999 } 01000 01001 inline gboolean 01002 qof_date_set_day_start (QofDate * qd) 01003 { 01004 g_return_val_if_fail (qd, FALSE); 01005 qd->qd_hour = 0; 01006 qd->qd_min = 0; 01007 qd->qd_sec = 0; 01008 qd->qd_nanosecs = G_GINT64_CONSTANT(0); 01009 return qof_date_valid (qd); 01010 } 01011 01012 inline gboolean 01013 qof_date_set_day_middle (QofDate * qd) 01014 { 01015 g_return_val_if_fail (qd, FALSE); 01016 qd->qd_hour = 12; 01017 qd->qd_min = 0; 01018 qd->qd_sec = 0; 01019 qd->qd_nanosecs = G_GINT64_CONSTANT(0); 01020 return qof_date_valid (qd); 01021 } 01022 01023 /******************** END OF FILE *************************/