qofdate.c

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

Generated on Mon May 21 17:42:21 2007 for QOF by  doxygen 1.5.1