Skip to content

Commit 5fe60b7

Browse files
authored
Airbyte CDK: use pytz.utc instead of datetime.utc (#38026)
Signed-off-by: Artem Inzhyyants <[email protected]>
1 parent 8e4cecf commit 5fe60b7

File tree

2 files changed

+27
-11
lines changed
  • airbyte-cdk/python

2 files changed

+27
-11
lines changed

airbyte-cdk/python/airbyte_cdk/sources/declarative/interpolation/macros.py

+20-11
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
import builtins
66
import datetime
7-
import numbers
7+
import typing
88
from typing import Union
99

10+
import isodate
11+
import pytz
1012
from dateutil import parser
1113
from isodate import parse_duration
1214

@@ -15,7 +17,7 @@
1517
"""
1618

1719

18-
def now_utc():
20+
def now_utc() -> datetime.datetime:
1921
"""
2022
Current local date and time in UTC timezone
2123
@@ -25,7 +27,7 @@ def now_utc():
2527
return datetime.datetime.now(datetime.timezone.utc)
2628

2729

28-
def today_utc():
30+
def today_utc() -> datetime.date:
2931
"""
3032
Current date in UTC timezone
3133
@@ -35,7 +37,7 @@ def today_utc():
3537
return datetime.datetime.now(datetime.timezone.utc).date()
3638

3739

38-
def timestamp(dt: Union[numbers.Number, str]):
40+
def timestamp(dt: Union[float, str]) -> Union[int, float]:
3941
"""
4042
Converts a number or a string to a timestamp
4143
@@ -48,21 +50,21 @@ def timestamp(dt: Union[numbers.Number, str]):
4850
:param dt: datetime to convert to timestamp
4951
:return: unix timestamp
5052
"""
51-
if isinstance(dt, numbers.Number):
53+
if isinstance(dt, (int, float)):
5254
return int(dt)
5355
else:
54-
return _str_to_datetime(dt).astimezone(datetime.timezone.utc).timestamp()
56+
return _str_to_datetime(dt).astimezone(pytz.utc).timestamp()
5557

5658

5759
def _str_to_datetime(s: str) -> datetime.datetime:
5860
parsed_date = parser.isoparse(s)
5961
if not parsed_date.tzinfo:
6062
# Assume UTC if the input does not contain a timezone
61-
parsed_date = parsed_date.replace(tzinfo=datetime.timezone.utc)
62-
return parsed_date.astimezone(datetime.timezone.utc)
63+
parsed_date = parsed_date.replace(tzinfo=pytz.utc)
64+
return parsed_date.astimezone(pytz.utc)
6365

6466

65-
def max(*args):
67+
def max(*args: typing.Any) -> typing.Any:
6668
"""
6769
Returns biggest object of an iterable, or two or more arguments.
6870
@@ -95,7 +97,7 @@ def day_delta(num_days: int, format: str = "%Y-%m-%dT%H:%M:%S.%f%z") -> str:
9597
return (datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=num_days)).strftime(format)
9698

9799

98-
def duration(datestring: str) -> datetime.timedelta:
100+
def duration(datestring: str) -> Union[datetime.timedelta, isodate.Duration]:
99101
"""
100102
Converts ISO8601 duration to datetime.timedelta
101103
@@ -111,10 +113,17 @@ def format_datetime(dt: Union[str, datetime.datetime], format: str) -> str:
111113
112114
Usage:
113115
`"{{ format_datetime(config.start_date, '%Y-%m-%d') }}"`
116+
117+
CPython Datetime package has known bug with `stfrtime` method: '%s' formatting uses locale timezone
118+
https://github.com/python/cpython/issues/77169
119+
https://github.com/python/cpython/issues/56959
114120
"""
115121
if isinstance(dt, datetime.datetime):
116122
return dt.strftime(format)
117-
return _str_to_datetime(dt).strftime(format)
123+
dt_datetime = _str_to_datetime(dt)
124+
if format == "%s":
125+
return str(int(dt_datetime.timestamp()))
126+
return dt_datetime.strftime(format)
118127

119128

120129
_macros_list = [now_utc, today_utc, timestamp, max, day_delta, duration, format_datetime]

airbyte-cdk/python/unit_tests/sources/declarative/interpolation/test_macros.py

+7
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,10 @@ def test_timestamp(test_name, input_value, expected_output):
7171
timestamp_function = macros["timestamp"]
7272
actual_output = timestamp_function(input_value)
7373
assert actual_output == expected_output
74+
75+
76+
def test_utc_datetime_to_local_timestamp_conversion():
77+
"""
78+
This test ensures correct timezone handling independent of the timezone of the system on which the sync is running.
79+
"""
80+
assert macros["format_datetime"](dt="2020-10-01T00:00:00Z", format="%s") == "1601510400"

0 commit comments

Comments
 (0)