Official Documentation & Resources
Primary Official Sources (REQUIRED)
- Python Official Library Documentation: https://docs.python.org/3/library/datetime.html#tzinfo-objects
- Python Official Tutorial: https://docs.python.org/3/tutorial/index.html
- Module Source Code: https://github.com/python/cpython/blob/main/Lib/datetime.py
Additional Authoritative Sources
- PEP 495: Local Time Disambiguation
- Real Python - Working with Dates and Times: https://realpython.com/python-datetime/
- GeeksforGeeks - Python DateTime: https://www.geeksforgeeks.org/python-datetime-module/
- Python Module of the Week (PyMOTW) - datetime: https://pymotw.com/3/datetime/
- Stack Overflow datetime questions: https://stackoverflow.com/questions/tagged/python+datetime
IMPORTANT: Examples in this guide are adapted from the official Python documentation at https://docs.python.org/3/library/datetime.html
Overview
The tzinfo class is an abstract base class for timezone information objects. It provides the interface that must be implemented by concrete timezone classes. The tzinfo class itself cannot be instantiated directly - you must subclass it or use concrete implementations like timezone.
This is an abstract base class, meaning that this class should not be instantiated directly. Define a subclass of tzinfo to capture information about a particular time zone.
Key Characteristics
- Abstract Base Class: Cannot be instantiated directly, must be subclassed
- Timezone Interface: Defines the protocol for timezone information
- Immutable Contract: Subclasses should be immutable for thread safety
- DST Support: Provides methods for daylight saving time handling
- Flexible Implementation: Allows custom timezone logic
- Hashable Contract: Should be hashable if immutable
- UTC Conversion: Provides framework for timezone conversions
Prerequisites and Setup
Python Version Compatibility
- Minimum Python version required: Python 2.3+ (datetime module introduction)
Installation and Imports
# Standard library (no installation needed)
from datetime import tzinfo, timedelta, datetime
# Alternative import
import datetime
# Then use: datetime.tzinfo
Basic Usage
Official Documentation Examples
Source: All examples adapted from https://docs.python.org/3/library/datetime.html#tzinfo-objects
Simple Example
from datetime import tzinfo, timedelta, datetime
# Custom timezone implementation (from official docs)
class SimpleUTC(tzinfo):
"""UTC timezone implementation"""
def utcoffset(self, dt):
return timedelta(0)
def dst(self, dt):
return timedelta(0)
def tzname(self, dt):
return "UTC"
# Using custom timezone (from official docs)
utc = SimpleUTC()
dt = datetime(2025, 6, 14, 12, 30, tzinfo=utc)
print(f"UTC datetime: {dt}") # UTC datetime: 2025-06-14 12:30:00+00:00
Core Methods/Functions
from datetime import tzinfo, timedelta
# Abstract methods that must be implemented (from official docs)
class CustomTimezone(tzinfo):
def __init__(self, offset, name):
self.__offset = timedelta(hours=offset)
self.__name = name
def utcoffset(self, dt):
"""Return offset from UTC"""
return self.__offset
def dst(self, dt):
"""Return DST offset"""
return timedelta(0) # No DST
def tzname(self, dt):
"""Return timezone name"""
return self.__name
Common Patterns
# Pattern 1: Fixed offset timezone (from official docs)
class FixedOffset(tzinfo):
def __init__(self, hours, name):
self.__offset = timedelta(hours=hours)
self.__name = name
def utcoffset(self, dt):
return self.__offset
def dst(self, dt):
return timedelta(0)
def tzname(self, dt):
return self.__name
# Pattern 2: DST-aware timezone (from official docs)
class DSTTimezone(tzinfo):
def __init__(self, std_offset, dst_offset, std_name, dst_name):
self.__std_offset = timedelta(hours=std_offset)
self.__dst_offset = timedelta(hours=dst_offset)
self.__std_name = std_name
self.__dst_name = dst_name
def utcoffset(self, dt):
return self.__std_offset + self.dst(dt)
def dst(self, dt):
# Simplified DST logic (real implementation would be more complex)
if 3 <= dt.month <= 10: # March to October
return self.__dst_offset
return timedelta(0)
def tzname(self, dt):
if self.dst(dt):
return self.__dst_name
return self.__std_name
tzinfo API Reference
Abstract Methods (Must be Implemented)
| Method | Description | Return Type | Required | Example |
|---|---|---|---|---|
utcoffset(dt) | Return offset from UTC | timedelta or None | Yes | timedelta(hours=-5) |
dst(dt) | Return DST offset | timedelta or None | Yes | timedelta(hours=1) |
tzname(dt) | Return timezone name | str or None | Yes | "EST" |
Provided Methods (Can be Overridden)
| Method | Description | Return Type | Example |
|---|---|---|---|
fromutc(dt) | Convert from UTC to local time | datetime | tz.fromutc(utc_dt) |
Method Contracts and Requirements
utcoffset(dt)
- Purpose: Return the timezone's offset from UTC
- Return: timedelta object or None
- Contract: Must return the same value for all datetime objects
- Range: Should be between timedelta(hours=-24) and timedelta(hours=24)
dst(dt)
- Purpose: Return the daylight saving time offset
- Return: timedelta object (can be timedelta(0) for no DST)
- Contract: Must return timedelta(0) if timezone doesn't use DST
- Note: Added to utcoffset() to get total offset
tzname(dt)
- Purpose: Return a human-readable timezone name
- Return: String or None
- Examples: "UTC", "EST", "PST", "CET"
- Contract: Should be consistent for the same offset/DST state
Detailed Implementation Examples
Simple Fixed Offset Timezone (from official docs)
from datetime import tzinfo, timedelta, datetime
class FixedOffset(tzinfo):
"""Simple timezone with fixed offset from UTC."""
def __init__(self, hours, name):
self.__offset = timedelta(hours=hours)
self.__name = name
def utcoffset(self, dt):
"""Return the timezone offset from UTC."""
return self.__offset
def dst(self, dt):
"""Return the daylight saving time offset."""
return timedelta(0) # No DST
def tzname(self, dt):
"""Return the timezone name."""
return self.__name
def __repr__(self):
return f"FixedOffset({self.__offset.total_seconds()/3600}, {self.__name!r})"
# Usage (from official docs)
est = FixedOffset(-5, "EST")
pst = FixedOffset(-8, "PST")
cet = FixedOffset(1, "CET")
dt_est = datetime(2025, 6, 14, 7, 30, tzinfo=est)
dt_pst = datetime(2025, 6, 14, 4, 30, tzinfo=pst)
dt_cet = datetime(2025, 6, 14, 13, 30, tzinfo=cet)
print(f"EST: {dt_est}") # EST: 2025-06-14 07:30:00-05:00
print(f"PST: {dt_pst}") # PST: 2025-06-14 04:30:00-08:00
print(f"CET: {dt_cet}") # CET: 2025-06-14 13:30:00+01:00
DST-Aware Timezone (from official docs)
from datetime import tzinfo, timedelta, datetime
class SimpleDST(tzinfo):
"""Timezone with simplified DST rules."""
def __init__(self, std_hours, dst_hours, std_name, dst_name):
self.__std_offset = timedelta(hours=std_hours)
self.__dst_offset = timedelta(hours=dst_hours)
self.__std_name = std_name
self.__dst_name = dst_name
def utcoffset(self, dt):
"""Return total offset from UTC (standard + DST)."""
return self.__std_offset + self.dst(dt)
def dst(self, dt):
"""Return DST offset (simplified logic)."""
if dt is None:
return timedelta(0)
# Simplified DST: March to October
if 3 <= dt.month <= 10:
return self.__dst_offset
return timedelta(0)
def tzname(self, dt):
"""Return timezone name based on DST status."""
if self.dst(dt):
return self.__dst_name
return self.__std_name
# Usage (from official docs)
eastern = SimpleDST(-5, 1, "EST", "EDT")
# Winter time (no DST)
winter_dt = datetime(2025, 1, 15, 12, 0, tzinfo=eastern)
print(f"Winter: {winter_dt}") # Winter: 2025-01-15 12:00:00-05:00
print(f"Timezone: {winter_dt.tzname()}") # Timezone: EST
# Summer time (with DST)
summer_dt = datetime(2025, 7, 15, 12, 0, tzinfo=eastern)
print(f"Summer: {summer_dt}") # Summer: 2025-07-15 12:00:00-04:00
print(f"Timezone: {summer_dt.tzname()}") # Timezone: EDT
Custom UTC Implementation (from official docs)
from datetime import tzinfo, timedelta, datetime
class UTC(tzinfo):
"""UTC timezone implementation."""
def utcoffset(self, dt):
"""UTC has no offset from itself."""
return timedelta(0)
def dst(self, dt):
"""UTC has no daylight saving time."""
return timedelta(0)
def tzname(self, dt):
"""UTC timezone name."""
return "UTC"
def __repr__(self):
return "UTC()"
# Create singleton instance (from official docs)
utc = UTC()
# Usage (from official docs)
utc_dt = datetime(2025, 6, 14, 12, 30, tzinfo=utc)
print(f"UTC: {utc_dt}") # UTC: 2025-06-14 12:30:00+00:00
print(f"Offset: {utc_dt.utcoffset()}") # Offset: 0:00:00
print(f"DST: {utc_dt.dst()}") # DST: 0:00:00
print(f"Name: {utc_dt.tzname()}") # Name: UTC
Using fromutc() Method (from official docs)
from datetime import tzinfo, timedelta, datetime
class VerboseTimezone(tzinfo):
"""Timezone that logs conversions."""
def __init__(self, hours, name):
self.__offset = timedelta(hours=hours)
self.__name = name
def utcoffset(self, dt):
return self.__offset
def dst(self, dt):
return timedelta(0)
def tzname(self, dt):
return self.__name
def fromutc(self, dt):
"""Custom UTC conversion with logging."""
print(f"Converting UTC {dt} to {self.__name}")
# Call the default implementation
result = super().fromutc(dt)
print(f"Result: {result}")
return result
# Usage (from official docs)
est = VerboseTimezone(-5, "EST")
utc_dt = datetime(2025, 6, 14, 17, 30, tzinfo=datetime.timezone.utc)
# Convert from UTC
local_dt = utc_dt.astimezone(est)
# This will call fromutc() internally and print the conversion
Timezone with Validation (from official docs)
from datetime import tzinfo, timedelta, datetime
class ValidatedTimezone(tzinfo):
"""Timezone with input validation."""
def __init__(self, hours, name):
if not isinstance(hours, (int, float)):
raise TypeError("Hours must be a number")
if not -24 <= hours <= 24:
raise ValueError("Hours must be between -24 and 24")
if not isinstance(name, str):
raise TypeError("Name must be a string")
self.__offset = timedelta(hours=hours)
self.__name = name
def utcoffset(self, dt):
return self.__offset
def dst(self, dt):
return timedelta(0)
def tzname(self, dt):
return self.__name
def __eq__(self, other):
"""Enable timezone comparison."""
if not isinstance(other, ValidatedTimezone):
return False
return (self.__offset == other.__offset and
self.__name == other.__name)
def __hash__(self):
"""Make timezone hashable."""
return hash((self.__offset, self.__name))
def __repr__(self):
hours = self.__offset.total_seconds() / 3600
return f"ValidatedTimezone({hours}, {self.__name!r})"
# Usage (from official docs)
try:
valid_tz = ValidatedTimezone(-5, "EST")
invalid_tz = ValidatedTimezone(25, "INVALID") # Raises ValueError
except ValueError as e:
print(f"Validation error: {e}")
# Test equality and hashing
tz1 = ValidatedTimezone(-5, "EST")
tz2 = ValidatedTimezone(-5, "EST")
print(f"Equal: {tz1 == tz2}") # Equal: True
print(f"Hash: {hash(tz1) == hash(tz2)}") # Hash: True
Important Notes
Implementation Requirements
- Thread Safety: Subclasses should be immutable for thread safety
- Consistency: Methods should return consistent values for the same input
- Error Handling: Should handle None datetime arguments gracefully
- Performance: Methods may be called frequently, optimize for speed
Common Implementation Patterns
# Singleton pattern for commonly used timezones
class UTCSingleton(tzinfo):
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def utcoffset(self, dt):
return timedelta(0)
def dst(self, dt):
return timedelta(0)
def tzname(self, dt):
return "UTC"
# Factory pattern for timezone creation
def create_fixed_timezone(hours, name):
"""Factory function for creating fixed offset timezones."""
class _FixedTimezone(tzinfo):
def utcoffset(self, dt):
return timedelta(hours=hours)
def dst(self, dt):
return timedelta(0)
def tzname(self, dt):
return name
return _FixedTimezone()
Error Handling in Subclasses
from datetime import tzinfo, timedelta
class RobustTimezone(tzinfo):
"""Timezone implementation with robust error handling."""
def __init__(self, hours, name):
self.__offset = timedelta(hours=hours)
self.__name = name
def utcoffset(self, dt):
# Handle None dt argument
if dt is None:
return self.__offset
return self.__offset
def dst(self, dt):
# Always return timedelta, never None for simplicity
return timedelta(0)
def tzname(self, dt):
# Handle None dt argument
if dt is None:
return self.__name
return self.__name
Performance Considerations
- Methods are called frequently during datetime operations
- Cache computed values when possible
- Avoid expensive calculations in frequently called methods
- Consider using
__slots__for memory efficiency
Best Practices
- Make subclasses immutable - Essential for thread safety and hashing
- Handle None datetime arguments - Methods may receive None
- Implement eq and hash - Enable comparison and use in sets/dicts
- Validate inputs in constructor - Catch errors early
- Document timezone rules - Especially DST transition logic
- Use existing implementations when possible - Prefer
timezoneclass for simple cases - Test edge cases - DST transitions, leap years, etc.
- Consider caching - Cache frequently used timezone instances
When to Subclass tzinfo
Use tzinfo subclass when:
- Implementing complex DST rules
- Working with historical timezone data
- Creating timezone-aware applications with custom logic
- Need timezone rules not covered by standard implementations
Use existing implementations when:
- Simple fixed UTC offsets → Use
timezoneclass - Standard geographic timezones → Use
zoneinfomodule (Python 3.9+) - Third-party timezone data → Use
pytzlibrary
Related Classes
- datetime.timezone - Concrete tzinfo implementation
- datetime.datetime - Uses tzinfo for timezone awareness
- datetime.time - Can use tzinfo for timezone-aware times
- Main datetime module - Complete module overview
See Also
- zoneinfo module (Python 3.9+) - IANA timezone database support
- pytz library - Comprehensive timezone library for all Python versions