Practical
Most timezone bugs trace back to a handful of repeated mistakes. Fix the habits, and most of the bugs simply stop happening.
Timezone bugs have a reputation for being subtle and hard to test for, and that reputation is earned — they often only show up for part of the year, or only for users in specific regions, or only right at a DST boundary. The good news is that a small number of habits prevent the overwhelming majority of them.
This is the single highest-value habit on this list. UTC has no daylight saving time and no ambiguous or missing hours, which means it's always safe to store, compare, and calculate durations against. Convert to a local timezone only at the final step, when displaying something to an actual person — never store a local timestamp as your source of truth.
If you need to remember "what timezone was this event scheduled in," store an actual IANA identifier like America/Chicago, not a fixed offset like UTC-6. Offsets change with daylight saving time and with political boundary changes; the identifier itself resolves to the correct current offset for any given date, automatically, as long as the underlying timezone database is kept up to date.
A field called createdAt tells you nothing about whether it's seconds or milliseconds. A field called createdAtMs makes the unit mismatch visible in code review, before it ever reaches production. This one habit prevents the single most common timestamp bug there is.
If your test suite only ever runs against "today's date," it will never exercise the one week a year where DST transitions actually break things. Deliberately test against dates that fall inside a spring-forward and fall-back transition for your relevant timezones — the DST Change Tracker can tell you exactly which dates those are for any zone and year.
If a date needs to be logged, serialized, or passed between systems as text, ISO 8601 removes the ambiguity that formats like 03/04/2026 carry, and it sorts correctly as plain text with no parsing required. See the full ISO 8601 guide for the format's specifics.
Leap years, variable month lengths, and DST transitions are exactly the kind of edge cases that are easy to get subtly wrong in custom code and hard to catch in testing. Use your language's built-in date library, or a well-tested tool, rather than writing your own day-counting logic — it's one of the few areas where "just use the standard library" is close to universal advice.
Related
Correct date math, already handling leap years and month lengths.
Find the exact dates to test against for any timezone.
The text format that avoids most date ambiguity.