时区¶
默认情况下启用对时区的支持。Airflow 在内部和数据库中以 UTC 格式存储 datetime 信息。它允许您使用依赖时区的调度运行 DAG。目前,Airflow 不会在用户界面中将它们转换为最终用户的时区。它将始终在那里以 UTC 格式显示。此外,操作符中使用的模板也不会转换。时区信息是公开的,由 DAG 的编写者决定如何处理它。
如果您的用户居住在多个时区,并且您想根据每个用户的挂钟显示 datetime 信息,这将非常方便。
即使你只在一个时区中运行 Airflow,在数据库中以 UTC 存储数据仍然是一种好习惯(在 Airflow 变得支持时区之前,这也是推荐的甚至必需的设置)。主要原因是许多国家/地区使用夏令时 (DST),其中时钟在春季调快,在秋季调慢。如果你在当地时间工作,则可能会在过渡发生时每年遇到两次错误。(Pendulum 和 pytz 文档更详细地讨论了这些问题。)对于简单的 DAG 来说,这可能无关紧要,但如果你从事金融服务等行业,则会出现问题,因为你必须满足一天结束时的截止时间。
时区在 airflow.cfg
中设置。默认情况下,它设置为 UTC,但你可以更改它以使用系统的设置或任意 IANA 时区,例如 Europe/Amsterdam
。它依赖于 pendulum
,它比 pytz
更准确。当你安装 Airflow 时,Pendulum 会被安装。
注意
Pendulum 默认情况下依赖于其自己的时区数据库,该数据库的更新频率不如 IANA 数据库。你可以通过将 PYTZDATA_TZDATADIR
环境变量设置为系统的数据库(例如 /usr/share/zoneinfo
)来使 Pendulum 依赖于系统的数据库。
Web UI¶
默认情况下,Web UI 将以 UTC 显示时间。可以通过使用右上角的菜单更改显示的时区(单击时钟以激活它)
“本地”是从浏览器的时区检测到的。“服务器”值来自 [core]
部分中的 default_timezone
设置。
用户选择的时区存储在 LocalStorage 中,因此是按浏览器设置的。
注意
如果你已将 Airflow 安装配置为使用不同的默认时区,并且希望 UI 使用此相同时区,请将 [webserver]
部分中的 default_ui_timezone
设置为空字符串或相同的值。
(它当前默认为 UTC,以便在小版本发布之间保持 UI 行为的一致性。)
概念¶
朴素和感知日期时间对象¶
Python 的 datetime.datetime 对象有一个 tzinfo 属性,可用于存储时区信息,表示为 datetime.tzinfo 子类的实例。当设置此属性并描述偏移量时,datetime 对象是已知的。否则,它就是朴素的。
你可以使用 timezone.is_localized()
和 timezone.is_naive()
来确定日期时间是已知的还是朴素的。
因为 Airflow 使用时区已知的 datetime 对象。如果你的代码创建 datetime 对象,它们也需要已知。
from airflow.utils import timezone
now = timezone.utcnow()
a_date = timezone.datetime(2017, 1, 1)
朴素 datetime 对象的解释¶
尽管 Airflow 完全以时区已知的方式操作,但它仍然接受你的 DAG 定义中的 start_dates
和 end_dates
的朴素日期时间对象。这主要是为了保持向后兼容性。如果遇到朴素的 start_date
或 end_date
,则应用默认时区。它的应用方式是假设朴素日期时间已经在默认时区中。换句话说,如果你将默认时区设置为 Europe/Amsterdam
,并创建一个朴素的 start_date
datetime(2017, 1, 1)
,则假定它为 2017 年 1 月 1 日阿姆斯特丹时间。
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 会引发异常。这就是为什么在启用时区支持时你应该始终创建已知的 datetime 对象的原因。
在实践中,这很少成为问题。Airflow 在模型和 DAG 中为你提供了已知的 datetime 对象,而且大多数情况下,新的 datetime 对象是通过 timedelta 算术从现有对象创建的。在应用程序代码中经常创建的唯一 datetime 是当前时间,而 timezone.utcnow()
会自动执行正确操作。
支持时区的 DAG¶
创建支持时区的 DAG 非常简单。只需确保使用 pendulum
提供支持时区的 start_date
即可。不要尝试使用标准库 时区,因为众所周知它们存在局限性,并且我们故意不允许在 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 将在每天 05:00 UTC 运行,而不管夏令时如何。