r22606 - gnucash/trunk/src/libqof/qof - Rewrite gnc_iso8601_to_timespec_gmt
John Ralls
jralls at code.gnucash.org
Sat Dec 1 17:42:28 EST 2012
Author: jralls
Date: 2012-12-01 17:42:28 -0500 (Sat, 01 Dec 2012)
New Revision: 22606
Trac: http://svn.gnucash.org/trac/changeset/22606
Modified:
gnucash/trunk/src/libqof/qof/gnc-date.c
gnucash/trunk/src/libqof/qof/test/test-gnc-date.c
Log:
Rewrite gnc_iso8601_to_timespec_gmt
Into something that isn't an ugly hack and actually works.
Modified: gnucash/trunk/src/libqof/qof/gnc-date.c
===================================================================
--- gnucash/trunk/src/libqof/qof/gnc-date.c 2012-12-01 22:42:19 UTC (rev 22605)
+++ gnucash/trunk/src/libqof/qof/gnc-date.c 2012-12-01 22:42:28 UTC (rev 22606)
@@ -1391,157 +1391,41 @@
/********************************************************************\
* iso 8601 datetimes should look like 1998-07-02 11:00:00.68-05
\********************************************************************/
-/* hack alert -- this routine returns incorrect values for
- * dates before 1970 */
+/* Unfortunately, not all strptime or struct tm implementations
+ * support timezones, so we have to do this with sscanf.
+ */
+#define ISO_DATE_FORMAT "%d-%d-%d %d:%d:%lf%s"
Timespec
gnc_iso8601_to_timespec_gmt(const char *str)
{
- char buf[4];
- gchar *dupe;
- Timespec ts;
- struct tm stm;
- long int nsec = 0;
+ Timespec time = { 0L, 0L };
+ GDateTime *gdt;
+ gint hour = 0, minute = 0, day = 0, month = 0, year = 0;
+ gchar zone[6] = "\0\0\0\0\0\0";
+ gdouble second = 0.0;
+ gint fields;
- ts.tv_sec = 0;
- ts.tv_nsec = 0;
- memset (&stm, 0, sizeof (stm));
- if (!str) return ts;
- dupe = g_strdup(str);
- stm.tm_year = atoi(str) - 1900;
- str = strchr (str, '-');
- if (str)
- {
- str++;
- }
- else
- {
- return ts;
- }
- stm.tm_mon = atoi(str) - 1;
- str = strchr (str, '-');
- if (str)
- {
- str++;
- }
- else
- {
- return ts;
- }
- stm.tm_mday = atoi(str);
+ if (!str)
+ return time;
- str = strchr (str, ' ');
- if (str)
+ fields = sscanf (str, ISO_DATE_FORMAT, &year, &month,
+ &day, &hour, &minute, &second, zone);
+ if (fields < 1)
+ return time;
+ else if (fields > 6 && strlen (zone) > 0) /* Date string included a timezone */
{
- str++;
+ GTimeZone *tz = g_time_zone_new (zone);
+ gdt = g_date_time_new (tz, year, month, day, hour, minute, second);
+ g_time_zone_unref (tz);
}
- else
- {
- return ts;
- }
- stm.tm_hour = atoi(str);
- str = strchr (str, ':');
- if (str)
- {
- str++;
- }
- else
- {
- return ts;
- }
- stm.tm_min = atoi(str);
- str = strchr (str, ':');
- if (str)
- {
- str++;
- }
- else
- {
- return ts;
- }
- stm.tm_sec = atoi (str);
+ else /* No zone info, assume UTC */
+ gdt = g_date_time_new_utc (year, month, day, hour, minute, second);
- /* The decimal point, optionally present ... */
- /* hack alert -- this algo breaks if more than 9 decimal places present */
- if (strchr (str, '.'))
- {
- int decimals, i, multiplier = 1000000000;
- str = strchr (str, '.') + 1;
- decimals = strcspn (str, "+- ");
- for (i = 0; i < decimals; i++) multiplier /= 10;
- nsec = atoi(str) * multiplier;
- }
- stm.tm_isdst = -1;
-
- /* Timezone format can be +hh or +hhmm or +hh.mm (or -) (or not present) */
- str += strcspn (str, "+-");
- if (*str)
- {
- buf[0] = str[0];
- buf[1] = str[1];
- buf[2] = str[2];
- buf[3] = 0;
- stm.tm_hour -= atoi(buf);
-
- str += 3;
- if ('.' == *str) str++;
- if (isdigit ((unsigned char)*str) && isdigit ((unsigned char)*(str + 1)))
- {
- int cyn;
- /* copy sign from hour part */
- if ('+' == buf[0])
- {
- cyn = -1;
- }
- else
- {
- cyn = +1;
- }
- buf[0] = str[0];
- buf[1] = str[1];
- buf[2] = str[2];
- buf[3] = 0;
- stm.tm_min += cyn * atoi(buf);
- }
- }
-
- /* Note that gnc_mktime returns 'local seconds' which is the true time
- * minus the timezone offset. We don't want to work with local
- * seconds, since they swim around acording to daylight savings, etc.
- * We want to work with universal time. Thus, add an offset
- * to undo the damage that gnc_mktime causes.
- */
- {
- struct tm tmp_tm;
- struct tm tm;
- long int tz;
- int tz_hour;
- gint64 secs;
-
- /* Use a temporary tm struct so the gnc_mktime call below
- * doesn't mess up stm. */
- tmp_tm = stm;
- tmp_tm.tm_isdst = -1;
-
- secs = gnc_mktime (&tmp_tm);
- /* The call to gnc_localtime is 'bogus', but it forces 'timezone' to
- * be set. Note that we must use the accurate date, since the
- * value of 'gnc_timezone' includes daylight savings corrections
- * for that date. */
-
- gnc_localtime_r (&secs, &tm);
-
- tz = gnc_timezone (&tmp_tm);
-
- tz_hour = tz / 3600;
- stm.tm_hour -= tz_hour;
- stm.tm_min -= (tz % 3600) / 60;
- stm.tm_isdst = tmp_tm.tm_isdst;
- ts.tv_sec = gnc_mktime (&stm);
- ts.tv_nsec = nsec;
- }
- g_free(dupe);
- return ts;
+ time.tv_sec = g_date_time_to_unix (gdt);
+ time.tv_nsec = g_date_time_get_microsecond (gdt) * 1000;
+ g_date_time_unref (gdt);
+ return time;
}
/********************************************************************\
Modified: gnucash/trunk/src/libqof/qof/test/test-gnc-date.c
===================================================================
--- gnucash/trunk/src/libqof/qof/test/test-gnc-date.c 2012-12-01 22:42:19 UTC (rev 22605)
+++ gnucash/trunk/src/libqof/qof/test/test-gnc-date.c 2012-12-01 22:42:28 UTC (rev 22606)
@@ -1617,7 +1617,7 @@
g_assert_cmpint (t.tv_sec, ==, g_date_time_to_unix (gdt2));
g_assert_cmpint (t.tv_nsec, ==, get_nanoseconds (gdt2));
- t = gnc_iso8601_to_timespec_gmt ("2012-07-04 19:27:44.0+08.40");
+ t = gnc_iso8601_to_timespec_gmt ("2012-07-04 19:27:44.0+08:40");
g_assert_cmpint (t.tv_sec, ==, g_date_time_to_unix (gdt3));
g_assert_cmpint (t.tv_nsec, ==, get_nanoseconds (gdt3));
More information about the gnucash-changes
mailing list