-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathmacros.py
More file actions
197 lines (149 loc) · 5.2 KB
/
macros.py
File metadata and controls
197 lines (149 loc) · 5.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
#
import builtins
import datetime
import typing
from typing import Optional, Union
import isodate
import pytz
from dateutil import parser
from isodate import parse_duration
from airbyte_cdk.sources.declarative.datetime.datetime_parser import DatetimeParser
"""
This file contains macros that can be evaluated by a `JinjaInterpolation` object
"""
def now_utc() -> datetime.datetime:
"""
Current local date and time in UTC timezone
Usage:
`"{{ now_utc() }}"`
"""
return datetime.datetime.now(datetime.timezone.utc)
def today_utc() -> datetime.date:
"""
Current date in UTC timezone
Usage:
`"{{ today_utc() }}"`
"""
return datetime.datetime.now(datetime.timezone.utc).date()
def today_with_timezone(timezone: str) -> datetime.date:
"""
Current date in custom timezone
:param timezone: timezone expressed as IANA keys format. Example: "Pacific/Tarawa"
:return:
"""
return datetime.datetime.now(tz=pytz.timezone(timezone)).date()
def timestamp(dt: Union[float, str]) -> Union[int, float]:
"""
Converts a number or a string to a timestamp
If dt is a number, then convert to an int
If dt is a string, then parse it using dateutil.parser
Usage:
`"{{ timestamp(1658505815.223235) }}"
:param dt: datetime to convert to timestamp
:return: unix timestamp
"""
if isinstance(dt, (int, float)):
return int(dt)
else:
return str_to_datetime(dt).astimezone(pytz.utc).timestamp()
def str_to_datetime(s: str) -> datetime.datetime:
"""
Converts a string to a datetime object with UTC timezone
If the input string does not contain timezone information, UTC is assumed.
Supports both basic date strings like "2022-01-14" and datetime strings with optional timezone
like "2022-01-01T13:45:30+00:00".
Usage:
`"{{ str_to_datetime('2022-01-14') }}"`
:param s: string to parse as datetime
:return: datetime object in UTC timezone
"""
parsed_date = parser.isoparse(s)
if not parsed_date.tzinfo:
# Assume UTC if the input does not contain a timezone
parsed_date = parsed_date.replace(tzinfo=pytz.utc)
return parsed_date.astimezone(pytz.utc)
def max(*args: typing.Any) -> typing.Any:
"""
Returns biggest object of an iterable, or two or more arguments.
max(iterable, *[, default=obj, key=func]) -> value
max(arg1, arg2, *args, *[, key=func]) -> value
Usage:
`"{{ max(2,3) }}"
With a single iterable argument, return its biggest item. The
default keyword-only argument specifies an object to return if
the provided iterable is empty.
With two or more arguments, return the largest argument.
:param args: args to compare
:return: largest argument
"""
return builtins.max(*args)
def min(*args: typing.Any) -> typing.Any:
"""
Returns smallest object of an iterable, or two or more arguments.
min(iterable, *[, default=obj, key=func]) -> value
min(arg1, arg2, *args, *[, key=func]) -> value
Usage:
`"{{ min(2,3) }}"
With a single iterable argument, return its smallest item. The
default keyword-only argument specifies an object to return if
the provided iterable is empty.
With two or more arguments, return the smallest argument.
:param args: args to compare
:return: smallest argument
"""
return builtins.min(*args)
def day_delta(num_days: int, format: str = "%Y-%m-%dT%H:%M:%S.%f%z") -> str:
"""
Returns datetime of now() + num_days
Usage:
`"{{ day_delta(25) }}"`
:param num_days: number of days to add to current date time
:return: datetime formatted as RFC3339
"""
return (
datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=num_days)
).strftime(format)
def duration(datestring: str) -> Union[datetime.timedelta, isodate.Duration]:
"""
Converts ISO8601 duration to datetime.timedelta
Usage:
`"{{ now_utc() - duration('P1D') }}"`
"""
return parse_duration(datestring)
def format_datetime(
dt: Union[str, datetime.datetime, int], format: str, input_format: Optional[str] = None
) -> str:
"""
Converts datetime to another format
Usage:
`"{{ format_datetime(config.start_date, '%Y-%m-%d') }}"`
CPython Datetime package has known bug with `stfrtime` method: '%s' formatting uses locale timezone
https://github.com/python/cpython/issues/77169
https://github.com/python/cpython/issues/56959
"""
if isinstance(dt, datetime.datetime):
return dt.strftime(format)
if isinstance(dt, int):
dt_datetime = DatetimeParser().parse(dt, input_format if input_format else "%s")
else:
dt_datetime = (
datetime.datetime.strptime(dt, input_format) if input_format else str_to_datetime(dt)
)
if dt_datetime.tzinfo is None:
dt_datetime = dt_datetime.replace(tzinfo=pytz.utc)
return DatetimeParser().format(dt=dt_datetime, format=format)
_macros_list = [
now_utc,
today_utc,
timestamp,
max,
min,
day_delta,
duration,
format_datetime,
today_with_timezone,
str_to_datetime,
]
macros = {f.__name__: f for f in _macros_list}