|
| 1 | +import re |
| 2 | + |
1 | 3 | cimport numpy as cnp
|
2 | 4 | from cpython.object cimport (
|
3 | 5 | Py_EQ,
|
@@ -2590,26 +2592,33 @@ class Period(_Period):
|
2590 | 2592 | value = str(value)
|
2591 | 2593 | value = value.upper()
|
2592 | 2594 |
|
2593 |
| - freqstr = freq.rule_code if freq is not None else None |
2594 |
| - dt, reso = parse_datetime_string_with_reso(value, freqstr) |
2595 |
| - try: |
2596 |
| - ts = Timestamp(value) |
2597 |
| - except ValueError: |
2598 |
| - nanosecond = 0 |
2599 |
| - else: |
2600 |
| - nanosecond = ts.nanosecond |
2601 |
| - if nanosecond != 0: |
2602 |
| - reso = "nanosecond" |
2603 |
| - if dt is NaT: |
2604 |
| - ordinal = NPY_NAT |
| 2595 | + match = re.search(r"^\d{4}-\d{2}-\d{2}/\d{4}-\d{2}-\d{2}", value) |
| 2596 | + if match: |
| 2597 | + # Case that cannot be parsed (correctly) by our datetime |
| 2598 | + # parsing logic |
| 2599 | + dt, freq = parse_weekly_str(value, freq) |
2605 | 2600 |
|
2606 |
| - if freq is None: |
| 2601 | + else: |
| 2602 | + freqstr = freq.rule_code if freq is not None else None |
| 2603 | + dt, reso = parse_datetime_string_with_reso(value, freqstr) |
2607 | 2604 | try:
|
2608 |
| - freq = attrname_to_abbrevs[reso] |
2609 |
| - except KeyError: |
2610 |
| - raise ValueError(f"Invalid frequency or could not " |
2611 |
| - f"infer: {reso}") |
2612 |
| - freq = to_offset(freq) |
| 2605 | + ts = Timestamp(value) |
| 2606 | + except ValueError: |
| 2607 | + nanosecond = 0 |
| 2608 | + else: |
| 2609 | + nanosecond = ts.nanosecond |
| 2610 | + if nanosecond != 0: |
| 2611 | + reso = "nanosecond" |
| 2612 | + if dt is NaT: |
| 2613 | + ordinal = NPY_NAT |
| 2614 | + |
| 2615 | + if freq is None: |
| 2616 | + try: |
| 2617 | + freq = attrname_to_abbrevs[reso] |
| 2618 | + except KeyError: |
| 2619 | + raise ValueError(f"Invalid frequency or could not " |
| 2620 | + f"infer: {reso}") |
| 2621 | + freq = to_offset(freq) |
2613 | 2622 |
|
2614 | 2623 | elif PyDateTime_Check(value):
|
2615 | 2624 | dt = value
|
@@ -2669,3 +2678,31 @@ def validate_end_alias(how: str) -> str: # Literal["E", "S"]
|
2669 | 2678 | if how not in {"S", "E"}:
|
2670 | 2679 | raise ValueError("How must be one of S or E")
|
2671 | 2680 | return how
|
| 2681 | + |
| 2682 | + |
| 2683 | +cdef parse_weekly_str(value, BaseOffset freq): |
| 2684 | + """ |
| 2685 | + Parse e.g. "2017-01-23/2017-01-29", which cannot be parsed by the general |
| 2686 | + datetime-parsing logic. This ensures that we can round-trip with |
| 2687 | + Period.__str__ with weekly freq. |
| 2688 | + """ |
| 2689 | + match = re.search(r"^\d{4}-\d{2}-\d{2}/\d{4}-\d{2}-\d{2}", value) |
| 2690 | + if not match: |
| 2691 | + raise ValueError("Could not parse as weekly-freq Period") |
| 2692 | + |
| 2693 | + start, end = value.split("/") |
| 2694 | + start = Timestamp(start) |
| 2695 | + end = Timestamp(end) |
| 2696 | + |
| 2697 | + if (end - start).days != 6: |
| 2698 | + # We are interested in cases where this is str(period) |
| 2699 | + # of a Week-freq period |
| 2700 | + raise ValueError("Could not parse as weekly-freq Period") |
| 2701 | + |
| 2702 | + if freq is None: |
| 2703 | + day_name = end.day_name()[:3].upper() |
| 2704 | + freqstr = f"W-{day_name}" |
| 2705 | + freq = to_offset(freqstr) |
| 2706 | + # We _should_ have freq.is_on_offset(end) |
| 2707 | + |
| 2708 | + return end, freq |
0 commit comments