GitHub Actions runs its cron jobs in UTC. No exceptions. UTC is the right choice for infrastructure that lives across datacenters: it does not drift, it does not shift, every machine in every region agrees on what time it is. But UTC does not know that your organization has a concept called a business window, and it does not know that business windows in the Central timezone move by an hour every spring and again every fall.
So when you write a cron expression that fires at 5pm UTC because that translates to 11am Central Standard Time, that expression is correct for exactly six months of the year. The other six months, when Central Daylight Time is in effect, your 5pm UTC cron fires at noon. Maybe that is fine. Maybe it is not. But it is not what you intended.
I kept thinking the answer was to update the expression twice a year. Put it on the calendar. Remember to do it. Except that is the kind of thing that gets missed. And missing it meant a deploy firing when the team was not watching and the environment was not ready.
The actual solution has two parts that work together. The first: register both cron schedules, one for the standard-time UTC offset of your window and one for the daylight-time offset. That way the pipeline always fires near the right time regardless of which half of the year it is. The second: at runtime, the pipeline reads the actual local hour in the target timezone and, if it finds itself outside the intended window, it exits cleanly without running the deployment. The dual cron entries are the net; the runtime check is the knot that closes it.
What I kept returning to was that this is a translation problem, not a scheduling problem. UTC is the machine's language. Business windows are the team's language. The gap between them is where incidents happen. The pipeline needed to learn to translate between the two, not just once at configuration time but on every run.
DST transitions are predictable. They happen on a known date each year. But predictable is not the same as automatic, and in the space between those two words is where you find deploys running at noon when they were supposed to run at eleven.
Build the translation in once. Let the pipeline carry it.