← Back to list

SQL Boo-Boos #5: Why My Dates Shifted by One Day (The Timezone Trap Nobody Warns You About)

Have you ever spent hours double-checking your math only to realize the numbers weren’t wrong, but the clock was?

Preethi Kaluva in Towards Data Engineering · 2026-06-02 11:37 · 0 claps · 5.0 min read
#sql #data-engineering #database #sql-optimization #performance-optimization
Open on Medium ↗
Wiki topics: 🔧 · Data Engineering 📐 · Mathematics

SQL Boo-Boos #5: Why My Dates Shifted by One Day (The Timezone Trap Nobody Warns You About)

Have you ever spent hours double-checking your math only to realize the numbers weren’t wrong, but the clock was?

It’s Wednesday afternoon, and I’m staring at a dashboard that makes no sense. Yesterday’s sales numbers are $40,000 short. I dive into the database, re-run my queries, and verify the logic. Everything looks perfect. On my screen, the data is flawless.

Then I look at the raw server logs.

Because of a timezone mismatch, every transaction after 7:00 PM was being kicked into the next day. The code wasn’t broken, and the SQL was perfect, but my dates were silently shifting behind my back. It wasn’t a calculation error; it was just a few invisible hours stealing my credit.

Why Does This Even Come Up?

This boo-boo doesn’t happen in a vacuum. It happens when a few very specific things align.

Most production databases especially cloud-hosted ones, store timestamps in UTC. This is the right call. UTC is a universal baseline, it doesn’t change with seasons, and it plays nicely across global systems. Your orders table has a column like created_at TIMESTAMP and every row in it is logged in Coordinated Universal Time.

So far so good.

But your users? They might not be in UTC. They’re in New York (-5 or -4 depending on daylight saving), or London (+0 or +1), or Sydney (+10 or +11). And your business? It operates on local business days. When the finance team says “Monday’s revenue,” they mean Monday in New York not Monday in UTC. Those are not the same Monday.

This is where people reach for timezone conversion. And this is exactly where the one-day shift sneaks in.

The Specific Scenario That Burns You

Let’s say you’re running an e-commerce platform. Your US customers are in the Eastern timezone (America/New_York). UTC is 5 hours ahead of EST in winter, 4 hours ahead during daylight saving time.

An order comes in at 11:30 PM Eastern on Monday, January 13th.

What gets stored in your database?

2025–01–14 04:30:00 UTC

That’s Tuesday in UTC. But it’s Monday in New York. Now you run a daily revenue report:

SELECT
    created_at::DATE AS order_date,
    SUM(order_total) AS revenue
FROM orders
GROUP BY 1
ORDER BY order_date;

Without any timezone conversion, DATE(created_at) extracts the UTC date which is January 14th. That order just got counted under Tuesday. In reality, your customer placed that order on Monday evening. Your operations team fulfilled it on Monday. But your report says Tuesday.

Every single night, from roughly 7:00 PM to midnight Eastern time, your orders are being silently assigned to the wrong calendar day. You’re not losing data. You’re not getting errors. Everything looks technically correct. It’s just… wrong.

What Happens When You Try to Fix It (And Do It Wrong)

Here’s where the boo-boo gets compounded. You realize the problem, you google “SQL convert UTC to local timezone,” and you find a quick fix that looks like this in SQL:

SELECT
    CONVERT_TIMEZONE('EST', created_at)::DATE AS order_date,
    SUM(order_total) AS revenue
FROM orders
GROUP BY 1
ORDER BY order_date;

You run it. Dates look better! You ship it.

Then March arrives. Daylight Saving Time kicks in. America/New_York is now UTC-4, not UTC-5. But you hardcoded ‘EST’ which is always UTC-5, even in summer. Now you’re off by an hour, which means orders between 8:00 PM and midnight Eastern are still landing on the wrong day in your report.

You’ve traded one bug for a subtler, seasonal bug.

Let’s Walk Through This Step by Step

Here’s what’s happening under the hood. Follow this flow:

The fix is to convert before you extract the date and to use a proper timezone name, not a fixed offset abbreviation.

The Right Way to Do It

In SQL, the correct approach looks like this:

SELECT
    DATE(CONVERT_TIMEZONE('America/New_York', created_at)) AS order_date,
    SUM(order_total) AS revenue
FROM orders
GROUP BY 1
ORDER BY 1;

Using ‘America/New_York’ instead of ‘EST’ is critical. The timezone name automatically handles daylight saving transitions. It knows that in January you’re at UTC-5, and in July you’re at UTC-4. The hardcoded ‘EST’ doesn’t know that, and it doesn’t care.

In BigQuery (where a lot of reporting queries live):

SELECT
 DATE(created_at, 'America/New_York') AS order_date,
 SUM(order_total) AS revenue
 FROM orders
 GROUP BY order_date;

BigQuery’s DATE() function accepts a second argument for timezone, which is clean and explicit.

The Daylight Saving Time Special

Here’s a bonus edge case that comes up once a year and causes exactly one hour of chaos.

When clocks fall back in November, a one-hour window gets repeated. 1:00 AM to 2:00 AM Eastern happens twice on that day -once in EDT (UTC-4) and once in EST (UTC-5).

If you’re aggregating by hour during that window, you can end up double-counting. Two different UTC timestamps map to the same local hour. This won’t shift your day, but it will inflate one hour’s numbers by roughly 2x.

The safer approach for any time-sensitive reporting: always aggregate in UTC, then present in local time at the display layer. Push the timezone conversion as late in the pipeline as possible. Your SQL should work in UTC; your BI tool or application layer should handle the visual conversion for end users.

A Quick Checklist Before You Ship That Report

If you’re building any date-based reporting query, run through this before you call it done:

  1. What timezone are your timestamps stored in? (Ask your DBA or check the database configuration. Don’t assume.)
  2. What timezone does your business operate in? (Could be multiple if you serve multiple regions.)
  3. Are you using a proper timezone name -like ‘America/Chicago’ or a fixed offset abbreviation like ‘CST’ that won’t account for DST?
  4. Are you converting before extracting the date? DATE(CONVERT(ts)) is correct. CONVERT(DATE(ts)) is not -you’ve already lost the time component before converting.
  5. Have you tested your query against data from a DST transition weekend? If not, do it. Seriously.

Why This Boo-Boo Is Sneaky

What makes this one particularly painful is that the query works. The totals even add up correctly, just assigned to the wrong buckets. It survives code review. It survives QA. It survives the first few weeks in production. And then a manager notices that the Monday numbers seem a little low, and Friday numbers seem a little high, and then reveals it’s been wrong since launch. And such cases required going back and reprocessing weeks of historical data.

The fix is always small: one function call, one timezone name.

To conclude and reiterate:

If your database stores timestamps in UTC and your reports operate on local business days, you must convert before extracting the date.

Use timezone names like ‘America/New_York’, never fixed offset abbreviations like ‘EST’ or ‘PST’. Fixed offsets break twice a year when DST changes.

In Snowflake: DATE(CONVERT_TIMEZONE(‘America/New_York’, ts)) In BigQuery: DATE(ts, ‘America/New_York’) In MySQL: CONVERT_TZ(ts, ‘UTC’, ‘America/New_York’)

Test your queries against late-night timestamps specifically anything between 8:00 PM and midnight in your business timezone. That’s where the one-day shift will show up…

The timezone boo-boo is one of those bugs that makes you feel slightly betrayed. You wrote valid SQL. The database did exactly what you asked. But what you asked for wasn’t quite what you meant.

Once you’ve seen it once, you’ll never forget to check it again. That’s the thing about boo-boos: they’re embarrassing, but they’re educational.

See you in the next one!


메타데이터
post_id
9aaf4c02870e
slug
sql-boo-boos-5-why-my-dates-shifted-by-one-day-the-timezone-trap-nobody-warns-you-about-9aaf4c02870e
url
https://medium.com/towards-data-engineering/sql-boo-boos-5-why-my-dates-shifted-by-one-day-the-timezone-trap-nobody-warns-you-about-9aaf4c02870e
canonical_url
https://medium.com/towards-data-engineering/sql-boo-boos-5-why-my-dates-shifted-by-one-day-the-timezone-trap-nobody-warns-you-about-9aaf4c02870e
author_url
https://medium.com/@kaluvapreethi
status
ok
fetched_at
2026-06-25 16:53:31