python/cpython · error · ValueError
fromutc() requires a non-None dst() result
Error message
fromutc() requires a non-None dst() result
What it means
Raised by tzinfo.fromutc() when dt.dst() returns None on the first call. The default fromutc algorithm computes delta = utcoffset - dst and applies it, so a None DST value breaks the arithmetic. A tzinfo may legitimately return None for dst() on naive datetimes (the argument is None), but fromutc always passes a real datetime, so returning None there signals an incomplete or sentinel implementation.
Source
Thrown at Lib/_pydatetime.py:1348
def fromutc(self, dt):
"datetime in UTC -> datetime in local time."
if not isinstance(dt, datetime):
raise TypeError("fromutc() requires a datetime argument")
if dt.tzinfo is not self:
raise ValueError("dt.tzinfo is not self")
dtoff = dt.utcoffset()
if dtoff is None:
raise ValueError("fromutc() requires a non-None utcoffset() "
"result")
# See the long comment block at the end of this file for an
# explanation of this algorithm.
dtdst = dt.dst()
if dtdst is None:
raise ValueError("fromutc() requires a non-None dst() result")
delta = dtoff - dtdst
if delta:
dt += delta
dtdst = dt.dst()
if dtdst is None:
raise ValueError("fromutc(): dt.dst gave inconsistent "
"results; cannot convert")
return dt + dtdst
# Pickle support.
def __reduce__(self):
getinitargs = getattr(self, "__getinitargs__", None)
if getinitargs:
args = getinitargs()
else:
args = ()
return (self.__class__, args, self.__getstate__())View on GitHub (pinned to bc6749cc3b)
Solutions
- Return timedelta(0) instead of None from dst() for zones without daylight saving
- Override fromutc() in the custom tzinfo so it does not depend on dst()
- Replace the custom zone with datetime.timezone or zoneinfo.ZoneInfo
Example fix
// before
class FixedTZ(tzinfo):
def utcoffset(self, dt): return timedelta(hours=2)
def dst(self, dt): return None
def tzname(self, dt): return 'F'
// after
class FixedTZ(tzinfo):
def utcoffset(self, dt): return timedelta(hours=2)
def dst(self, dt): return timedelta(0)
def tzname(self, dt): return 'F' Defensive patterns
Strategy: validation
Validate before calling
probe = dt.replace(tzinfo=tz)
if probe.dst() is None:
raise ValueError('dst() returned None; use timedelta(0) for no-DST zones')
tz.fromutc(probe) Type guard
def tz_dst_is_concrete(tz, dt) -> bool:
return dt.replace(tzinfo=tz).dst() is not None Try / catch
try:
local = tz.fromutc(dt)
except ValueError as e:
if 'dst()' in str(e):
local = dt + tz.utcoffset(dt) # fallback treating offset as fixed
else:
raise Prevention
- Use timedelta(0), not None, to express 'no DST' in custom zones
- Test the full fromutc/astimezone path, not just individual methods
- Adopt zoneinfo instead of hand-rolled DST logic
When it happens
Trigger: Custom tzinfo whose dst(self, dt) returns None unconditionally; dst() implementations that return None for dt values they classify as ambiguous; calling .astimezone()/fromutc on datetimes attached to such a zone.
Common situations: Fixed-offset zones written with `def dst(self, dt): return None` copied from an old recipe (the correct no-DST value is timedelta(0)); tzinfo fakes in test suites that stub dst with None.
Related errors
- fromutc(): dt.dst gave inconsistent results; cannot convert
- dt.tzinfo is not self
- fromutc() requires a non-None utcoffset() result
- Malformed time zone string
- offset must be a timedelta strictly between -timedelta(hours
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/64a27c82ed2f17b9.
Report an issue: GitHub.