Depurando Problemas de Zona Horaria: Bugs Comunes y Soluciones

The Most Common Timezone Bugs

After years of developer pain, certain timezone bugs appear again and again. Here are the most common ones with their fixes.

Bug 1: The Naive Datetime Trap (Python)

# Bug: comparing naive and aware datetimes
from datetime import datetime, timezone
deadline = datetime(2024, 3, 1, 0, 0)          # naive — no timezone!
now = datetime.now(timezone.utc)                 # aware UTC
print(now > deadline)    # TypeError: can't compare!

# Worse: storing naive datetimes in DB then comparing
created = datetime.now()    # naive — assumed local, but which server?

# Fix: always use timezone-aware datetimes
deadline = datetime(2024, 3, 1, 0, 0, tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
print(now > deadline)    # Works correctly

Bug 2: DST Transition — The Missing or Doubled Hour

# Bug: scheduling something at "2:30 AM" when clocks spring forward
# On the second Sunday in March (US), 2:00 AM → 3:00 AM
# There is NO 2:30 AM — it doesn't exist!

import zoneinfo
from datetime import datetime
tz = zoneinfo.ZoneInfo("America/New_York")
# This datetime does not exist — zoneinfo handles it, but your intent is wrong
dt = datetime(2024, 3, 10, 2, 30, tzinfo=tz)

# Fix: always compute in UTC, convert for display
# Schedule at 7:30 AM UTC instead of "2:30 AM local"

Bug 3: JavaScript Date Parsing Inconsistency

// Bug: date-only strings parsed as UTC, but displayed as local time
const d = new Date("2024-03-01");
// Parsed as: 2024-03-01T00:00:00Z (midnight UTC)
// Displayed as: March 1 in UTC+9 = but 2024-02-29T15:00:00 in UTC-9!
console.log(d.toLocaleDateString());  // "2/29/2024" in US Western timezone!

// Fix: always include time and timezone in date strings
const d = new Date("2024-03-01T00:00:00Z");   // UTC
const d = new Date("2024-03-01T00:00:00+09:00"); // KST

Bug 4: Database Storing Local Time

-- Bug: MySQL TIMESTAMP stored in local time
-- Server timezone changes → all historical data is wrong!

-- Fix: use DATETIME with UTC-only writes
-- In Django:
USE_TZ = True  # ensures all datetimes go in as UTC

Bug 5: Hardcoded UTC Offset Instead of IANA Timezone

# Bug: hardcoding UTC+9 for Korea Standard Time
KST = timezone(timedelta(hours=9))
dt = datetime.now(KST)   # Will be wrong during DST (if KST ever had DST)

# Fix: use IANA timezone names
from zoneinfo import ZoneInfo
dt = datetime.now(ZoneInfo("Asia/Seoul"))  # Correct, handles any future changes

Debugging Techniques

  • Print UTC everywhere: When debugging, always log datetime.now(timezone.utc).isoformat()
  • Test in multiple timezones: Set TZ=America/New_York or TZ=Asia/Tokyo env var to test your code in a different timezone without changing your machine
  • Test around DST transitions: Use libfaketime (Linux) or freezegun (Python) to simulate specific times
  • Check database timezone: SHOW timezone; (PostgreSQL) and SELECT @@global.time_zone; (MySQL)
# Python: test code in different timezone without changing system
TZ=America/New_York python my_script.py

# pytest with freezegun
from freezegun import freeze_time

@freeze_time("2024-03-10 06:59:00", tz_offset=-5)
def test_dst_spring_forward():
    # Test behavior at DST transition
    result = schedule_task("America/New_York", hour=2, minute=30)
    assert result.hour != 2  # 2:30 AM doesn't exist, should handle gracefully

The Golden Checklist

  • All datetimes are timezone-aware (no naive datetimes in production)
  • Database stores UTC (not local time)
  • Server timezone is set to UTC
  • API responses include timezone offset in all timestamp fields
  • Cron jobs either use UTC or explicitly set CRON_TZ
  • IANA timezone names used everywhere (never raw UTC offsets like +09:00)
  • DST transitions tested in automated tests