diff --git a/dlt/common/time.py b/dlt/common/time.py index 4ce411baa4..74c32e4ea0 100644 --- a/dlt/common/time.py +++ b/dlt/common/time.py @@ -164,17 +164,30 @@ def detect_datetime_format(value: str) -> Optional[str]: ): "%Y-%m-%dT%H:%M:%S.%fZ", # UTC with fractional seconds re.compile( r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{2}:\d{2}$" - ): "%Y-%m-%dT%H:%M:%S%z", # Timezone offset + ): "%Y-%m-%dT%H:%M:%S%z", # Positive timezone offset re.compile( r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{4}$" - ): "%Y-%m-%dT%H:%M:%S%z", # Timezone without colon - # Full datetime with fractional seconds and timezone + ): "%Y-%m-%dT%H:%M:%S%z", # Positive timezone without colon + # Full datetime with fractional seconds and positive timezone offset re.compile( r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+\+\d{2}:\d{2}$" ): "%Y-%m-%dT%H:%M:%S.%f%z", re.compile( r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+\+\d{4}$" - ): "%Y-%m-%dT%H:%M:%S.%f%z", # Timezone without colon + ): "%Y-%m-%dT%H:%M:%S.%f%z", # Positive timezone without colon + re.compile( + r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}-\d{2}:\d{2}$" + ): "%Y-%m-%dT%H:%M:%S%z", # Negative timezone offset + re.compile( + r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}-\d{4}$" + ): "%Y-%m-%dT%H:%M:%S%z", # Negative timezone without colon + # Full datetime with fractional seconds and negative timezone offset + re.compile( + r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+-\d{2}:\d{2}$" + ): "%Y-%m-%dT%H:%M:%S.%f%z", + re.compile( + r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+-\d{4}$" + ): "%Y-%m-%dT%H:%M:%S.%f%z", # Negative Timezone without colon # Datetime without timezone re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$"): "%Y-%m-%dT%H:%M:%S", # No timezone re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$"): "%Y-%m-%dT%H:%M", # Minute precision diff --git a/tests/common/test_time.py b/tests/common/test_time.py index 8c25983d46..9c7a1567e2 100644 --- a/tests/common/test_time.py +++ b/tests/common/test_time.py @@ -132,8 +132,26 @@ def test_datetime_to_timestamp_helpers( [ ("2024-10-20T15:30:00Z", "%Y-%m-%dT%H:%M:%SZ"), # UTC 'Z' ("2024-10-20T15:30:00.123456Z", "%Y-%m-%dT%H:%M:%S.%fZ"), # UTC 'Z' with fractional seconds - ("2024-10-20T15:30:00+02:00", "%Y-%m-%dT%H:%M:%S%z"), # Timezone offset - ("2024-10-20T15:30:00+0200", "%Y-%m-%dT%H:%M:%S%z"), # Timezone without colon + ("2024-10-20T15:30:00+02:00", "%Y-%m-%dT%H:%M:%S%z"), # Positive timezone offset + ("2024-10-20T15:30:00+0200", "%Y-%m-%dT%H:%M:%S%z"), # Positive timezone offset (no colon) + ( + "2024-10-20T15:30:00.123456+02:00", + "%Y-%m-%dT%H:%M:%S.%f%z", + ), # Positive timezone offset with fractional seconds + ( + "2024-10-20T15:30:00.123456+0200", + "%Y-%m-%dT%H:%M:%S.%f%z", + ), # Positive timezone offset with fractional seconds (no colon) + ("2024-10-20T15:30:00-02:00", "%Y-%m-%dT%H:%M:%S%z"), # Negative timezone offset + ("2024-10-20T15:30:00-0200", "%Y-%m-%dT%H:%M:%S%z"), # Negative timezone offset (no colon) + ( + "2024-10-20T15:30:00.123456-02:00", + "%Y-%m-%dT%H:%M:%S.%f%z", + ), # Negative timezone offset with fractional seconds + ( + "2024-10-20T15:30:00.123456-0200", + "%Y-%m-%dT%H:%M:%S.%f%z", + ), # Negative timezone offset with fractional seconds (no colon) ("2024-10-20T15:30:00", "%Y-%m-%dT%H:%M:%S"), # No timezone ("2024-10-20T15:30", "%Y-%m-%dT%H:%M"), # Minute precision ("2024-10-20T15", "%Y-%m-%dT%H"), # Hour precision