static int
ParseISO8601Number(char *str, char **endptr, int64 *ipart, double *fpart)
{
- int sign = 1;
+ double val;
- *ipart = 0;
- *fpart = 0.0;
-
- /* Parse sign if there is any */
- if (*str == '-')
- {
- sign = -1;
- str++;
- }
-
- *endptr = str;
+ /*
+ * Historically this has accepted anything that strtod() would take,
+ * notably including "e" notation, so continue doing that. This is
+ * slightly annoying because the precision of double is less than that of
+ * int64, so we would lose accuracy for inputs larger than 2^53 or so.
+ * However, historically we rejected inputs outside the int32 range,
+ * making that concern moot. What we do now is reject abs(val) above
+ * 1.0e15 (a round number a bit less than 2^50), so that any accepted
+ * value will have an exact integer part, and thereby a fraction part with
+ * abs(*fpart) less than 1. In the absence of field complaints it doesn't
+ * seem worth working harder.
+ */
+ if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
+ return DTERR_BAD_FORMAT;
errno = 0;
-
- /* Parse int64 part if there is any */
- if (isdigit((unsigned char) **endptr))
- *ipart = strtoi64(*endptr, endptr, 10) * sign;
-
- /* Parse fractional part if there is any */
- if (**endptr == '.')
- {
- /*
- * As in ParseFraction, some versions of strtod insist on seeing some
- * digits after '.', but some don't. We want to allow zero digits
- * after '.' as long as there were some before it.
- */
- if (isdigit((unsigned char) *(*endptr + 1)))
- *fpart = strtod(*endptr, endptr) * sign;
- else
- {
- (*endptr)++; /* advance over '.' */
- str++; /* so next test will fail if no digits */
- }
- }
-
- /* did we not see anything that looks like a number? */
+ val = strtod(str, endptr);
+ /* did we not see anything that looks like a double? */
if (*endptr == str || errno != 0)
return DTERR_BAD_FORMAT;
-
+ /* watch out for overflow, including infinities; reject NaN too */
+ if (isnan(val) || val < -1.0e15 || val > 1.0e15)
+ return DTERR_FIELD_OVERFLOW;
+ /* be very sure we truncate towards zero (cf dtrunc()) */
+ if (val >= 0)
+ *ipart = (int64) floor(val);
+ else
+ *ipart = (int64) -floor(-val);
+ *fpart = val - *ipart;
+ /* Callers expect this to hold */
+ Assert(*fpart > -1.0 && *fpart < 1.0);
return 0;
}
1 mon 3 days 04:05:06
(1 row)
+select interval 'P10.5e4Y'; -- not per spec, but we've historically taken it
+ interval
+--------------
+ 105000 years
+(1 row)
+
select interval 'P.Y0M3DT4H5M6S'; -- error
ERROR: invalid input syntax for type interval: "P.Y0M3DT4H5M6S"
LINE 1: select interval 'P.Y0M3DT4H5M6S';
select interval 'PT2562047788.0152155019444';
interval
-----------------------------------
- @ 2562047788 hours 54.775807 secs
+ @ 2562047788 hours 54.775429 secs
(1 row)
select interval 'PT-2562047788.0152155022222';
interval
---------------------------------------
- @ 2562047788 hours 54.775808 secs ago
+ @ 2562047788 hours 54.775429 secs ago
(1 row)
-- overflow each date/time field