← Blog

Scheduled Job Shifted an Hour After DST: The Timezone Fix

When your scheduled job runs at wrong time after dst , the cause is almost often a clash between Coordinated Universal Time (UTC) and local wall-clock time. If your background worker or database runs on UTC, it does not observe daylight saving time, causing any daily trigger tied to local business hours to drift forward or backward by exactly one hour twice a year.

For non-technical founders and small teams running web applications on platforms like Bubble, Lovable, Bolt, Replit, or Supabase, this subtle clock shift can create immediate chaos. A daily digest email meant for 8:00 AM suddenly fires at 7:00 AM or 9:00 AM. A nightly billing invoice charges customers during dinner instead of midnight. Worst of all, jobs scheduled during the changeover window might fire twice in an hour or skip running entirely. This guide explains why daylight saving time (DST) disrupts automated schedules, how your specific hosting stack handles cron triggers, and how to fix your background jobs so they execute reliably year-round.

The Root Mechanism: Why Clocks Shift Scheduled Jobs

To understand why your scheduled job runs at wrong time after dst, you have to look at how computer servers track time compared to how humans track daylight. Nearly every cloud server, hosted database, and serverless runtime operates internally on Coordinated Universal Time (UTC). UTC is a continuous, linear time standard governed by atomic clocks; it rarely observes seasonal shifts, rarely adds an hour in spring, and rarely falls back in autumn.

Humans, however, schedule their lives around local wall-clock time. In regions observing daylight saving time—such as North America and Europe—civil authorities advance clocks by one hour in spring and wind them back by one hour in autumn. The rules governing these changes are maintained globally in the IANA Time Zone Database.

When you set up an automated schedule, one of two fundamental errors typically occurs:

  • The UTC Drift Problem: You set a job to run at 09:00 UTC because in winter that matches 9:00 AM in London (GMT) or 4:00 AM in New York (EST). When summer arrives and New York switches to Eastern Daylight Time (EDT, UTC-4), 09:00 UTC is now 5:00 AM local time. The server ran precisely on schedule, but to your end users, the task shifted by sixty minutes.
  • The Non-Existent or Duplicate Hour Problem: If your scheduler is configured to observe a local timezone (such as America/New_York), the clock jumping from 1:59:59 AM to 3:00:00 AM in the spring means that 2:30 AM literally does not exist on the calendar. A job scheduled for 2:30 AM will often be skipped completely. Conversely, during the autumn fallback, the hour between 1:00 AM and 2:00 AM occurs twice, causing a naive local scheduler to trigger your job a second time.

If your background automations have completely stopped triggering rather than just drifting, take a look at our companion guide on diagnosing why a scheduled job stopped running to rule out silent worker crashes or expired authentication tokens.

How Popular Platforms Handle Schedules and Timezones

Different development environments interpret schedules in distinct ways. If you built your application with visual builders, AI scaffolding tools, or serverless functions, the platform defaults determine whether your jobs run in UTC or honor local daylight adjustments.

Bubble Backend Workflows

In Bubble, scheduled API workflows and recurring events run entirely on Bubble’s backend servers, which operate on UTC. When you schedule a backend workflow from a client-side action (such as a user clicking a button), Bubble evaluates the relative time or date using the current user's local browser timezone unless you explicitly manipulate the date object. However, when you configure recurring workflows in the Bubble editor or schedule an API workflow from an existing backend workflow, the execution time is strictly anchored to UTC.

If you set a workflow to run daily at a fixed timestamp without accounting for daylight saving shifts, your users will notice the timing change whenever their local region changes clocks. For more troubleshooting advice on Bubble backend workflows, read our breakdown on fixing Bubble workflows that fail to run.

Supabase pg_cron

Supabase uses the popular PostgreSQL extension pg_cron to run periodic database tasks. By default, pg_cron interprets all standard 5-part cron syntax against UTC. If you create an entry such as:

SELECT cron.schedule('morning-sync', '0 13 * * *', 'SELECT sync_customer_data();');

That job will execute at precisely 13:00 UTC every day. In winter, 13:00 UTC corresponds to 8:00 AM Eastern Standard Time. When Eastern Daylight Time starts, that same 13:00 UTC trigger executes at 9:00 AM local time. As documented in the PostgreSQL date/time documentation, timestamp types without timezone offsets will cause unexpected calculation bugs if your query assumes the server follows your regional wall clock.

Vercel Cron Jobs

Vercel enables cron jobs via the vercel.json configuration file. Similar to classic Unix cron, Vercel cron expressions follow standard five-part scheduling syntax. According to the official Vercel Cron Jobs documentation, all expressions are evaluated strictly in UTC. Vercel does not support setting an arbitrary regional timezone inside the cron schedule definition itself. Any cron configured to hit an endpoint at 0 12 * * * will trigger at 12:00 PM UTC every single day, regardless of whether your users are based in California, London, or Tokyo.

Replit, VPS, and Linux Crontab

If you run a Python, Node, or Go worker on Replit or a virtual private server (VPS), the default behavior of the local cron daemon depends on the operating system environment. Many default Linux installations set the system timezone to UTC, but developer sandbox environments or local virtual machines might default to the timezone where the server cluster is physically located or where the developer registered. If your system timezone changes or adheres to daylight saving time, jobs scheduled between 2:00 AM and 3:00 AM will encounter spring-forward skips or autumn duplication.

The Decision Framework: Choosing UTC vs. Local Time

Solving the issue where a scheduled job runs at wrong time after dst requires choosing the right scheduling model deliberately. You should rarely leave schedule timing to server defaults. Instead, categorize your automated tasks into two distinct buckets: cadence-critical and human-facing .

Job TypePrimary GoalRecommended Timezone StandardHandling DST Transitions
System Maintenance & SyncsMaintain exact intervals between runs (e.g., every 6 hours, once every 24 hours).Strict UTCUnaffected. UTC never skips or duplicates hours; tasks execute at steady intervals.
Database Backups & CleanupsRun during the lowest possible traffic window without overlapping heavy read loads.Strict UTCKeep in UTC. A one-hour shift in server maintenance during summer rarely affects performance.
Customer Daily SummariesArrive in an inbox when the user begins their workday (e.g., exactly 8:00 AM local).User's Local IANA TimezoneCalculate recipient offsets dynamically or schedule via timezone-aware queue workers.
End-of-Day Billing BatchesClose financial books at the end of the legal calendar day for a specific jurisdiction.Jurisdiction Local TimeAvoid scheduling during the 1:00 AM–3:00 AM switchover window to prevent double charges.

Rule 1: Use UTC for Cadence-Critical Jobs

If the purpose of the job is internal hygiene—such as clearing expired sessions, syncing Stripe webhook logs, refreshing cache tokens, or backing up tables—schedule the job strictly in UTC. rarely adjust these jobs for daylight saving time. A backup job does not care whether the sun is shining outside; it only cares that 24 hours have elapsed since the prior backup run. Running maintenance in UTC guarantees that your application rarely skips a backup during the spring shift or runs a redundant, expensive data cleanup twice during the autumn fallback.

Rule 2: Use Explicit IANA Timezones for Human-Facing Jobs

If the job produces an external artifact that a person consumes—such as a morning summary email, a scheduled SMS notification, or an alert before an appointment—anchoring the schedule to UTC will inevitably generate complaints twice a year. For these tasks, you must store the user's specific IANA timezone string (such as Europe/Paris or America/Los_Angeles) rather than a fixed UTC offset like -05:00. Fixed numerical offsets do not adapt when daylight saving begins or ends, but IANA identifiers automatically adjust when the transition date arrives.

Step-by-Step Implementation: How to Fix Shifting Jobs

Once you know which model your task requires, apply these platform-specific fixes to ensure your background tasks fire when intended.

1. Handling Timezones in Vercel and Serverless Functions

Because Vercel cron expressions are strictly UTC, you cannot tell the scheduler itself to observe daylight saving time. If you have an email digest that must reach East Coast users at 8:00 AM year-round, you have two engineering options:

  1. The Hourly Dispatch Pattern: Schedule your Vercel cron job to run every hour on the hour ( 0 * * * * ). In your API route handler, query your database for users whose local time matches 8:00 AM using an IANA-aware library like date-fns-tz or luxon . Send messages only to users whose local clock hit the target hour during that specific run. This completely insulates your app from DST drift across all global timezones.
  2. Dynamic Offset Evaluation: If you only have users in one primary timezone, configure the handler to check whether Daylight Saving Time is active on the current date. If the job runs at 12:00 UTC, the code checks if the target market is in daylight saving mode; if not, it can defer execution or exit early.

2. Fixing Schedules in Bubble

To keep an automated communication workflow firing at the correct human time in Bubble without relying on client browser triggers:

  • Store the target execution hour as an integer on the User or Team data type (e.g., Target_Hour = 8) alongside their text-based IANA timezone (e.g., Timezone_ID = America/Chicago).
  • Create a backend workflow that runs on a recurring hourly schedule in UTC.
  • In that backend workflow, run a search for users where the current formatted date/time converted to their stored timezone equals their target hour.
  • Trigger the nested notification workflow exclusively for the matching records. This guarantees that when clocks change in Chicago, the workflow naturally aligns with their new local offset without requiring manual intervention in your Bubble editor.

3. Configuring Supabase and pg_cron for Timezones

Modern versions of pg_cron allow you to specify the timezone directly in the cron schedule command. If your database requires a job to run at 8:00 AM Eastern time regardless of whether it is EST or EDT, specify the timezone parameter explicitly:

SELECT cron.schedule(
  'daily-digest',
  '0 8 * * *',
  'SELECT generate_daily_digest();',
  'America/New_York'
);

When an explicit IANA timezone name is passed to pg_cron , the scheduler automatically checks the system's timezone data and triggers the job at 8:00 AM wall-clock time throughout both summer and winter. However, remember the golden rule: rarely schedule a critical local job between 2:00 AM and 3:00 AM local time, as the spring transition will cause the scheduler to skip that trigger window.

Detecting Schedule Failures and Drift with Heartbeat Monitoring

The most dangerous aspect of daylight saving drift is that it fails quietly. Unlike an unhandled code exception or a database connection crash, a scheduled job that shifts by an hour does not generate an error log. The worker returns an HTTP status code 200 OK, reports a clean exit, and marks the task complete. You often only discover the problem when a paying subscriber contacts support to ask why their morning report arrived an hour late.

To prevent this, production applications rely on heartbeat monitoring (sometimes referred to as a dead man's switch). Instead of waiting for an error, heartbeat monitoring expects a periodic ping from your scheduled task within an explicit time window.

How Heartbeat Monitoring Works

When you set up a heartbeat check for a daily cron job:

  1. You configure a monitoring endpoint that expects an incoming HTTP request within an allowable window (for example, every day at 8:00 AM with a 15-minute grace period).
  2. At the conclusion of your scheduled worker, your code issues a simple GET or POST request to that unique heartbeat URL.
  3. If the background task shifts by an hour due to an unhandled daylight saving transition, the ping arrives outside the expected grace period.
  4. The monitoring system immediately raises an alert indicating that the job failed to execute on schedule.

Heartbeat checks catch both sides of the problem: they alert you if your job failed to trigger at all (such as a skipped run during spring-forward) or if it ran outside its contractual execution window. Learn how this fits into a comprehensive app protection plan in our guide to how managed monitoring works.

How Nightlamp Keeps Your Scheduled Jobs on Track

When an automated job silently drifts or crashes after a calendar change, you shouldn't have to spend hours parsing server logs or timezone conversion tables to figure out what happened. Nightlamp provides managed monitoring and App Care designed specifically for non-technical founders and modern software teams.

Nightlamp monitors HTTP endpoints, SSL certificate validity, DNS records, and scheduled cron heartbeats. When a daily workflow drifts out of its expected window or drops offline, human engineers diagnose incidents for you; Nightlamp does not just fire alerts. Nightlamp does not auto-remediate infrastructure on its own; a real engineer diagnoses each incident and tells you exactly what to fix. Whether the root cause is an unexpected daylight saving offset shift in Vercel, a stuck queue in Bubble, or an unhandled timezone argument in Postgres, you receive a plain-English diagnosis outlining precisely what broke and the exact steps required to resolve it.

Frequently Asked Questions

Why did my daily cron job run twice on the same day?

Your job ran twice because your scheduler is configured using local wall-clock time rather than UTC, and the system experienced the autumn daylight saving transition. When the clock falls back from 2:00 AM to 1:00 AM, the hour between 1:00 AM and 2:00 AM happens twice. Any cron trigger configured inside that window will fire on the first pass and fire again sixty minutes later on the second pass. To prevent this, schedule local tasks outside the 1:00 AM–3:00 AM window or run strictly in UTC.

Why was my job skipped completely when clocks changed in spring?

When daylight saving time begins in the spring, clocks skip forward from 1:59:59 AM to 3:00:00 AM. The hour between 2:00 AM and 2:59 AM does not exist on that day. If your scheduler evaluates triggers against local civil time and your task was set to run at 2:30 AM, the scheduler rarely saw the clock strike 2:30, and the job was bypassed until the next calendar day.

Can I fix timezone drift by just setting my server to my local timezone?

Setting your server to a local timezone is strongly discouraged in production environments. While it might temporarily align a single cron job, it introduces severe architectural liabilities: server log timestamps will become discontinuous during autumn fallback, database queries comparing historical events can return incorrect results, and third-party API integrations (such as payment gateways and webhook handlers) will experience timestamp calculation errors. Standard industry practice is to keep all server clocks, databases, and logs in UTC, resolving local human time within your application code.

How do I test whether my job will survive a DST shift before it happens?

The safest way to test daylight saving resilience is to pass simulated timestamps to your job's execution handler in a staging or local development environment. Test your scheduling logic against three specific boundary dates: the day before the spring forward change, the exact transition hour, and the day after. Verify that tasks scheduled for 1:30 AM, 2:30 AM, and 3:30 AM execute exactly once per 24-hour cycle without throwing timezone parsing exceptions.

Protect Your App's Critical Background Tasks

Scheduled automations are the quiet backbone of your business—until a missed cron or shifted workflow disrupts billing, communications, and customer trust. To stop finding out about broken jobs and scheduling bugs from frustrated users, let experienced engineers keep watch over your production workflows. To protect your application with complete heartbeat, uptime, and workflow monitoring, start a trial with Nightlamp today.