
airflow web ui 显示的“last run time”实际指 dag 数据区间(data interval)的起始时间,而任务日志和 dag 详情页中显示的时间则是数据区间的结束时间,二者天然相差一个调度周期,并非时区或配置错误。
在 Apache Airflow 中,“Last Run Time”这一字段常被误解为任务实际触发或完成的时间,但其本质是 DAG 的 data interval start(数据区间起始时间),而非执行时间戳(execution_date 在旧版本中已弃用,现统一由 data interval 定义)。Airflow 的调度模型基于数据驱动:每个 DAG 运行对应处理一段历史数据窗口,例如:
- 若 schedule_interval = '0 0 * * *'(每天 UTC 00:00 执行),则:
- Data Interval Start: 2024-05-10T00:00:00+00:00
- Data Interval End: 2024-05-11T00:00:00+00:00
- 此次 DAG 实际在 2024-05-11T00:00:00+00:00(即 interval end)之后被调度器触发(通常略有延迟),用于处理 [2024-05-10, 2024-05-11) 区间的数据。
因此,您在首页看到的 “Last Run Time”(如 2024-05-10)是 data interval start;点击进入 DAG 页面后,顶部显示的 Execution Date 或日志中的 logical_date 默认展示的是 data interval end(即 2024-05-11),这正是您观察到“小时+1”(或日期+1)现象的根本原因——它反映的是同一调度周期的区间终点,而非 bug 或时区偏差。
✅ 验证方式(在 DAG 文件中添加调试逻辑):
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime, timedelta
def print_intervals(**context):
dag_run = context['dag_run']
print(f"Data Interval Start: {dag_run.data_interval_start}")
print(f"Data Interval End: {dag_run.data_interval_end}")
print(f"Logical Date (alias for start): {context['logical_date']}")
with DAG(
'debug_data_interval',
schedule_interval='0 0 * * *',
start_date=datetime(2024, 1, 1),
catchup=True,
) as dag:
task = PythonOperator(
task_id='print_intervals',
python_callable=print_intervals,
dag=dag
)? 注意事项:
- Airflow 2.2+ 已完全采用 data_interval_start / data_interval_end 替代旧版 execution_date,所有内部逻辑(调度、重跑、UI 渲染)均以此为准;
- Web UI 各处时间显示逻辑不同:DAG 列表页 → data_interval_start;DAG 详情页头部 & Task Instance 日志页 → data_interval_end(Airflow 默认将 logical_date 显示为 interval end 以对齐“该次运行所代表的数据截止点”);
- 无需修改时区配置(如 default_timezone)来“修复”该差异——这是设计使然,确保语义清晰:“这次运行负责处理从 X 到 Y 的数据”。
总结:这不是缺陷,而是 Airflow 数据时间模型的核心体现。理解 data interval 是掌握调度语义、正确设置 start_date、排查延迟/跳过问题的前提。建议在 DAG 文档中显式标注数据区间含义,避免团队误读。










