Skip to content

The Most Common Date and Time Bugs in Software

A practical guide to the mistakes that keep breaking software: naive datetimes, UTC confusion, DST gaps, date-only bugs, Unix timestamp units, week numbers, and more.

· 13 min read · Gabriel

Developer screen showing date and time bugs in software
Table of Contents

The bug usually starts with a sentence that sounds harmless

Most date and time bugs do not start with exotic astronomy. They start with a product sentence that sounds obvious: “send the reminder tomorrow morning”, “expire this trial after 30 days”, “show today’s orders”, “run this job at 2:30 AM”, “store the timestamp”. Each sentence hides a rule. Tomorrow morning where? Thirty calendar days or 30 times 24 hours? Today in the customer’s city, the warehouse, or the database server? What happens when 2:30 AM does not exist?

Software is good at counting. Calendars are not just counting. They are a stack of human conventions: time zones, daylight saving changes, business days, local date formats, leap years, ISO weeks, database types, and legal cut-off times. Bugs appear when code treats one layer as if it were another.

This guide is a field manual for the mistakes that repeatedly break production systems. It is written for developers, product managers, analysts, and anyone who has ever looked at a dashboard and wondered why yesterday’s numbers changed after breakfast.

1. Naive datetimes: the timestamp with no passport

A naive datetime is a date and clock time without a time zone. 2026-03-08 02:15 looks precise, but it does not say where it happened. In New York on the 2026 spring daylight saving transition, the hour from 02:00 to 02:59 does not occur. In another city it may be perfectly valid. In UTC it means something else again.

The common failure is storing local wall time as if it were an instant. A meeting created for “9 AM Europe/Madrid” should remain 9 AM Madrid time when Spain changes clocks. A payment event that already happened should be stored as an instant. Those are different models.

Use caseStoreWhy
Audit log, click, payment, API eventInstant, usually UTCThe event already happened at one moment.
Future meeting in a cityLocal date, local time, IANA zoneThe local clock time must survive DST rule changes.
Birthday, holiday, billing dayDate onlyThere may be no useful time-of-day.
Opening hoursLocal time plus place/ruleThe rule repeats in local civil time.

If you need to compare exact moments, convert to instants. If you need to keep a human appointment where it was intended, keep the place too. An offset such as UTC+01:00 is not enough for future scheduling because the offset can change.

2. UTC confusion: useful storage, bad user experience

“Store everything in UTC” is a useful rule for events that already happened. It is not a complete product design. Users do not think in UTC when they schedule a dentist appointment, publish a webinar, or choose a hotel check-in date. They think in local civil time.

The bug appears when a system stores UTC correctly but forgets the user’s intended zone. A calendar invite created in Los Angeles for 9 AM may display as 5 PM UTC internally. That is fine. But if the app later rebuilds the event using the server’s time zone, or only stores a fixed offset, the meeting can move after a DST change.

Good systems separate three questions: what instant is this, what local time did the user intend, and which time zone rules explain that local time? Our time zone converter is useful precisely because the date, place, and offset all matter together.

3. DST gaps and folds: times that disappear or happen twice

Daylight saving time creates two classic traps. In spring, a local hour can be skipped. In autumn, a local hour can happen twice. If your scheduler accepts “2026-03-08 02:30 America/New_York”, what should it do? Reject it? Move it to 03:00? Interpret it in another zone? There is no universally correct answer. The product needs a rule.

Ambiguous autumn times are even more subtle. “2026-11-01 01:30 America/New_York” can refer to the first 01:30 or the second 01:30. Both are real local labels. Logs, transport systems, hospital systems, and job schedulers need to know which one is meant.

A useful test suite includes real transition dates for zones your users care about. Do not only test January 15. Test a spring-forward day, a fall-back day, and a country whose DST rules differ from your own.

4. Date-only values that accidentally become midnight

A date is not always a datetime with midnight attached. A hotel booking for June 3, a birthday, a tax deadline date, and a public holiday are date concepts. When code turns them into 2026-06-03T00:00:00Z, it has invented a time and a zone.

This is how birthdays shift to the previous day for users west of UTC. It is also how “today’s report” can show yesterday’s data for one team and tomorrow’s data for another. The fix is not complicated: use date-only types for date-only concepts, and only create a datetime when the business rule actually needs a time.

For deadlines, ask a sharper question: does the item expire at the start of the date, the end of the date, a local business close, or a precise instant? “Expires on June 3” is product copy, not a complete rule.

5. Adding days as seconds

Adding 86,400 seconds is not the same as adding one calendar day in local time. Most days are 24 hours, but DST transition days can be 23 or 25 hours. If a daily reminder should appear at 8 AM local time, add a calendar day in the user’s zone. If a cache should expire after exactly 24 hours, add a duration.

PhraseUsually meansSafer model
Same time tomorrowNext local calendar day at the same wall timeDate/time in user zone
Expires in 24 hoursExact elapsed durationInstant plus duration
Due in 3 business daysLocal working-day ruleBusiness calendar
Every Monday at 9Recurring local scheduleLocal time plus IANA zone

This distinction is why a business days calculator is not just a date-difference calculator with weekends removed. Holidays, local weekends, and observed closures can change the result.

6. Unix timestamp units: seconds, milliseconds, microseconds, nanoseconds

Unix time is elegant: count time since 1970-01-01T00:00:00Z. The problem is that software ecosystems disagree on the unit. Many APIs use seconds. JavaScript timestamps use milliseconds. Some databases and observability systems use microseconds or nanoseconds. A number like 1717425600 and 1717425600000 represent the same moment in different units; use the wrong one and your date jumps by centuries.

The fastest sanity check is digit count. Current Unix seconds are 10 digits. Milliseconds are 13 digits. Microseconds are 16. Nanoseconds are 19. A good importer should identify the unit explicitly or reject ambiguous input instead of guessing silently. Our Unix timestamp converter shows the same idea in tool form.

7. Sorting dates as strings in the wrong format

ISO-style dates sort well as text: 2026-06-03 comes before 2026-12-01. Many local formats do not. A list of MM/DD/YYYY or DD/MM/YYYY strings can look correct in a small sample and fail the moment months or locales change.

Use ISO 8601 or real date types for interchange and storage. Use localized formats only at the display boundary. If a CSV is meant for humans, spell out the expected format in the header or export documentation. If it is meant for machines, prefer an unambiguous standard such as RFC 3339 for timestamps.

8. Week-number bugs around New Year

Week numbers are not universal. ISO weeks start Monday and make Week 1 the week with the first Thursday. Some US-style systems start Sunday and count the week containing January 1 as Week 1. Around New Year, those rules can differ by a full week.

If a report says “Week 1 revenue”, a payroll export says “2026-W01”, and a spreadsheet uses a default week-number function, confirm the rule before reconciling numbers. For international teams, the safest label is the week system plus the date range, for example: “ISO 2026-W01, Dec 29, 2025-Jan 4, 2026.”

The same logic is built into our week number tool, which displays the date range instead of treating the number as self-explanatory.

9. Server time zone leaks

One of the most frustrating production bugs is code that passes every test locally and fails after deployment because the server runs in a different time zone. The developer’s laptop, CI runner, Docker image, database, queue worker, and analytics warehouse may all have different defaults.

Set explicit time zones in tests. Run at least one test job with a non-local time zone. Avoid relying on process defaults for parsing or formatting. If a function receives a date string, make the expected format and zone part of the contract.

10. “End of day” without a place

End of day sounds clear until customers live in more than one place. A promotion that ends “Friday at midnight” could mean the customer’s local midnight, the company headquarters’ midnight, UTC midnight, or the payment processor’s cut-off. Each creates a different user experience.

Good product copy names the place or uses a countdown tied to the actual deadline. Good backend code stores the deadline as a precise instant after the business rule has been applied. Good support documentation says which rule was used.

A practical checklist before shipping date/time code

  • Is this value an instant, a local date/time, a date-only value, a duration, or a recurrence rule?
  • If it is local, which IANA time zone owns the rule?
  • If it is a date-only value, have we avoided inventing midnight in UTC?
  • Do DST gaps and folds have explicit behavior?
  • Are timestamp units named, validated, and tested?
  • Do reports define the week-number system and date range?
  • Do tests run outside the developer’s own time zone?
  • Does user-facing copy say enough for a person to act correctly?

The deeper rule

Date and time bugs are rarely caused by a lack of clever code. They are caused by a missing noun. “Tomorrow” needs a place. “Thirty days” needs to say calendar or duration. “Week 1” needs a system. “Timestamp” needs a unit. “End of day” needs a jurisdiction or time zone.

When the noun is explicit, the code usually becomes simpler. The database column gets the right type. The API contract becomes less surprising. The UI can explain the result. And the next person reading the code does not need to reverse-engineer what “now plus one day” was supposed to mean.

Further reading

← Back to all articles