# 时区

> 贡献者：[@morefreeze](https://github.com/morefreeze)

默认情况下启用对时区的支持。 Airflow 在内部处理和数据库中以 UTC 格式存储日期时间信息。 这样您就可以在调度中执行带有时区的 DAG。 目前，Airflow 不支持在 Web UI 中显示最终用户时区，它始终以 UTC 显示。 此外，`Operator`中使用的模板也不会被转换。 时区信息取决于 DAG 作者如何使用它。

如果您的用户居住在多个时区，并且您希望根据每个用户的挂钟(wall clock)显示日期时间信息，这将非常方便。

即使您只在一个时区运行 Airflow，在数据库中以 UTC 格式存储数据仍然是一种很好的做法（在 Airflow 关心时区问题之前也是如此，这也是推荐的甚至是必需的设置）。 主要原因是夏令时（DST）。 许多国家都有 DST 系统，其中时间在春季向前移动，在秋季向后移动。 如果您在当地工作，那么当发生转换时，您可能每年会遇到两次错误。 （pendulum 和 pytz 文档更详细地讨论了这些问题。）这可能并不影响简单的 DAG，但如果您涉及金融服务中，这可能就变成灾难。

时区在`airflow.cfg`中设置。 默认情况下，它设置为 utc，您可以将其更改为使用系统设置或任意 IANA 时区，例如`Europe/Amsterdam` (阿姆斯特丹)。 安装 Airflow 时会安装 Pendulum，它比 pytz 更精确，也会让您更容易处理时区问题。

请注意，**Web UI 目前仅显示 UTC 时间**。

## 概念

### 了解 datetime 时区敏感对象

tzinfo 是 datetime.datetime 对象中表示时区信息的属性，它是 datetime.tzinfo 类的对象。 设置 tzinfo 会使 datetime 对象变为时区敏感，否则就是不敏感。

您可以使用`timezone.is_aware()`和`timezone.is_naive()`来确定 datetime 是否时区敏感。

因为 Airflow 使用时区敏感 datetime 对象，所以您创建 datetime 对象时也应该考虑时区问题。

```py
from airflow.utils import timezone

now = timezone.utcnow()
a_date = timezone.datetime(2017,1,1)
```

### 理解原生 datetime 对象

尽管 Airflow 现在是时区敏感的，但为了向后兼容，它仍然允许在 DAG 定义中使用原生 datetime 为`start_dates`和`end_dates`赋值。 如果遇到使用原生 datetime 的`start_date`或`end_date`则使用默认时区。 换句话说，如果您设置了默认时区为`Europe/Amsterdam`，那么对于`start_date = datetime(2017,1,1)`，则认定它是 2017 年 1 月 1 日的 Amesterdam 时间。

```py
 default_args = dict(
    start_date = datetime(2016, 1, 1),
    owner = 'Airflow'
)

dag = DAG ('my_dag', default_args=default_args)
op = DummyOperator(task_id='dummy', dag=dag)
print(op.owner) # Airflow

```

不幸的是，在 DST 转换期间，某些时间不存在或有二义性。 在这种情况下，pendulum 会抛出异常。 这就是为什么在启用时区支持时应始终创建时区敏感的 datetime 对象。

实际上，这个问题几乎不会发生。 在 model 或 DAG 中，Airflow 总是返回给您的是时区敏感的 datetime 对象，通常新的 datetime 对象是通过现有对象和 timedelta 计算而来的。 唯一经常创建并且可能出问题的是当前时间，用`timezone.utcnow()`会避免这些麻烦。

### 默认时区

默认时区是在`airflow.cfg`中的`[core]`下的`default_timezone`定义的。 如果您刚刚安装了 Airflow，它推荐您设置为 utc。 您还可以将其设置为系统或 IANA 时区（例如`Europe/Amsterdam`）。 DAG 也会在 worker 上进行计算，因此确保所有 worker 节点上的此设置相同。

```py
[core]
default_timezone = utc
```

## 时区敏感 DAG

只需设置`start_date`为时区敏感的 datetime，就创建了时区敏感的 DAG。 建议使用 pendulum，但也可以使用 pytz（手动安装）。

```py
import pendulum

local_tz = pendulum.timezone("Europe/Amsterdam")

default_args = dict (
    start_date = datetime(2016, 1, 1, tzinfo=local_tz),
    owner = 'Airflow'
)

dag = DAG('my_tz_dag', default_args=default_args)
op = DummyOperator(task_id='dummy', dag=dag)
print(dag.timezone) # <Timezone [Europe/Amsterdam]>
```

### 模板

Airflow 在模板中返回时区敏感的 datetime，但不会将它们转换为本地时间，因此它们仍然是 UTC 时区。 由 DAG 来处理转换。

```py
import pendulum

local_tz = pendulum.timezone("Europe/Amsterdam")
local_tz.convert(execution_date)
```

### Cron 安排

如果您设置了 crontab，Airflow 会假定您始终希望在同一时间运行。 然后它将忽略 DST。 比如，您有一个调度要在每天格林威治标准时间 +1 的 08:00 运行，它将始终在这个时间运行，**无论 DST 是否发生**。

### 时间增量调度

对于具有时间增量的调度，Airflow 假定您始终希望以指定的间隔运行。 比如，您指定`timedelta(hours=2)`运行，它将始终每两小时运行一次。 在这种情况下，**将考虑 DST**。
