|
4 | 4 | from typing import Optional, Tuple, Union
|
5 | 5 |
|
6 | 6 | import iso8601
|
| 7 | +from fastapi import HTTPException |
7 | 8 | from pystac.utils import datetime_to_str
|
8 | 9 |
|
9 | 10 | RFC33339_PATTERN = (
|
@@ -45,53 +46,74 @@ def rfc3339_str_to_datetime(s: str) -> datetime:
|
45 | 46 | return iso8601.parse_date(s)
|
46 | 47 |
|
47 | 48 |
|
48 |
| -def str_to_interval(interval: Optional[str]) -> Optional[DateTimeType]: |
49 |
| - """Extract a tuple of datetimes from an interval string. |
| 49 | +def parse_single_date(date_str: str) -> datetime: |
| 50 | + """ |
| 51 | + Parse a single RFC3339 date string into a datetime object. |
| 52 | +
|
| 53 | + Args: |
| 54 | + date_str (str): A string representing the date in RFC3339 format. |
| 55 | +
|
| 56 | + Returns: |
| 57 | + datetime: A datetime object parsed from the date_str. |
| 58 | +
|
| 59 | + Raises: |
| 60 | + ValueError: If the date_str is empty or contains the placeholder '..'. |
| 61 | + """ |
| 62 | + if ".." in date_str or not date_str: |
| 63 | + raise ValueError("Invalid date format.") |
| 64 | + return rfc3339_str_to_datetime(date_str) |
| 65 | + |
50 | 66 |
|
51 |
| - Interval strings are defined by |
52 |
| - OGC API - Features Part 1 for the datetime query parameter value. These follow the |
53 |
| - form '1985-04-12T23:20:50.52Z/1986-04-12T23:20:50.52Z', and allow either the start |
54 |
| - or end (but not both) to be open-ended with '..' or ''. |
| 67 | +def str_to_interval(interval: Optional[str]) -> Optional[DateTimeType]: |
| 68 | + """ |
| 69 | + Extract a tuple of datetime objects from an interval string defined by the OGC API. |
| 70 | + The interval can either be a single datetime or a range with start and end datetime. |
55 | 71 |
|
56 | 72 | Args:
|
57 |
| - interval (str or None): The interval string to convert to a tuple of |
58 |
| - datetime.datetime objects, or None if no datetime is specified. |
| 73 | + interval (Optional[str]): The interval string to convert to datetime objects, |
| 74 | + or None if no datetime is specified. |
59 | 75 |
|
60 | 76 | Returns:
|
61 |
| - Optional[DateTimeType]: A tuple of datetime.datetime objects or None if |
62 |
| - input is None. |
| 77 | + Optional[DateTimeType]: A tuple of datetime.datetime objects or |
| 78 | + None if input is None. |
63 | 79 |
|
64 | 80 | Raises:
|
65 |
| - ValueError: If the string is not a valid interval string and not None. |
| 81 | + HTTPException: If the string is not valid for various reasons such as being empty, |
| 82 | + having more than one slash, or if date formats are invalid. |
66 | 83 | """
|
67 | 84 | if interval is None:
|
68 | 85 | return None
|
69 | 86 |
|
70 | 87 | if not interval:
|
71 |
| - raise ValueError("Empty interval string is invalid.") |
| 88 | + raise HTTPException(status_code=400, detail="Empty interval string is invalid.") |
72 | 89 |
|
73 | 90 | values = interval.split("/")
|
74 |
| - if len(values) == 1: |
75 |
| - # Single date for == date case |
76 |
| - return rfc3339_str_to_datetime(values[0]) |
77 |
| - elif len(values) > 2: |
78 |
| - raise ValueError( |
79 |
| - f"Interval string '{interval}' contains more than one forward slash." |
| 91 | + if len(values) > 2: |
| 92 | + raise HTTPException( |
| 93 | + status_code=400, |
| 94 | + detail="Interval string contains more than one forward slash.", |
80 | 95 | )
|
81 | 96 |
|
82 |
| - start = None |
83 |
| - end = None |
84 |
| - if values[0] not in ["..", ""]: |
85 |
| - start = rfc3339_str_to_datetime(values[0]) |
86 |
| - if values[1] not in ["..", ""]: |
87 |
| - end = rfc3339_str_to_datetime(values[1]) |
| 97 | + try: |
| 98 | + start = parse_single_date(values[0]) if values[0] not in ["..", ""] else None |
| 99 | + end = ( |
| 100 | + parse_single_date(values[1]) |
| 101 | + if len(values) > 1 and values[1] not in ["..", ""] |
| 102 | + else None |
| 103 | + ) |
| 104 | + except (ValueError, iso8601.ParseError) as e: |
| 105 | + raise HTTPException(status_code=400, detail=str(e)) |
88 | 106 |
|
89 | 107 | if start is None and end is None:
|
90 |
| - raise ValueError("Double open-ended intervals are not allowed.") |
| 108 | + raise HTTPException( |
| 109 | + status_code=400, detail="Double open-ended intervals are not allowed." |
| 110 | + ) |
91 | 111 | if start is not None and end is not None and start > end:
|
92 |
| - raise ValueError("Start datetime cannot be before end datetime.") |
93 |
| - else: |
94 |
| - return start, end |
| 112 | + raise HTTPException( |
| 113 | + status_code=400, detail="Start datetime cannot be before end datetime." |
| 114 | + ) |
| 115 | + |
| 116 | + return start, end |
95 | 117 |
|
96 | 118 |
|
97 | 119 | def now_in_utc() -> datetime:
|
|
0 commit comments