Timezone-aware datetimes for Python that just work.
heliclockter
is a timezone-aware datetime library that ensures your timestamps are always timezone-aware. It's statically type checkable and runtime enforceable.
pip install heliclockter
from heliclockter import datetime_utc, datetime_local, datetime_tz
# UTC datetime
utc_now = datetime_utc.now()
# datetime_utc(2022, 11, 4, 15, 28, 10, 478176, tzinfo=zoneinfo.ZoneInfo(key='UTC'))
# Local timezone datetime
local_now = datetime_local.now()
# Any timezone datetime
from zoneinfo import ZoneInfo
paris_tz = datetime_tz.now(tz=ZoneInfo("Europe/Paris"))
# Create a timestamp 2 hours in the future
future = datetime_utc.future(hours=2)
# Parse strings (naive timestamps assumed UTC)
parsed = datetime_utc.strptime('2022-11-04T15:49:29', '%Y-%m-%dT%H:%M:%S')
Python's standard datetime
allows "naive" datetimes without timezone info, leading to bugs when:
- Mixing naive and aware datetimes (causes runtime TypeErrors)
- Deploying across different timezones
- Forgetting to add
tzinfo
when creating datetimes
heliclockter
enforces timezone-aware datetimes at the type level, catching these issues before production.
- Always timezone-aware - No more naive datetime accidents
- Type safe - Full typing support for better IDE experience
- Zero dependencies - Lightweight, uses only standard library
- Pydantic support - Automatic integration when Pydantic is installed
- Python 3.10+ - Modern Python for modern applications
from heliclockter import datetime_utc, datetime_tz
from zoneinfo import ZoneInfo
# Start with UTC
utc_time = datetime_utc.now()
# To convert to different timezones, create custom classes
class datetime_tokyo(datetime_tz):
assumed_timezone_for_timezone_naive_input = ZoneInfo('Asia/Tokyo')
class datetime_ny(datetime_tz):
assumed_timezone_for_timezone_naive_input = ZoneInfo('America/New_York')
# Convert using from_datetime
tokyo_time = datetime_tokyo.from_datetime(utc_time)
ny_time = datetime_ny.from_datetime(utc_time)
from heliclockter import datetime_utc, datetime_tz
from datetime import datetime
# datetime_utc assumes UTC for naive inputs
naive_dt = datetime(2022, 11, 4, 15, 30, 0)
utc_dt = datetime_utc.from_datetime(naive_dt) # OK - assumes UTC
# datetime_tz requires explicit timezone
try:
tz_dt = datetime_tz.from_datetime(naive_dt) # Raises error
except Exception as e:
print(e) # "Cannot create aware datetime from naive if no tz is assumed"
from zoneinfo import ZoneInfo
from heliclockter import datetime_tz
class datetime_cet(datetime_tz):
"""Datetime guaranteed to be in CET timezone."""
assumed_timezone_for_timezone_naive_input = ZoneInfo('CET')
# Parse naive timestamps as CET
aware_dt = datetime_cet.strptime('2022-11-04T15:49:29', '%Y-%m-%dT%H:%M:%S')
from heliclockter import datetime_utc, datetime_local
def schedule_task(when: datetime_utc) -> None:
"""Schedule a task at a specific UTC time."""
print(f"Task scheduled for {when.isoformat()}")
# Type checker ensures only UTC datetimes are passed
utc_time = datetime_utc.now()
schedule_task(utc_time) # ✓ OK
local_time = datetime_local.now()
schedule_task(local_time) # ✗ Type error
datetime_tz
- Base class for timezone-aware datetimesdatetime_utc
- Always UTC (naive inputs assumed UTC)datetime_local
- Always local timezone (naive inputs assumed local)
now()
- Current timefrom_datetime()
- Convert from standard datetimestrptime()
- Parse string to datetimefuture()/past()
- Create relative timestampsastimezone()
- Convert to other timezone
heliclockter
is a portmanteau of "clock" and "helicopter". Like a helicopter parent, it strictly supervises your datetime handling, ensuring you never make timezone mistakes.
We welcome contributions! See CONTRIBUTING.md.
BSD 3-Clause License. See LICENSE.