mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Rewrite gnc_iso8601_to_timespec_gmt
Into something that isn't an ugly hack and actually works. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@22606 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
586e124ba5
commit
16bf61fbcc
@ -1391,157 +1391,41 @@ xaccDateUtilGetStampNow (void)
|
||||
/********************************************************************\
|
||||
* 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;
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
|
@ -1617,7 +1617,7 @@ test_gnc_iso8601_to_timespec_gmt (void)
|
||||
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));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user