isinstance(x, date) Silently Matches datetime Too (tested)
datetime.datetime is a subclass of datetime.date. We measured what that means for an isinstance() check meant to tell them apart: a datetime value passes an isinstance(x, date) test too, with nothing to signal that the match was the wrong one.
1. A datetime instance passes isinstance(x, date)
from datetime import date, datetime
dt = datetime(2026, 6, 15, 14, 30, 0)
print(isinstance(dt, date)) # checking for a plain date
print(isinstance(dt, datetime)) # checking for a datetime
True
True
Measured: the same object answers True to both checks. Per the docs, datetime.datetime is implemented as a subclass of datetime.date, adding time-of-day fields. There's no exception or warning — isinstance(x, date) simply can't distinguish "this is a plain date" from "this is a datetime, which is also a date."
2. Why order matters in a dispatch function
def describe(x):
if isinstance(x, date):
return "date-only: " + x.isoformat()
return "datetime: " + x.isoformat()
print(describe(dt))
date-only: 2026-06-15T14:30:00
Measured: a function written to check "is this a plain date" first catches the datetime value too, and labels it date-only — even though the exact same object's .isoformat() call still prints the full time of day. The label and the data disagree, silently, because the branch that ran was never meant to handle a value with a time component.
3. The reverse check doesn't have this problem
d = date(2026, 6, 15)
print(isinstance(d, datetime))
False
Measured: a plain date does not pass an isinstance(x, datetime) check — the subclass relationship only goes one direction. This asymmetry is exactly why check order matters: testing for datetime first is safe, testing for date first is not.
4. Fix: check the more specific type first, or use type() for exact matching
def describe_fixed(x):
if isinstance(x, datetime): # check the subclass FIRST
return "datetime: " + x.isoformat()
if isinstance(x, date):
return "date-only: " + x.isoformat()
return "not a date/datetime"
print(describe_fixed(dt))
print(describe_fixed(date(2026, 6, 15)))
datetime: 2026-06-15T14:30:00
date-only: 2026-06-15
Measured: checking isinstance(x, datetime) before isinstance(x, date) correctly routes both values. If you specifically need to exclude datetime instances from a "plain date" branch, type(x) is date (confirmed False for our dt instance above) is the exact-type check that isinstance() alone can't give you.
Frequently asked questions
Is datetime.datetime a subclass of datetime.date in Python?
Yes -- per the datetime docs, datetime is a subclass of date that adds time-of-day fields. We confirmed isinstance(a_datetime, date) returns True.
Why did my function treat a datetime value as a plain date?
We measured a dispatch function that checks isinstance(x, date) before isinstance(x, datetime) silently matching datetime values on the date branch, since the subclass check succeeds for both.
Does isinstance(a_date, datetime) also return True for a plain date?
No -- we confirmed the relationship only goes one way: a plain date object does not pass an isinstance check against datetime.
How do I check for an exact date, excluding datetime instances?
We confirmed type(x) is date returns False for a datetime instance, where isinstance(x, date) returns True -- use type() for an exact match if that distinction matters.
What's the safe order for isinstance checks on date and datetime?
We verified checking isinstance(x, datetime) first, then isinstance(x, date) as a fallback, correctly routes both types -- the more specific subclass needs to be checked before the more general base class.
Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in datetime module: ran isinstance() of a datetime.datetime instance against datetime.date, and against a date-vs-datetime dispatch function that checks isinstance(x, date) before isinstance(x, datetime). PlainHeaders is our extension; Python's datetime module is built in.