diff --git a/ChangeLog b/ChangeLog index 9d62e9722..ebff72afe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2008-06-18 Matthias Clasen + + Bug 537635 – Corrections and improvements to + g_time_val_from_iso8601()/g_time_val_to_iso8601() + + * glib/gtimer.c (g_time_val_from_iso8601): set tv_usec to 0 rather + than 1 when a fraction of a second is not specified + (g_time_val_from_iso8601): calculate a fraction of a second + correctly even in case it does not happen to consist of exactly + six digits; do not allow random data after the ISO 8601 string, + only whitespace + (make g_time_val_to_iso8601): support fractions of a second + Patch by Peter Kjellerstedt + + * tests/testglib.c: Update to match + 2008-06-16 Christian Persch * glib/gbookmarkfile.c diff --git a/glib/gtimer.c b/glib/gtimer.c index a29e39dd2..b75730ac4 100644 --- a/glib/gtimer.c +++ b/glib/gtimer.c @@ -287,7 +287,7 @@ mktime_utc (struct tm *tm) /** * g_time_val_from_iso8601: - * @iso_date: a ISO 8601 encoded date string + * @iso_date: an ISO 8601 encoded date string * @time_: a #GTimeVal * * Converts a string containing an ISO 8601 encoded date and time @@ -365,10 +365,18 @@ g_time_val_from_iso8601 (const gchar *iso_date, } time_->tv_sec = mktime_utc (&tm); - time_->tv_usec = 1; + time_->tv_usec = 0; if (*iso_date == '.') - time_->tv_usec = strtoul (iso_date + 1, (char **)&iso_date, 10); + { + glong mul = 100000; + + while (g_ascii_isdigit (*++iso_date)) + { + time_->tv_usec += (*iso_date - '0') * mul; + mul /= 10; + } + } if (*iso_date == '+' || *iso_date == '-') { @@ -377,24 +385,29 @@ g_time_val_from_iso8601 (const gchar *iso_date, val = 60 * strtoul (iso_date + 1, (char **)&iso_date, 10); if (*iso_date == ':') - val = 60 * val + strtoul (iso_date + 1, NULL, 10); + val = 60 * val + strtoul (iso_date + 1, (char **)&iso_date, 10); else val = 60 * (val / 100) + (val % 100); time_->tv_sec += (time_t) (val * sign); } + else if (*iso_date++ != 'Z') + return FALSE; - return TRUE; + while (g_ascii_isspace (*iso_date)) + iso_date++; + + return *iso_date == '\0'; } /** * g_time_val_to_iso8601: * @time_: a #GTimeVal * - * Converts @time_ into a ISO 8601 encoded string, relative to the + * Converts @time_ into an ISO 8601 encoded string, relative to the * Coordinated Universal Time (UTC). * - * Return value: a newly allocated string containing a ISO 8601 date + * Return value: a newly allocated string containing an ISO 8601 date * * Since: 2.12 */ @@ -402,24 +415,36 @@ gchar * g_time_val_to_iso8601 (GTimeVal *time_) { gchar *retval; + struct tm *tm; #ifdef HAVE_GMTIME_R struct tm tm_; #endif g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL); -#define ISO_8601_LEN 21 -#define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ" - retval = g_new0 (gchar, ISO_8601_LEN + 1); - - strftime (retval, ISO_8601_LEN, - ISO_8601_FORMAT, #ifdef HAVE_GMTIME_R - gmtime_r (&(time_->tv_sec), &tm_) + tm = gmtime_r (&time_->tv_sec, &tm_); #else - gmtime (&(time_->tv_sec)) + tm = gmtime (&time_->tv_sec); #endif - ); + + if (time_->tv_usec != 0) + { +#define ISO_8601_FRAC_LEN 28 +#define ISO_8601_FRAC_FORMAT "%%Y-%%m-%%dT%%H:%%M:%%S.%06ldZ" + gchar *format = g_strdup_printf (ISO_8601_FRAC_FORMAT, time_->tv_usec); + + retval = g_new0 (gchar, ISO_8601_FRAC_LEN + 1); + strftime (retval, ISO_8601_FRAC_LEN, format, tm); + g_free (format); + } + else + { +#define ISO_8601_LEN 21 +#define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ" + retval = g_new0 (gchar, ISO_8601_LEN + 1); + strftime (retval, ISO_8601_LEN, ISO_8601_FORMAT, tm); + } return retval; } diff --git a/tests/testglib.c b/tests/testglib.c index 4f84adc98..f14158f38 100644 --- a/tests/testglib.c +++ b/tests/testglib.c @@ -1383,36 +1383,70 @@ various_string_tests (void) g_free (tmp_string); g_free (string); -#define REF_INVALID "Wed Dec 19 17:20:20 GMT 2007" -#define REF_SEC_UTC 320063760 -#define REF_STR_UTC "1980-02-22T10:36:00Z" -#define REF_STR_CEST "1980-02-22T12:36:00+02:00" -#define REF_STR_EST "1980-02-22T05:36:00-05:00" +#define REF_INVALID1 "Wed Dec 19 17:20:20 GMT 2007" +#define REF_INVALID2 "1980-02-22T10:36:00Zulu" +#define REF_SEC_UTC 320063760 +#define REF_STR_UTC "1980-02-22T10:36:00Z" +#define REF_STR_CEST "1980-02-22T12:36:00+02:00" +#define REF_STR_EST "19800222T053600-0500" +#define REF_USEC_UTC 50000 +#define REF_STR_USEC_UTC "1980-02-22T10:36:00.050000Z" +#define REF_STR_USEC_CEST "19800222T123600.050000000+0200" +#define REF_STR_USEC_EST "1980-02-22T05:36:00.05-05:00" if (g_test_verbose()) g_print ("checking g_time_val_from_iso8601...\n"); ref_date.tv_sec = REF_SEC_UTC; ref_date.tv_usec = 0; - g_assert (g_time_val_from_iso8601 (REF_INVALID, &date) == FALSE); + g_assert (g_time_val_from_iso8601 (REF_INVALID1, &date) == FALSE); + g_assert (g_time_val_from_iso8601 (REF_INVALID2, &date) == FALSE); g_assert (g_time_val_from_iso8601 (REF_STR_UTC, &date) != FALSE); if (g_test_verbose()) - g_print ("\t=> UTC stamp = %ld (should be: %ld) (%ld off)\n", date.tv_sec, ref_date.tv_sec, date.tv_sec - ref_date.tv_sec); - g_assert (date.tv_sec == ref_date.tv_sec); + g_print ("\t=> UTC stamp = %ld.%06ld (should be: %ld.%06ld) (%ld.%06ld off)\n", + date.tv_sec, date.tv_usec, ref_date.tv_sec, ref_date.tv_usec, + date.tv_sec - ref_date.tv_sec, date.tv_usec - ref_date.tv_usec); + g_assert (date.tv_sec == ref_date.tv_sec && date.tv_usec == ref_date.tv_usec); g_assert (g_time_val_from_iso8601 (REF_STR_CEST, &date) != FALSE); if (g_test_verbose()) - g_print ("\t=> CEST stamp = %ld (should be: %ld) (%ld off)\n", date.tv_sec, ref_date.tv_sec, date.tv_sec - ref_date.tv_sec); - g_assert (date.tv_sec == ref_date.tv_sec); + g_print ("\t=> CEST stamp = %ld.%06ld (should be: %ld.%06ld) (%ld.%06ld off)\n", + date.tv_sec, date.tv_usec, ref_date.tv_sec, ref_date.tv_usec, + date.tv_sec - ref_date.tv_sec, date.tv_usec - ref_date.tv_usec); + g_assert (date.tv_sec == ref_date.tv_sec && date.tv_usec == ref_date.tv_usec); g_assert (g_time_val_from_iso8601 (REF_STR_EST, &date) != FALSE); if (g_test_verbose()) - g_print ("\t=> EST stamp = %ld (should be: %ld) (%ld off)\n", date.tv_sec, ref_date.tv_sec, date.tv_sec - ref_date.tv_sec); - g_assert (date.tv_sec == ref_date.tv_sec); + g_print ("\t=> EST stamp = %ld.%06ld (should be: %ld.%06ld) (%ld.%06ld off)\n", + date.tv_sec, date.tv_usec, ref_date.tv_sec, ref_date.tv_usec, + date.tv_sec - ref_date.tv_sec, date.tv_usec - ref_date.tv_usec); + g_assert (date.tv_sec == ref_date.tv_sec && date.tv_usec == ref_date.tv_usec); + + ref_date.tv_usec = REF_USEC_UTC; + g_assert (g_time_val_from_iso8601 (REF_STR_USEC_UTC, &date) != FALSE); + if (g_test_verbose()) + g_print ("\t=> UTC stamp = %ld.%06ld (should be: %ld.%06ld) (%ld.%06ld off)\n", + date.tv_sec, date.tv_usec, ref_date.tv_sec, ref_date.tv_usec, + date.tv_sec - ref_date.tv_sec, date.tv_usec - ref_date.tv_usec); + g_assert (date.tv_sec == ref_date.tv_sec && date.tv_usec == ref_date.tv_usec); + + g_assert (g_time_val_from_iso8601 (REF_STR_USEC_CEST, &date) != FALSE); + if (g_test_verbose()) + g_print ("\t=> CEST stamp = %ld.%06ld (should be: %ld.%06ld) (%ld.%06ld off)\n", + date.tv_sec, date.tv_usec, ref_date.tv_sec, ref_date.tv_usec, + date.tv_sec - ref_date.tv_sec, date.tv_usec - ref_date.tv_usec); + g_assert (date.tv_sec == ref_date.tv_sec && date.tv_usec == ref_date.tv_usec); + + g_assert (g_time_val_from_iso8601 (REF_STR_USEC_EST, &date) != FALSE); + if (g_test_verbose()) + g_print ("\t=> EST stamp = %ld.%06ld (should be: %ld.%06ld) (%ld.%06ld off)\n", + date.tv_sec, date.tv_usec, ref_date.tv_sec, ref_date.tv_usec, + date.tv_sec - ref_date.tv_sec, date.tv_usec - ref_date.tv_usec); + g_assert (date.tv_sec == ref_date.tv_sec && date.tv_usec == ref_date.tv_usec); if (g_test_verbose()) g_print ("checking g_time_val_to_iso8601...\n"); ref_date.tv_sec = REF_SEC_UTC; - ref_date.tv_usec = 1; + ref_date.tv_usec = 0; date_str = g_time_val_to_iso8601 (&ref_date); g_assert (date_str != NULL); if (g_test_verbose()) @@ -1420,6 +1454,14 @@ various_string_tests (void) g_assert (strcmp (date_str, REF_STR_UTC) == 0); g_free (date_str); + ref_date.tv_usec = REF_USEC_UTC; + date_str = g_time_val_to_iso8601 (&ref_date); + g_assert (date_str != NULL); + if (g_test_verbose()) + g_print ("\t=> date string = %s (should be: %s)\n", date_str, REF_STR_USEC_UTC); + g_assert (strcmp (date_str, REF_STR_USEC_UTC) == 0); + g_free (date_str); + if (g_test_verbose()) g_print ("checking g_ascii_strcasecmp..."); g_assert (g_ascii_strcasecmp ("FroboZZ", "frobozz") == 0);