Don’t reinvent time. You are probably doing it wrong
TL;DR: Time is not absolute. Your code breaks when you treat it that way.
Problems 😔
Solutions 😃
- Use solid libraries
- Avoid system clock trust
- Normalize all timestamps
- Test with edge cases
- Embrace time weirdness
- Always include time zones
- Check All Timezones
- Fail-Fast
- Treat timestamps as Timestamps
Context 💬
You think a day has 24 hours, weeks begin on Monday, or February always has 28 days.
Your users in Ouagadougou get a double midnight, and your backups skip a day in Sydney.
Time illusions creep into your code when you assume it’s simple.
You build logic that fails during daylight-saving changes, leap seconds, or even when the clock drifts.
Programmers often struggle with time management.
When you work with time in your applications, you face one of programming’s most deceptive challenges.
Most developers start by writing simple time calculations, assuming that days always have 24 hours, months have consistent lengths, and time zones remain static.
These assumptions create defects that surface months or years later when your application encounters real-world time scenarios.
Time handling represents a perfect example of the Dunning-Kruger effect in programming. The more you learn about time, the more you realize how little you know.
Political decisions change time zones, leap seconds adjust atomic time, and cultural differences affect calendar systems worldwide.
Sample Code 📖
Wrong ❌
from datetime import datetime, timedelta
class TimeCalculator:
def add_business_days(self, start_date, days):
# Assumes every day has 24 hours
result = start_date
for _ in range(days):
result += timedelta(days=1)
# Skip weekends
while result.weekday() >= 5:
result += timedelta(days=1)
return result
def get_monthly_report_date(self, year, month):
# Assumes all months have 31 days
return datetime(year, month, 31)
def calculate_age(self, birth_date):
# Ignores leap years and timezone changes
today = datetime.now()
return (today - birth_date).days // 365
def schedule_meeting(self, base_time, timezone_offset):
# Assumes timezone offset never changes
return base_time + timedelta(hours=timezone_offset)
def is_same_day(self, time1, time2):
# Compares without considering timezone
return time1.date() == time2.date()
Right 👉
import pytz
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from zoneinfo import ZoneInfo
class TimeHandler:
def __init__(self, timezone='UTC'):
self.timezone = ZoneInfo(timezone)
def add_business_days(self, start_date, days):
"""Add business days accounting for timezone and DST"""
if not start_date.tzinfo:
start_date = start_date.replace(tzinfo=self.timezone)
result = start_date
days_added = 0
while days_added < days:
result += timedelta(days=1)
# Skip weekends
if result.weekday() < 5:
days_added += 1
return result
def get_monthly_report_date(self, year, month):
"""Get last day of month safely"""
next_month = datetime(year, month, 1,
tzinfo=self.timezone) + relativedelta(months=1)
return next_month - timedelta(days=1)
def calculate_age(self, birth_date):
"""Calculate age accounting for leap years"""
if not birth_date.tzinfo:
birth_date = birth_date.replace(tzinfo=self.timezone)
today = datetime.now(self.timezone)
return relativedelta(today, birth_date).years
def schedule_meeting(self, base_time, target_timezone):
"""Schedule meeting with proper timezone handling"""
if not base_time.tzinfo:
base_time = base_time.replace(tzinfo=self.timezone)
target_tz = ZoneInfo(target_timezone)
return base_time.astimezone(target_tz)
def is_same_day(self, time1, time2, timezone):
"""Compare dates in specific timezone"""
tz = ZoneInfo(timezone)
local_time1 = time1.astimezone(tz)
local_time2 = time2.astimezone(tz)
return local_time1.date() == local_time2.date()
Detection 🔍
You can detect this smell when you see hardcoded time calculations, assumptions about day lengths, timezone-naive datetime operations, or custom date arithmetic.
Look for magic numbers like 86400 (seconds in a day), 365 (days in a year), or hardcoded timezone offsets.
Watch for datetime operations that don’t specify time zones, leap year calculations using simple division, or any code that treats time as purely mathematical without considering political and physical realities.
Level 🔋
Why the Bijection Is Important 🗺️
Time in the real world is fuzzy, political, and full of exceptions.
If your program models it as linear and perfect, you introduce a mismatch to the MAPPER.
That mismatch leads to defects that are impossible to reproduce and hard to explain.
You need to represent time in a way that reflects its behavior: with context, rules, and variability.
When your code assumes simplified time behavior, you break the correspondence between your program’s time model and reality.
This creates defects that appear randomly when your application encounters real-world time scenarios such as daylight saving time transitions, leap years, or timezone changes.
Maintaining the bijection means respecting the true complexity of time and using established libraries that handle these edge cases correctly.
Breaking this correspondence leads to scheduling errors, incorrect age calculations, and data corruption in time-sensitive applications.
You cannot create a date with a day of February 30th.
You need to follow the fail-fast principle.
AI Generation 🤖
AI often assumes new Date() works fine. Many generated examples ignore time zones, DST changes, and even correct parsing. AI helps you repeat illusions faster.
AI code generators sometimes create time-handling code with common falsehoods.
They often generate simple date arithmetic, hardcoded timezone assumptions, and naive datetime operations because these patterns sometimes happen in training data.
AI Detection 🧲
If you ask AI to “handle timezones correctly” or “avoid daylight saving defects,” it can generate better code. But it needs clear instructions. The default output is usually wrong.
AI tools can detect time handling falsehoods when you provide specific instructions about timezone awareness, leap year handling, and DST considerations.
You must explicitly ask for these checks, as AI won’t automatically identify time-related assumptions.
Try Them! 🛠
Remember: AI Assistants make lots of mistakes
Suggested Prompt: “Review this time handling code for common falsehoods about time. Check for timezone-naive operations, hardcoded day/month lengths, leap year assumptions, and DST handling. Suggest improvements using established time libraries and proper timezone handling.”
Conclusion 🏁
When you treat time as simple, your code lies. Time is a deeply broken concept riddled with politics, exceptions, and drift.
Respect it and never write your own date logic.
Use libraries that have spent decades fixing what you can’t even see.
Your applications will become more reliable when you respect time’s true nature and use proper time handling practices from the beginning of your development process.
Relations 👩❤️💋👨
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-viii-8mn3352
https://hackernoon.com/code-smell-246-modeling-expiration-dates
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxix
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xli
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xvi
More Information 📕
https://en.wikipedia.org/wiki/Dunning–Kruger_effect
Disclaimer 📘
Code Smells are my opinion.
Credits 🙏
Photo by Luis Cortes on Unsplash
A day can be 23 hours. Or 25. You just forgot.
Paul Ford
This article is part of the CodeSmell Series.