the local timezone (:ticket:`#1058`).
- Fix `~Cursor.rownumber` after using `~AsyncServerCursor.scroll()` on
`AsyncServerCursor` (:ticket:`#1066`).
+- Fix interval parsing with days or other parts and negative time in C module
+ (:ticket:`#1071`).
Current release
cdef const char *sep
cdef const char *end = ptr + length
- # If there are spaces, there is a [+|-]n [days|months|years]
while True:
+ # If there are spaces, there is a [+|-]n [days|months|years]
+ sep = strchr(ptr, b' ')
+ if sep == NULL or sep > end:
+ break
+
if ptr[0] == b'-' or ptr[0] == b'+':
sign = ptr[0]
ptr += 1
else:
sign = 0
- sep = strchr(ptr, b' ')
- if sep == NULL or sep > end:
- break
-
val = 0
ptr = _parse_date_values(ptr, end, &val, 1)
if ptr == NULL:
else:
break
- # Parse the time part. An eventual sign was already consumed in the loop
+ # Parse the time part if present
cdef int64_t vals[3]
- memset(vals, 0, sizeof(vals))
if ptr != NULL:
+ memset(vals, 0, sizeof(vals))
+
+ # Parse the sign of the time part.
+ if ptr[0] == b'-' or ptr[0] == b'+':
+ sign = ptr[0]
+ ptr += 1
+ else:
+ sign = 0
+
ptr = _parse_date_values(ptr, end, vals, ARRAYSIZE(vals))
if ptr == NULL:
s = bytes(data).decode("utf8", "replace")
secs = vals[2] + 60 * (vals[1] + 60 * vals[0])
if secs > 86_400:
- days += secs // 86_400
+ if sign == b'-':
+ days -= secs // 86_400
+ else:
+ days += secs // 86_400
secs %= 86_400
if ptr[0] == b'.':
ptr = _parse_micros(ptr + 1, &us)
- if sign == b'-':
- secs = -secs
- us = -us
+ if sign == b'-':
+ secs = -secs
+ us = -us
try:
return cdt.timedelta_new(days, secs, us)
("60d", "2 month"),
("-90d", "-3 month"),
("186d", "6 mons 6 days"),
+ ("174d", "6 mons -6 days"),
("736d", "2 years 6 days"),
+ ("724d", "2 years -6 days"),
+ ("330d", "1 years -1 month"),
("83063d,81640s,447000m", "1993534:40:40.447"),
+ ("-1d,64800s", "41 days -990:00:00"),
+ ("0s", "10 days -240:00:00"),
+ ("20d", "10 days 240:00:00"),
+ ("0d", "-10 days 240:00:00"),
+ ("-20d", "-10 days -240:00:00"),
],
)
@pytest.mark.parametrize("fmt_out", pq.Format)