28 #include <glib/gprintf.h> 32 #include "qofdate-p.h" 35 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) 36 #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) 38 static GHashTable *DateFormatTable = NULL;
39 static gboolean QofDateInit = FALSE;
41 static gchar locale_separator =
'\0';
45 static const guint16 days_in_year[2][14] =
47 { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
48 { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
50 static const guint8 days_in_months[2][13] =
52 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
53 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
57 typedef struct QofDateEntry_s
63 gboolean locale_specific;
71 DateFormatTable = g_hash_table_new (g_direct_hash, g_direct_equal);
74 QofDateEntry *d = g_new0 (QofDateEntry, 1);
75 d->format =
"%m/%d/%Y";
79 d->locale_specific = FALSE;
80 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
83 QofDateEntry *d = g_new0 (QofDateEntry, 1);
84 d->format =
"%d/%m/%Y";
88 d->locale_specific = FALSE;
89 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
92 QofDateEntry *d = g_new0 (QofDateEntry, 1);
93 d->format =
"%d.%m.%Y";
97 d->locale_specific = FALSE;
98 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
101 QofDateEntry *d = g_new0 (QofDateEntry, 1);
106 d->locale_specific = FALSE;
107 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
110 QofDateEntry *d = g_new0 (QofDateEntry, 1);
115 d->locale_specific = FALSE;
116 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
119 QofDateEntry *d = g_new0 (QofDateEntry, 1);
122 d->separator = locale_separator;
124 d->locale_specific = TRUE;
125 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
128 QofDateEntry *d = g_new0 (QofDateEntry, 1);
131 d->separator = locale_separator;
133 d->locale_specific = TRUE;
134 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
137 QofDateEntry *d = g_new0(QofDateEntry,1);
138 d->format =
"%Y-%m-%d %H:%M:%S.%N %z";
142 d->locale_specific = FALSE;
143 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER(d->df), d);
149 hash_value_free (gpointer key __attribute__ ((unused)), gpointer value,
150 gpointer data __attribute__ ((unused)))
160 g_hash_table_foreach (DateFormatTable, hash_value_free, NULL);
161 g_hash_table_destroy (DateFormatTable);
171 g_return_val_if_fail (mday != 0, 0);
172 g_return_val_if_fail (month != 0, 0);
173 g_return_val_if_fail (month <= 12, 0);
174 g_return_val_if_fail (month >= 1, 0);
175 g_return_val_if_fail (year != 0, 0);
177 g_return_val_if_fail (mday <=
179 return days_in_year[leap][month] + mday;
185 g_return_val_if_fail (month != 0, 0);
186 g_return_val_if_fail (month <= 12, 0);
187 g_return_val_if_fail (month >= 1, 0);
188 g_return_val_if_fail (year != 0, 0);
195 g_return_val_if_fail (qd, FALSE);
196 g_return_val_if_fail (qd->
qd_valid, FALSE);
210 g_return_val_if_fail (QofDateInit, FALSE);
211 g_return_val_if_fail (str, FALSE);
212 g_return_val_if_fail (strlen (str) != 0, FALSE);
214 ENTER (
" str=%s", str);
217 LEAVE (
" '%s' is too long! Max=%d str_len=%d",
224 check = *gmtime_r (&now, &check);
228 if (len == 0 && test[0] !=
'\0')
230 LEAVE (
" strftime could not understand '%s'", str);
236 LEAVE (
" %s creates a string '%s' that is too long!" 240 *identifier = g_hash_table_size (DateFormatTable) + 1;
242 QofDateEntry *d = g_new0 (QofDateEntry, 1);
245 d->separator = locale_separator;
247 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (d->df), d);
249 LEAVE (
" successful");
258 g_return_val_if_fail (QofDateInit, NULL);
259 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (format));
262 PERR (
" unknown format: '%d'", format);
273 g_return_val_if_fail (QofDateInit, FALSE);
276 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (format));
279 PERR (
" unknown format: '%d'", format);
283 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (format), d);
298 g_return_val_if_fail (QofDateInit, FALSE);
299 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
302 PERR (
" unknown format: '%d'", df);
314 g_return_val_if_fail (QofDateInit, NULL);
315 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
318 PERR (
" unknown format: '%d'", df);
329 g_return_val_if_fail (QofDateInit, locale_separator);
330 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
333 PERR (
" unknown format: '%d'", df);
334 return locale_separator;
344 g_return_val_if_fail (QofDateInit, FALSE);
347 DEBUG (
" Prevented attempt to override a default format");
350 if (g_ascii_isdigit (sep))
352 d = g_hash_table_lookup (DateFormatTable, GINT_TO_POINTER (df));
355 PERR (
" unknown format: '%d'", df);
359 g_hash_table_insert (DateFormatTable, GINT_TO_POINTER (df), d);
370 lookup_name (gpointer key __attribute__ ((unused)), gpointer value,
376 i = (
struct iter *) data;
377 d = (QofDateEntry *) value;
407 g_hash_table_foreach (DateFormatTable, lookup_name, &i);
412 date_normalise (
QofDate * date)
416 g_return_val_if_fail (date, NULL);
478 while (date->
qd_mday < (leap*-1))
546 set_day_of_the_week (date);
562 gchar * G_GNUC_UNUSED check;
565 error = ERR_NO_ERROR;
568 check = strptime_internal (str, format, date, &error);
570 if (error != ERR_NO_ERROR)
576 date = date_normalise (date);
587 g_return_val_if_fail (QofDateInit, NULL);
588 g_return_val_if_fail (date, NULL);
589 g_return_val_if_fail (date->
qd_valid, NULL);
590 d = g_hash_table_lookup (DateFormatTable,
591 GINT_TO_POINTER (df));
592 g_return_val_if_fail (d, NULL);
596 if (result == 0 && temp[0] !=
'\0')
598 PERR (
" qof extended strftime failed");
601 return g_strndup(temp, result);
644 g_return_if_fail (date);
652 g_return_val_if_fail (date, FALSE);
653 date = date_normalise (date);
656 PERR (
" unknown QofDate error");
717 g_return_val_if_fail (stm, NULL);
723 d->
qd_mon = stm->tm_mon + 1;
724 d->
qd_year = stm->tm_year + 1900;
731 d = date_normalise(d);
739 g_return_val_if_fail (qd, FALSE);
740 g_return_val_if_fail (stm, FALSE);
741 g_return_val_if_fail (qd->
qd_valid, FALSE);
744 PERR (
" date too large for struct tm");
751 stm->tm_mon = qd->
qd_mon - 1;
752 stm->tm_year = qd->
qd_year - 1900;
758 if (nanosecs != NULL)
766 g_return_val_if_fail (qd, FALSE);
767 g_return_val_if_fail (gd, FALSE);
768 g_return_val_if_fail (qd->
qd_valid, FALSE);
769 if (qd->
qd_year >= G_MAXUINT16)
771 PERR (
" QofDate out of range of GDate");
776 PERR (
" GDate failed to allow day, month and/or year");
788 g_return_val_if_fail (g_date_valid (date), NULL);
790 qd->
qd_year = g_date_get_year (date);
791 qd->
qd_mon = g_date_get_month (date);
792 qd->
qd_mday = g_date_get_day (date);
793 qd = date_normalise (qd);
805 g_return_if_fail (qd);
806 g_return_if_fail (time);
830 while (days < 0 || days >= (__isleap (y) ? 366 : 365))
833 yg = y + days / 365 - (days % 365 < 0);
835 days -= ((yg - y) * 365
836 + LEAPS_THRU_END_OF (yg - 1)
837 - LEAPS_THRU_END_OF (y - 1));
843 for (y = 12; days < (glong) ip[y]; --y)
853 count_leapseconds (time_t interval)
859 utc = *gmtime_r (&interval, &utc);
860 altered = mktime (&utc);
861 return altered - interval;
866 extract_interval (
const QofTime *qt)
877 leap_seconds = ((t > l) || (t < 0)) ?
878 count_leapseconds (l) :
879 count_leapseconds (t);
887 gint leap_extra_secs;
891 g_return_val_if_fail (qt, NULL);
892 g_return_val_if_fail (qof_time_is_valid (qt), NULL);
895 setenv (
"TZ",
"GMT", 1);
897 leap_extra_secs = extract_interval (qt);
898 qof_date_offset (qt, leap_extra_secs, qd);
909 days_between (gint64 year1, gint64 year2)
911 gint64 i, start, end, l;
916 start = (year1 < year2) ? year1 : year2;
917 end = (year2 < year1) ? year1: year2;
918 for (i = start; i < end; i++)
931 g_return_val_if_fail (qd, NULL);
932 g_return_val_if_fail (qd->
qd_valid, NULL);
1002 g_return_val_if_fail (qd, FALSE);
1010 gboolean track_last_day)
1012 g_return_val_if_fail (qd, FALSE);
1014 qd->
qd_mon += months % 12;
1026 qof_date_set_day_end (
QofDate * qd)
1036 qof_date_set_day_start (
QofDate * qd)
1038 g_return_val_if_fail (qd, FALSE);
1047 qof_date_set_day_middle (
QofDate * qd)
1049 g_return_val_if_fail (qd, FALSE);
gint64 qd_year
Extended version to cope with full range of dates.
#define MAX_DATE_BUFFER
The maximum length of a QofDate buffer.
#define QOF_DATE_FORMAT_UTC
QOF UTC format, xsd:date compatible. QOF_UTC_DATE_FORMAT "%Y-%m-%dT%H:%M:%SZ".
#define PERR(format, args...)
gboolean qof_date_format_set_current(QofDateFormat df)
Selects one registered date format as the current default.
#define QOF_DATE_FORMAT_ISO
Short ISO form. "%F".
gboolean qd_valid
If the QofDate is valid or merely initialised.
gboolean qof_date_adddays(QofDate *qd, gint days)
Add a number of days to a QofDate and normalise.
void qof_date_free(QofDate *date)
#define QOF_UTC_DATE_FORMAT
UTC date format string.
QofDate * qof_date_from_qtime(const QofTime *qt)
gboolean qof_date_format_set_name(const gchar *name, QofDateFormat format)
Set a shorthand name for a custom date format.
QofDateFormat qof_date_format_from_name(const gchar *name)
Returns the default date format for a known shorthand name.
const gchar * qd_zone
Calculated value based on struct tm.tm_zone.
QofDate * qof_date_get_current(void)
guint8 qof_date_get_mday(gint month, gint64 year)
void qof_date_close(void)
close down the QofDate tables
QofTimeSecs qof_time_get_secs(const QofTime *qt)
Get the number of seconds.
gchar * qof_date_print(const QofDate *date, QofDateFormat df)
Convert a QofDate to a timestamp according to the specified date format.
gboolean qof_date_format_set_date_separator(const gchar sep, QofDateFormat df)
Set a locale-specific separator.
QofTime * qof_time_new(void)
create an empty QofTime
QofDateFormat qof_date_format_get_current(void)
returns the current date format.
glong qof_time_get_nanosecs(const QofTime *qt)
Get the number of seconds.
const gchar * qof_date_format_get_format(QofDateFormat df)
Retrieve the strftime format string for a registered date format.
#define LEAVE(format, args...)
QofTime * qof_date_to_qtime(const QofDate *qd)
Full range replacement for struct tm.
gchar qof_date_format_get_date_separator(QofDateFormat df)
Return the field separator for the current date format.
QofTime * qof_time_get_current(void)
Get the current QofTime.
void qof_date_init(void)
initialise the QofDate tables
QofDate * qof_date_parse(const gchar *str, QofDateFormat df)
Convert a timestamp to a QofTime.
#define QOF_DATE_FORMAT_CE
Contintental European default. "%d.%m.%Y".
glong qd_hour
Signed replacement of struct tm.tm_hour.
void qof_time_free(QofTime *qt)
Free a QofTime when no longer required.
glong qd_min
Signed replacement of struct tm.tm_min.
glong qd_mon
Signed replacement of struct tm.tm_mon.
guint16 qof_date_get_yday(gint mday, gint month, gint64 year)
void qof_time_add_secs(QofTime *qt, QofTimeSecs secs)
Add (or subtract) seconds from a QofTime.
#define DEBUG(format, args...)
#define qof_date_isleap(year)
gboolean qof_date_to_struct_tm(const QofDate *qd, struct tm *stm, glong *nanosecs)
Convert a QofDate to a struct tm.
#define QOF_HOUR_TO_SEC(x)
QofDate * qof_date_new_dmy(gint day, gint month, gint64 year)
#define QOF_DATE_FORMAT_ISO8601
#define QOF_DATE_FORMAT_UK
United Kingdom default. "%d/%m/%Y".
#define QOF_DATE_FORMAT_LOCALE
GNU locale default. "%x".
void qof_time_set_nanosecs(QofTime *qt, glong nano)
Set the number of seconds.
#define MAX_DATE_LENGTH
The maximum length of a string used for or created by dates.
glong qd_gmt_off
Calculated value based on struct tm.tm_gmtoff.
QofTime * qof_time_set(QofTimeSecs t, glong nanosecs)
gboolean qof_date_equal(const QofDate *d1, const QofDate *d2)
gboolean qof_date_addmonths(QofDate *qd, gint months, gboolean track_last_day)
gboolean qof_date_to_gdate(const QofDate *qd, GDate *gd)
Convert a QofDate to a GDate.
const gchar * qof_date_format_to_name(QofDateFormat format)
Retrieve the shorthand name for the selected date format.
gint64 QofTimeSecs
Replacement for time_t.
#define QOF_DATE_FORMAT_CUSTOM
Date and time for the current locale "%c".
struct QofTime64 QofTime
Use a 64-bit signed int QofTime.
#define QOF_MIN_TO_SEC(x)
QofDate * qof_date_new(void)
QofTime * qof_date_time_difference(const QofDate *date1, const QofDate *date2)
gint qof_date_compare(const QofDate *d1, const QofDate *d2)
gint safe_strcmp(const gchar *da, const gchar *db)
gboolean qof_date_is_last_mday(const QofDate *qd)
#define QOF_DAYS_TO_SEC(x)
glong qd_mday
Signed replacement of struct tm.tm_mday.
QofDate * qof_date_from_gdate(const GDate *date)
Create a QofDate from a GDate.
#define ENTER(format, args...)
gboolean qof_date_format_add(const gchar *str, QofDateFormat *identifier)
Add a specific strftime compatible string as a new QofDateFormat.
#define QOF_DATE_FORMAT_US
Continental US default. "%m/%d/%Y".
gboolean qof_date_valid(QofDate *date)
Validate a QofDate.
const gchar * QofLogModule
QofDate * qof_date_from_struct_tm(const struct tm *stm)
Convert a struct tm to a QofDate.