Of course! Handling datetime objects with JSON in Python is a very common task. The core issue is that JSON doesn't have a native date/time type. It only supports string, number, boolean, null, array, and object.

Therefore, you must convert Python's datetime objects into one of these JSON-compatible types (most commonly a string) before serialization, and then convert them back after deserialization.
Here’s a complete guide covering the best methods, from simple to advanced.
The Problem: TypeError on Serialization
If you try to serialize a datetime object directly, you'll get a TypeError.
import json
from datetime import datetime
data = {
"user": "Alice",
"timestamp": datetime.now()
}
# This will raise a TypeError!
# json.dumps(data)
# TypeError: Object of type datetime is not JSON serializable
Solution 1: The Simple default Argument (Good for Quick Scripts)
The json.dumps() function has a default argument. If a value isn't JSON serializable, it calls the function you provide in default and uses its return value.

You can pass a simple lambda function that converts the object to its ISO 8601 string representation, which is a standard and unambiguous format.
import json
from datetime import datetime
data = {
"user": "Alice",
"timestamp": datetime.now(),
"login_time": datetime(2025, 10, 26, 10, 30, 0)
}
# Use the default argument with a lambda
json_string = json.dumps(data, default=str)
print(json_string)
# Example Output:
# {"user": "Alice", "timestamp": "2025-10-27T15:45:12.123456", "login_time": "2025-10-26T10:30:00"}
# To deserialize, you need to parse the string back into a datetime object
# This requires a custom hook for json.loads()
def datetime_decoder(dct):
for key, value in dct.items():
if isinstance(value, str):
try:
# Try to parse as ISO format
dct[key] = datetime.fromisoformat(value)
except (ValueError, TypeError):
# If it's not a datetime string, leave it as is
pass
return dct
# Deserializing the JSON string back to a Python dictionary
parsed_data = json.loads(json_string, object_hook=datetime_decoder)
print(parsed_data)
# Output:
# {'user': 'Alice', 'timestamp': datetime.datetime(2025, 10, 27, 15, 45, 12, 123456), 'login_time': datetime.datetime(2025, 10, 26, 10, 30)}
Pros:
- Simple, no extra libraries needed.
datetime.fromisoformat()is built-in and efficient for parsing.
Cons:
- You have to write and maintain the
datetime_decoderfunction yourself. - It will try to parse every string in your JSON, which is inefficient if you have many non-datetime strings.
Solution 2: Using orjson (Highly Recommended for Performance)
The orjson library is a fast, correct JSON library for Python. It's often 2-3x faster than the standard json library and has built-in support for datetime and other common types.

First, install it:
pip install orjson
Usage:
orjson automatically serializes datetime objects to ISO 8601 strings and deserializes them back into datetime objects without any extra code.
import orjson
from datetime import datetime
data = {
"user": "Bob",
"timestamp": datetime.now(),
"last_active": datetime(2025, 1, 1, 0, 0, 0)
}
# --- Serialization ---
# orjson.dumps() returns bytes, not a string.
# You need to decode it to get a JSON string.
json_bytes = orjson.dumps(data)
json_string = json_bytes.decode('utf-8')
print(json_string)
# Output:
# {"user": "Bob", "timestamp": "2025-10-27T15:50:00.123456", "last_active": "2025-01-01T00:00:00"}
# --- Deserialization ---
# orjson.loads() takes bytes, so you need to encode the string first.
parsed_data = orjson.loads(json_string.encode('utf-8'))
print(parsed_data)
# Output:
# {'user': 'Bob', 'timestamp': datetime.datetime(2025, 10, 27, 15, 50, 0, 123456), 'last_active': datetime.datetime(2025, 1, 1, 0, 0)}
Pros:
- Extremely fast.
- Handles
datetime(anddate,time,timedelta) automatically. - Produces compact, valid JSON.
Cons:
- Requires an external library (
pip install orjson). - Returns
bytesinstead of astr, which is a slight change in API.
Solution 3: The json Module with Custom Encoders/Decoders (The Robust Way)
For full control within the standard library, you can create a custom JSONEncoder and a corresponding object hook for decoding. This is the most robust and maintainable approach for larger applications.
Create a Custom JSONEncoder
Subclass json.JSONEncoder and override its default() method.
from datetime import datetime, date
import json
class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime, date)):
# Convert datetime and date objects to ISO format strings
return obj.isoformat()
# Let the base class default method raise the TypeError for other types
return super().default(obj)
Create a Custom Decoder Hook
This function will be passed to json.loads()'s object_hook. It inspects each dictionary as it's being decoded and attempts to convert string values that look like ISO-formatted dates.
import re
def datetime_decoder(dct):
# Regex to match ISO 8601 date and datetime strings
# e.g., "2025-10-27", "2025-10-27T15:50:00", "2025-10-27T15:50:00.123456"
iso_datetime_pattern = re.compile(r'^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?)?$')
for key, value in dct.items():
if isinstance(value, str) and iso_datetime_pattern.match(value):
if 'T' in value:
# It's a datetime string
try:
dct[key] = datetime.fromisoformat(value)
except ValueError:
pass # Not a valid datetime, leave as string
else:
# It's a date string
try:
dct[key] = date.fromisoformat(value)
except ValueError:
pass # Not a valid date, leave as string
return dct
Putting It All Together
from datetime import datetime, date
# --- Data to serialize ---
data = {
"event": "Product Launch",
"start_date": date(2025, 12, 25),
"event_time": datetime(2025, 12, 25, 12, 0, 0),
"details": "A Christmas special."
}
# --- Serialization ---
json_string = json.dumps(data, cls=DateTimeEncoder, indent=2)
print("--- Serialized JSON ---")
print(json_string)
print("\n")
# --- Deserialization ---
parsed_data = json.loads(json_string, object_hook=datetime_decoder)
print("--- Deserialized Python Object ---")
print(parsed_data)
print(f"Type of 'start_date': {type(parsed_data['start_date'])}")
print(f"Type of 'event_time': {type(parsed_data['event_time'])}")
Output:
--- Serialized JSON ---
{
"event": "Product Launch",
"start_date": "2025-12-25",
"event_time": "2025-12-25T12:00:00",
"details": "A Christmas special."
}
--- Deserialized Python Object ---
{'event': 'Product Launch', 'start_date': datetime.date(2025, 12, 25), 'event_time': datetime.datetime(2025, 12, 25, 12, 0), 'details': 'A Christmas special.'}
Type of 'start_date': <class 'datetime.date'>
Type of 'event_time': <class 'datetime.datetime'>
Pros:
- Uses only the standard library.
- Very explicit and robust.
- Easily extensible to handle other custom types (e.g., UUIDs, Decimals).
Cons:
- More code to write and maintain compared to the other solutions.
Summary: Which Method Should You Use?
| Method | Best For | Pros | Cons | | :---
