时区¶
默认情况下启用对时区的支持。Airflow 在内部和数据库中以 UTC 格式存储日期时间信息。它允许您使用依赖时区的计划运行 DAG。目前,Airflow 不会在用户界面中将其转换为最终用户的时区。它将始终在那里以 UTC 格式显示。此外,操作符中使用的模板也不会进行转换。时区信息是公开的,由 DAG 的编写者决定如何处理它。
如果您的用户居住在多个时区,并且您希望根据每个用户的时钟显示日期时间信息,这将非常方便。
即使您仅在一个时区中运行 Airflow,在数据库中以 UTC 格式存储数据仍然是一个好习惯(在 Airflow 具有时区感知能力之前,这也是推荐甚至必需的设置)。主要原因是许多国家/地区使用夏令时 (DST),在春季将时钟向前拨,在秋季向后拨。如果您使用本地时间,则在转换发生时,您很可能会每年遇到两次错误。(pendulum 和 pytz 文档更详细地讨论了这些问题。)这对于简单的 DAG 可能并不重要,但如果您在(例如)金融服务领域,需要满足每天结束的截止日期,则这是一个问题。
时区在 airflow.cfg
中设置。默认情况下,它设置为 UTC,但您可以将其更改为使用系统的设置或任意 IANA 时区,例如 Europe/Amsterdam
。它依赖于 pendulum
,它比 pytz
更准确。安装 Airflow 时会安装 Pendulum。
注意
Pendulum 默认依赖于自己的时区数据库,该数据库的更新频率不如 IANA 数据库。您可以通过将 PYTZDATA_TZDATADIR
环境变量设置为系统的数据库来使 Pendulum 依赖于系统的数据库,例如 /usr/share/zoneinfo
。
Web UI¶
默认情况下,Web UI 将以 UTC 格式显示时间。可以使用右上角的菜单更改显示的时区(单击时钟以激活它)
“本地”是从浏览器的时区检测到的。“服务器”值来自 [core]
部分中的 default_timezone
设置。
用户选择的时区存储在 LocalStorage 中,因此是每个浏览器的设置。
注意
如果您已将 Airflow 安装配置为使用不同的默认时区,并且希望 UI 使用相同的时区,请将 [webserver]
部分中的 default_ui_timezone
设置为空字符串或相同的值。
(它目前默认为 UTC,以保持 UI 在点发布之间默认行为的一致性。)
概念¶
朴素和感知日期时间对象¶
Python 的 datetime.datetime 对象具有一个 tzinfo 属性,该属性可用于存储时区信息,表示为 datetime.tzinfo 子类的实例。当设置此属性并描述偏移量时,日期时间对象是感知的。否则,它是朴素的。
您可以使用 timezone.is_localized()
和 timezone.is_naive()
来确定日期时间是感知的还是朴素的。
因为 Airflow 使用时区感知的日期时间对象。如果您的代码创建日期时间对象,它们也需要是感知的。
from airflow.utils import timezone
now = timezone.utcnow()
a_date = timezone.datetime(2017, 1, 1)
朴素日期时间对象的解释¶
尽管 Airflow 完全以时区感知的方式运行,但它仍然接受 DAG 定义中的 start_dates
和 end_dates
的朴素日期时间对象。这主要是为了保留向后兼容性。如果遇到朴素的 start_date
或 end_date
,则应用默认时区。它的应用方式是假设朴素日期时间已位于默认时区。换句话说,如果您的默认时区设置为 Europe/Amsterdam
,并且创建了 datetime(2017, 1, 1)
的朴素日期时间 start_date
,则假定它是 2017 年 1 月 1 日阿姆斯特丹时间的 start_date
。
dag = DAG(
"my_dag",
start_date=pendulum.datetime(2017, 1, 1, tz="UTC"),
default_args={"retries": 3},
)
op = BashOperator(task_id="hello_world", bash_command="Hello World!", dag=dag)
print(op.retries) # 3
不幸的是,在 DST 转换期间,某些日期时间不存在或不明确。在这种情况下,pendulum 会引发异常。这就是为什么在启用时区支持时,您应该始终创建感知日期时间对象的原因。
实际上,这很少成为问题。Airflow 在模型和 DAG 中为您提供时区感知的日期时间对象,并且大多数情况下,新的日期时间对象是通过 timedelta 算法从现有对象创建的。应用程序代码中经常创建的唯一日期时间是当前时间,并且 timezone.utcnow()
会自动执行正确的操作。
时区感知的 DAG¶
创建一个时区感知的 DAG 非常简单。只需确保使用 pendulum
提供一个时区感知的 start_date
即可。不要尝试使用标准库 timezone,因为它们已知存在限制,并且我们故意禁止在 DAG 中使用它们。
import pendulum
dag = DAG("my_tz_dag", start_date=pendulum.datetime(2016, 1, 1, tz="Europe/Amsterdam"))
op = EmptyOperator(task_id="empty", dag=dag)
print(dag.timezone) # <Timezone [Europe/Amsterdam]>
请注意,虽然可以为任务设置 start_date
和 end_date
,但 DAG 时区或全局时区(按此顺序)将始终用于计算数据间隔。第一次遇到时,开始日期或结束日期将使用与 start_date
或 end_date
关联的时区转换为 UTC,然后对于计算,将忽略此时间信息。
注意
在创作时区感知 DAG 时,您必须确保基础时区库(例如:pendulum)已使用法规的最新更改(夏令时更改等)进行更新。当预期发生时间更改时,您应该使用基础时区库验证切换是否按预期发生。可能需要更新库版本。作为一般建议,如果您可以用 UTC 创作 DAG,则首选这种方式。
模板¶
Airflow 在模板中返回时区感知的日期时间,但不会将其转换为本地时间,因此它们仍保留在 UTC 中。这留给 DAG 来处理。
import pendulum
local_tz = pendulum.timezone("Europe/Amsterdam")
local_tz.convert(logical_date)
Cron 调度¶
使用 cron 调度的时区感知 DAG 遵循夏令时。例如,在 US/Eastern
时区中具有起始日期且计划为 0 0 * * *
的 DAG 将在夏令时期间每天在 04:00 UTC 运行,否则在 05:00 运行。
时间差¶
使用 timedelta
或 relativedelta
调度的时间感知 DAG 会考虑开始日期夏令时的影响,但在安排后续运行时不会调整夏令时。例如,一个开始日期为 pendulum.datetime(2020, 1, 1, tz="UTC")
并且调度间隔为 timedelta(days=1)
的 DAG 将每天在 UTC 时间 05:00 运行,而不会考虑夏令时。