|
1 | 1 | # -*- coding: utf-8 -*- |
2 | 2 | # cython: profile=False |
3 | 3 | import collections |
| 4 | +import re |
4 | 5 |
|
5 | 6 | import sys |
6 | 7 | cdef bint PY3 = (sys.version_info[0] >= 3) |
@@ -235,6 +236,25 @@ cpdef inline int64_t cast_from_unit(object ts, object unit) except? -1: |
235 | 236 | return <int64_t> (base *m) + <int64_t> (frac *m) |
236 | 237 |
|
237 | 238 |
|
| 239 | +cpdef match_iso_format(object ts): |
| 240 | + """ |
| 241 | + Match a provided string against an ISO 8601 pattern, providing a group for |
| 242 | + each ``Timedelta`` component. |
| 243 | + """ |
| 244 | + pater = re.compile(r"""P |
| 245 | + (?P<days>-?[0-9]*)DT |
| 246 | + (?P<hours>[0-9]{1,2})H |
| 247 | + (?P<minutes>[0-9]{1,2})M |
| 248 | + (?P<seconds>[0-9]{0,2}) |
| 249 | + (\. |
| 250 | + (?P<milliseconds>[0-9]{0,3}) |
| 251 | + (?P<microseconds>[0-9]{0,3}) |
| 252 | + (?P<nanoseconds>[0-9]{0,3}) |
| 253 | + )?S""", re.VERBOSE) |
| 254 | + |
| 255 | + return re.match(pater, ts) |
| 256 | + |
| 257 | + |
238 | 258 | cdef inline parse_timedelta_string(object ts): |
239 | 259 | """ |
240 | 260 | Parse a regular format timedelta string. Return an int64_t (in ns) |
@@ -506,6 +526,33 @@ def _binary_op_method_timedeltalike(op, name): |
506 | 526 | # ---------------------------------------------------------------------- |
507 | 527 | # Timedelta Construction |
508 | 528 |
|
| 529 | +def _value_from_iso_match(match): |
| 530 | + """ |
| 531 | + Extracts and cleanses the appropriate values from a match object with |
| 532 | + groups for each component of an ISO 8601 duration |
| 533 | +
|
| 534 | + Parameters |
| 535 | + ---------- |
| 536 | + match: |
| 537 | + Regular expression with groups for each component of an ISO 8601 |
| 538 | + duration |
| 539 | +
|
| 540 | + Returns |
| 541 | + ------- |
| 542 | + int |
| 543 | + Precision in nanoseconds of matched ISO 8601 duration |
| 544 | + """ |
| 545 | + match_dict = {k: v for k, v in match.groupdict().items() if v} |
| 546 | + for comp in ['milliseconds', 'microseconds', 'nanoseconds']: |
| 547 | + if comp in match_dict: |
| 548 | + match_dict[comp] ='{:0<3}'.format(match_dict[comp]) |
| 549 | + |
| 550 | + match_dict = {k: int(v) for k, v in match_dict.items()} |
| 551 | + nano = match_dict.pop('nanoseconds', 0) |
| 552 | + |
| 553 | + return nano + convert_to_timedelta64(timedelta(**match_dict), 'ns') |
| 554 | + |
| 555 | + |
509 | 556 | cdef _to_py_int_float(v): |
510 | 557 | # Note: This used to be defined inside Timedelta.__new__ |
511 | 558 | # but cython will not allow `cdef` functions to be defined dynamically. |
@@ -825,7 +872,11 @@ class Timedelta(_Timedelta): |
825 | 872 | if isinstance(value, Timedelta): |
826 | 873 | value = value.value |
827 | 874 | elif is_string_object(value): |
828 | | - value = np.timedelta64(parse_timedelta_string(value)) |
| 875 | + if value[0] == 'P': # hackish |
| 876 | + match = match_iso_format(value) |
| 877 | + value = _value_from_iso_match(match) |
| 878 | + else: |
| 879 | + value = np.timedelta64(parse_timedelta_string(value)) |
829 | 880 | elif PyDelta_Check(value): |
830 | 881 | value = convert_to_timedelta64(value, 'ns') |
831 | 882 | elif is_timedelta64_object(value): |
|
0 commit comments