Under which category would you file this issue?
Airflow Core
Apache Airflow version
3.2.1
What happened and how to reproduce it?
The Calendar view on the DAG detail page does not respect the user's selected timezone from the UI timezone selector. All dates — both in the API query parameters and in the rendered calendar grid — are computed using either browser-local time or hardcoded UTC, while the rest of the UI (e.g. the "Start Date" column on the Runs tab) correctly converts timestamps to the user-selected timezone.
Observed behavior: Changing the timezone via the UI timezone selector has no effect on the Calendar view. Runs are grouped into the wrong day/hour cells for users whose selected timezone differs from UTC.
Comparison with Runs tab: On the Runs tab, the Start Date column properly updates when the user changes their timezone setting, confirming the timezone selector itself works correctly.
How to reproduce
- Set your Airflow UI timezone to something significantly offset from UTC (e.g.
Asia/Tokyo — UTC+9, or America/Los_Angeles — UTC-7).
- Navigate to a DAG that has runs near midnight UTC.
- Open the Calendar tab → observe that runs are grouped by their UTC date/hour, not by the selected timezone.
- Switch to the Runs tab → observe that the
Start Date column correctly shows times in the selected timezone.
Root cause analysis
The entire Calendar feature tree (Calendar.tsx, calendarUtils.ts, HourlyCalendarView.tsx, DailyCalendarView.tsx) was written using plain dayjs() without any awareness of the useTimezone() / TimezoneContext system that the rest of the Airflow UI uses. The dayjs/plugin/timezone and dayjs/plugin/utc plugins are not even imported in any of these files.
There are three specific problem areas:
1. Calendar.tsx — API query dates hardcode a literal "Z" suffix
// Current code (Calendar.tsx)
const gte = startDate.format("YYYY-MM-DD[T]HH:mm:ss[Z]");
const lte = endDate.format("YYYY-MM-DD[T]HH:mm:ss[Z]");
[Z] in a dayjs format string is a literal character escape — it unconditionally appends the letter Z (meaning "UTC") to whatever the browser-local time value is. This tells the API the timestamp is UTC when it is not. The component never calls useTimezone().
Compare with the Time component (used by the Runs tab), which correctly does:
// Time.tsx — correct pattern
const { selectedTimezone } = useTimezone();
const formattedTime = time.tz(selectedTimezone).format(format);
2. calendarUtils.ts — Run grouping uses raw UTC string slicing
// createDailyDataMap
const dateStr = run.date.slice(0, 10); // "YYYY-MM-DD" — always UTC
// createHourlyDataMap
const hourStr = run.date.slice(0, 13); // "YYYY-MM-DDTHH" — always UTC
If the API returns a UTC timestamp like 2024-01-15T23:30:00Z, this groups the run into January 15. But for a user in Asia/Tokyo (UTC+9), that run occurred on January 16 at 08:30 and should appear in that day's cell.
The generateDailyCalendarData and generateHourlyCalendarData functions also build their calendar grids using plain dayjs() (browser-local), with no timezone parameter.
3. HourlyCalendarView.tsx / DailyCalendarView.tsx — Display formatting uses plain dayjs
Both views format display labels (day number, weekday name, month abbreviation) with plain dayjs(day.day) calls that don't go through timezone conversion.
Suggested fix
Thread selectedTimezone from the useTimezone() hook through the component tree:
Calendar.tsx: Import useTimezone, compute the date range in the selected timezone, convert to UTC for the API query.
calendarUtils.ts: Add a timezone parameter to createDailyDataMap, createHourlyDataMap, generateDailyCalendarData, generateHourlyCalendarData, and calculateDataBounds. Use dayjs(run.date).tz(timezone) instead of string slicing for grouping.
- Views: Pass the timezone through and use it for display formatting.
This follows the same pattern already established by src/components/Time.tsx and src/utils/datetimeUtils.ts.
Affected files
airflow-core/src/airflow/ui/src/pages/Dag/Calendar/Calendar.tsx
airflow-core/src/airflow/ui/src/pages/Dag/Calendar/calendarUtils.ts
airflow-core/src/airflow/ui/src/pages/Dag/Calendar/HourlyCalendarView.tsx
airflow-core/src/airflow/ui/src/pages/Dag/Calendar/DailyCalendarView.tsx
What you think should happen instead?
The Calendar view should respect the user-selected timezone (from useTimezone() / TimezoneContext) consistently with the rest of the UI. Specifically:
- The date range sent to the API should be computed in the selected timezone and then converted to UTC.
- Runs should be grouped into day/hour buckets based on the selected timezone, not by raw UTC string slicing.
- Display labels (day numbers, weekday names, month headers) should reflect the selected timezone.
Operating System
Red Hat Enterprise Linux 9.7
Deployment
Virtualenv installation
Apache Airflow Provider(s)
No response
Versions of Apache Airflow Providers
No response
Official Helm Chart version
Not Applicable
Kubernetes Version
Not Applicable
Helm Chart configuration
Not Applicable
Docker Image customizations
No response
Anything else?
The CalendarCell.tsx tooltip display may also need updating if it shows raw date strings, but the four files listed above are the core of the issue.
Are you willing to submit PR?
Code of Conduct
Under which category would you file this issue?
Airflow Core
Apache Airflow version
3.2.1
What happened and how to reproduce it?
The Calendar view on the DAG detail page does not respect the user's selected timezone from the UI timezone selector. All dates — both in the API query parameters and in the rendered calendar grid — are computed using either browser-local time or hardcoded UTC, while the rest of the UI (e.g. the "Start Date" column on the Runs tab) correctly converts timestamps to the user-selected timezone.
Observed behavior: Changing the timezone via the UI timezone selector has no effect on the Calendar view. Runs are grouped into the wrong day/hour cells for users whose selected timezone differs from UTC.
Comparison with Runs tab: On the Runs tab, the
Start Datecolumn properly updates when the user changes their timezone setting, confirming the timezone selector itself works correctly.How to reproduce
Asia/Tokyo— UTC+9, orAmerica/Los_Angeles— UTC-7).Start Datecolumn correctly shows times in the selected timezone.Root cause analysis
The entire Calendar feature tree (
Calendar.tsx,calendarUtils.ts,HourlyCalendarView.tsx,DailyCalendarView.tsx) was written using plaindayjs()without any awareness of theuseTimezone()/TimezoneContextsystem that the rest of the Airflow UI uses. Thedayjs/plugin/timezoneanddayjs/plugin/utcplugins are not even imported in any of these files.There are three specific problem areas:
1.
Calendar.tsx— API query dates hardcode a literal "Z" suffix[Z]in a dayjs format string is a literal character escape — it unconditionally appends the letterZ(meaning "UTC") to whatever the browser-local time value is. This tells the API the timestamp is UTC when it is not. The component never callsuseTimezone().Compare with the
Timecomponent (used by the Runs tab), which correctly does:2.
calendarUtils.ts— Run grouping uses raw UTC string slicingIf the API returns a UTC timestamp like
2024-01-15T23:30:00Z, this groups the run into January 15. But for a user inAsia/Tokyo(UTC+9), that run occurred on January 16 at 08:30 and should appear in that day's cell.The
generateDailyCalendarDataandgenerateHourlyCalendarDatafunctions also build their calendar grids using plaindayjs()(browser-local), with no timezone parameter.3.
HourlyCalendarView.tsx/DailyCalendarView.tsx— Display formatting uses plain dayjsBoth views format display labels (day number, weekday name, month abbreviation) with plain
dayjs(day.day)calls that don't go through timezone conversion.Suggested fix
Thread
selectedTimezonefrom theuseTimezone()hook through the component tree:Calendar.tsx: ImportuseTimezone, compute the date range in the selected timezone, convert to UTC for the API query.calendarUtils.ts: Add atimezoneparameter tocreateDailyDataMap,createHourlyDataMap,generateDailyCalendarData,generateHourlyCalendarData, andcalculateDataBounds. Usedayjs(run.date).tz(timezone)instead of string slicing for grouping.This follows the same pattern already established by
src/components/Time.tsxandsrc/utils/datetimeUtils.ts.Affected files
airflow-core/src/airflow/ui/src/pages/Dag/Calendar/Calendar.tsxairflow-core/src/airflow/ui/src/pages/Dag/Calendar/calendarUtils.tsairflow-core/src/airflow/ui/src/pages/Dag/Calendar/HourlyCalendarView.tsxairflow-core/src/airflow/ui/src/pages/Dag/Calendar/DailyCalendarView.tsxWhat you think should happen instead?
The Calendar view should respect the user-selected timezone (from
useTimezone()/TimezoneContext) consistently with the rest of the UI. Specifically:Operating System
Red Hat Enterprise Linux 9.7
Deployment
Virtualenv installation
Apache Airflow Provider(s)
No response
Versions of Apache Airflow Providers
No response
Official Helm Chart version
Not Applicable
Kubernetes Version
Not Applicable
Helm Chart configuration
Not Applicable
Docker Image customizations
No response
Anything else?
The
CalendarCell.tsxtooltip display may also need updating if it shows raw date strings, but the four files listed above are the core of the issue.Are you willing to submit PR?
Code of Conduct