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 heliclockterfrom 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
tzinfowhen 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 errordatetime_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.
The table below shows which Pydantic and Python versions are supported for which heliclockter version.
Note that the latest version of heliclockter dropped support for Pydantic v1 code completely, meaning that you
also can't use heliclockter in combination with the pydantic.v1 module in Pydantic v2.
| heliclockter version | Pydantic support | Python support |
|---|---|---|
| 1.0 | v1 | 3.9, 3.10, 3.11, 3.12, 3.13 |
| 1.1, 1.2, 1.3 | v1, v2 | 3.9, 3.10, 3.11, 3.12, 3.13 |
| 2.0 | v1, v2 | 3.10, 3.11, 3.12, 3.13 |
| 3.0 | v2 | 3.10, 3.11, 3.12, 3.13, 3.14 |
BSD 3-Clause License. See LICENSE.