Bug description
fields.DateTime(format="timestamp") can serialize valid datetimes before the Unix epoch (1970) into negative POSIX timestamps, but marshmallow then rejects those same negative values when deserializing them.
This creates a round-trip failure where a value produced by Marshmallow cannot be read back by the same field.
Reproduction
from datetime import datetime, timezone
from marshmallow import fields
field = fields.DateTime(format="timestamp")
value = datetime(1950, 5, 3, tzinfo=timezone.utc)
serialized = field.serialize("date", {"date": value})
print(serialized)
# -620568000.0
field.deserialize(serialized)
# marshmallow.exceptions.ValidationError:
# Not a valid datetime.
The underlying Python datetime.fromtimestamp() supports negative timestamps, so the negative value itself is valid.
Expected behavior
A timestamp produced by fields.DateTime(format="timestamp") should be accepted by the corresponding deserializer, including valid negative POSIX timestamps representing dates before 1970.
Actual behavior
Negative POSIX timestamps are rejected during deserialization, even though the serializer can produce them from valid pre-1970 datetimes.
This means timestamp serialization is not reliably round-tripable for dates before the Unix epoch.
Root cause
marshmallow.utils.from_timestamp() explicitly rejects values below zero before calling Python's timestamp conversion.
The underlying datetime.fromtimestamp() implementation can correctly handle these negative values, so the explicit negative-value restriction is unnecessarily broad.
Existing OSError and OverflowError handling can continue to handle genuine platform-specific timestamp conversion failures.
Proposed fix
Remove the explicit rejection of negative timestamp values from from_timestamp() and rely on the existing timestamp conversion and exception handling.
Add regression coverage that:
- Serializes valid pre-1970 datetimes using both
timestamp and timestamp_ms.
- Confirms the serialized values are negative.
- Deserializes those values.
- Confirms the original datetimes are recovered.
Validation
I have reproduced the issue on the current development branch.
The regression test fails on the unfixed implementation and passes after the proposed fix.
The full test suite passes with the fix.
I would like to contribute a fix for this issue.
Bug description
fields.DateTime(format="timestamp")can serialize valid datetimes before the Unix epoch (1970) into negative POSIX timestamps, butmarshmallowthen rejects those same negative values when deserializing them.This creates a round-trip failure where a value produced by Marshmallow cannot be read back by the same field.
Reproduction
The underlying Python
datetime.fromtimestamp()supports negative timestamps, so the negative value itself is valid.Expected behavior
A timestamp produced by
fields.DateTime(format="timestamp")should be accepted by the corresponding deserializer, including valid negative POSIX timestamps representing dates before 1970.Actual behavior
Negative POSIX timestamps are rejected during deserialization, even though the serializer can produce them from valid pre-1970 datetimes.
This means timestamp serialization is not reliably round-tripable for dates before the Unix epoch.
Root cause
marshmallow.utils.from_timestamp()explicitly rejects values below zero before calling Python's timestamp conversion.The underlying
datetime.fromtimestamp()implementation can correctly handle these negative values, so the explicit negative-value restriction is unnecessarily broad.Existing
OSErrorandOverflowErrorhandling can continue to handle genuine platform-specific timestamp conversion failures.Proposed fix
Remove the explicit rejection of negative timestamp values from
from_timestamp()and rely on the existing timestamp conversion and exception handling.Add regression coverage that:
timestampandtimestamp_ms.Validation
I have reproduced the issue on the current development branch.
The regression test fails on the unfixed implementation and passes after the proposed fix.
The full test suite passes with the fix.
I would like to contribute a fix for this issue.