Parse a date string (filing dates, news timestamps) into UTC datetime. Accepts ISO-8601 with or without time component, ``YYYY-MM-DD``, ``YYYY/MM/DD``, journal long form (``Apr 15, 2024``), and a few related variants. Args: value: Date or datetime string. Returns:
(value: str)
| 37 | |
| 38 | |
| 39 | def parse_filing_date(value: str) -> datetime: |
| 40 | """Parse a date string (filing dates, news timestamps) into UTC datetime. |
| 41 | |
| 42 | Accepts ISO-8601 with or without time component, ``YYYY-MM-DD``, |
| 43 | ``YYYY/MM/DD``, journal long form (``Apr 15, 2024``), and a few |
| 44 | related variants. |
| 45 | |
| 46 | Args: |
| 47 | value: Date or datetime string. |
| 48 | |
| 49 | Returns: |
| 50 | Aware UTC datetime. |
| 51 | |
| 52 | Raises: |
| 53 | ValueError: If none of the accepted formats match. |
| 54 | """ |
| 55 | text = value.strip() |
| 56 | if not text: |
| 57 | raise ValueError("empty date string") |
| 58 | |
| 59 | last_error: Exception | None = None |
| 60 | for fmt in _DATE_FORMATS: |
| 61 | try: |
| 62 | parsed = datetime.strptime(text, fmt) |
| 63 | except ValueError as exc: |
| 64 | last_error = exc |
| 65 | continue |
| 66 | return to_utc(parsed) |
| 67 | |
| 68 | raise ValueError( |
| 69 | f"could not parse date {value!r}; tried {len(_DATE_FORMATS)} formats" |
| 70 | ) from last_error |
| 71 | |
| 72 | |
| 73 | def business_days_between(a: date, b: date) -> int: |