Skip to content

Commit e4d281f

Browse files
fix: respect format parameter in ab_datetime_format for AirbyteDateTime objects
Co-Authored-By: Aaron <AJ> Steers <[email protected]>
1 parent f7f9868 commit e4d281f

File tree

1 file changed

+32
-3
lines changed

1 file changed

+32
-3
lines changed

airbyte_cdk/utils/datetime_helpers.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ def ab_datetime_now() -> AirbyteDateTime:
358358
return AirbyteDateTime.from_datetime(datetime.now(timezone.utc))
359359

360360

361-
def ab_datetime_parse(dt_str: str | int) -> AirbyteDateTime:
361+
def ab_datetime_parse(dt_str: str | int, formats: list[str] | None = None, disallow_other_formats: bool = False) -> AirbyteDateTime:
362362
"""Parses a datetime string or timestamp into an AirbyteDateTime with timezone awareness.
363363
364364
This implementation is as flexible as possible to handle various datetime formats.
@@ -374,6 +374,10 @@ def ab_datetime_parse(dt_str: str | int) -> AirbyteDateTime:
374374
Args:
375375
dt_str: A datetime string in ISO8601/RFC3339 format, Unix timestamp (int/str),
376376
or other recognizable datetime format.
377+
formats: Optional list of format strings to try before falling back to more
378+
forgiving parsing logic. If provided, these formats will be tried in order.
379+
disallow_other_formats: If True, only the provided formats will be used for parsing,
380+
and more forgiving parsing logic will not be attempted.
377381
378382
Returns:
379383
AirbyteDateTime: A timezone-aware datetime object.
@@ -388,6 +392,8 @@ def ab_datetime_parse(dt_str: str | int) -> AirbyteDateTime:
388392
'2023-03-14T15:00:00+00:00'
389393
>>> ab_datetime_parse("2023-03-14") # Date-only
390394
'2023-03-14T00:00:00+00:00'
395+
>>> ab_datetime_parse("2023-03-14 15:09:26", formats=["%Y-%m-%d %H:%M:%S"])
396+
'2023-03-14T15:09:26+00:00'
391397
"""
392398
try:
393399
# Handle numeric values as Unix timestamps (UTC)
@@ -408,6 +414,20 @@ def ab_datetime_parse(dt_str: str | int) -> AirbyteDateTime:
408414
f"Could not parse datetime string: expected string or integer, got {type(dt_str)}"
409415
)
410416

417+
if formats:
418+
for format_str in formats:
419+
try:
420+
parsed = datetime.strptime(dt_str, format_str)
421+
if parsed.tzinfo is None:
422+
parsed = parsed.replace(tzinfo=timezone.utc)
423+
return AirbyteDateTime.from_datetime(parsed)
424+
except ValueError:
425+
continue
426+
427+
if disallow_other_formats:
428+
raise ValueError(f"Could not parse datetime string '{dt_str}' with any of the provided formats: {formats}")
429+
430+
411431
# Handle date-only format first
412432
if ":" not in dt_str and dt_str.count("-") == 2 and "/" not in dt_str:
413433
try:
@@ -442,21 +462,30 @@ def ab_datetime_parse(dt_str: str | int) -> AirbyteDateTime:
442462
raise ValueError(f"Could not parse datetime string: {dt_str}")
443463

444464

445-
def ab_datetime_try_parse(dt_str: str) -> AirbyteDateTime | None:
465+
def ab_datetime_try_parse(dt_str: str | int, formats: list[str] | None = None, disallow_other_formats: bool = False) -> AirbyteDateTime | None:
446466
"""Try to parse the input as a datetime, failing gracefully instead of raising an exception.
447467
448468
This is a thin wrapper around `ab_datetime_parse()` that catches parsing errors and
449469
returns `None` instead of raising an exception.
450470
The implementation is as flexible as possible to handle various datetime formats.
451471
Always returns a timezone-aware datetime (defaults to `UTC` if no timezone specified).
452472
473+
Args:
474+
dt_str: A datetime string in ISO8601/RFC3339 format, Unix timestamp (int/str),
475+
or other recognizable datetime format.
476+
formats: Optional list of format strings to try before falling back to more
477+
forgiving parsing logic. If provided, these formats will be tried in order.
478+
disallow_other_formats: If True, only the provided formats will be used for parsing,
479+
and more forgiving parsing logic will not be attempted.
480+
453481
Example:
454482
>>> ab_datetime_try_parse("2023-03-14T15:09:26Z") # Returns AirbyteDateTime
455483
>>> ab_datetime_try_parse("2023-03-14 15:09:26Z") # Missing 'T' delimiter still parsable
456484
>>> ab_datetime_try_parse("2023-03-14") # Returns midnight UTC time
485+
>>> ab_datetime_try_parse("2023-03-14 15:09:26", formats=["%Y-%m-%d %H:%M:%S"]) # Using specific format
457486
"""
458487
try:
459-
return ab_datetime_parse(dt_str)
488+
return ab_datetime_parse(dt_str, formats=formats, disallow_other_formats=disallow_other_formats)
460489
except (ValueError, TypeError):
461490
return None
462491

0 commit comments

Comments
 (0)