Skip to main content

Enforcing Temporal Constraints

To enforce temporal constraints such as range limits, timezone requirements, and precision levels, use the date_schema, datetime_schema, time_schema, and timedelta_schema functions from pydantic_core.core_schema.

Range Constraints

You can enforce upper and lower bounds for all temporal types using le (less than or equal), ge (greater than or equal), lt (less than), and gt (greater than). These constraints accept both Python objects and ISO8601 strings.

from datetime import date
from pydantic_core import SchemaValidator, core_schema, ValidationError

# Enforce a date range between 2019 and 2020
schema = core_schema.date_schema(le=date(2020, 1, 1), ge=date(2019, 1, 1))
v = SchemaValidator(schema)

# Valid input
assert v.validate_python(date(2019, 6, 1)) == date(2019, 6, 1)

# Invalid input
try:
v.validate_python(date(2021, 1, 1))
except ValidationError as e:
print(e)
# Input should be less than or equal to 2020-01-01 [type=less_than_equal, ...]

Relative Constraints (Past/Future)

For date and datetime types, you can enforce that a value is in the past or future relative to the current time using now_op.

from datetime import date, timedelta
from pydantic_core import SchemaValidator, core_schema

# Enforce that the date must be in the past
v = SchemaValidator(core_schema.date_schema(now_op='past'))

# Validates if the date is before 'now'
past_date = date.today() - timedelta(days=1)
assert v.validate_python(past_date) == past_date

# You can also specify a UTC offset (in seconds) for the 'now' calculation
# This defaults to the local system offset if not provided
v_offset = SchemaValidator(
core_schema.date_schema(now_op='future', now_utc_offset=3600)
)

Timezone Awareness

The tz_constraint parameter allows you to enforce whether a datetime or time must be timezone-aware, naive, or match a specific UTC offset.

from datetime import datetime, timezone, timedelta
from pydantic_core import SchemaValidator, core_schema, ValidationError

# Require timezone-aware datetimes
v_aware = SchemaValidator(core_schema.datetime_schema(tz_constraint='aware'))
assert v_aware.validate_python('2022-06-08T12:13:14Z').tzinfo is not None

# Require a specific UTC offset in seconds (e.g., +01:00 = 3600 seconds)
v_offset = SchemaValidator(core_schema.datetime_schema(tz_constraint=3600))
offset_dt = datetime.now(tz=timezone(timedelta(hours=1)))
assert v_offset.validate_python(offset_dt) == offset_dt

# Fails for UTC (offset 0) when 3600 is required
try:
v_offset.validate_python(datetime.now(tz=timezone.utc))
except ValidationError as e:
# Timezone offset of 3600 required, got 0 [type=timezone_offset, ...]
pass

Microsecond Precision

By default, pydantic-core truncates sub-microsecond precision (more than 6 digits in fractional seconds). You can change this behavior to raise an error instead using microseconds_precision.

from pydantic_core import SchemaValidator, core_schema, ValidationError

# Raise an error if precision exceeds 6 digits
v = SchemaValidator(core_schema.time_schema(microseconds_precision='error'))

# Valid precision
assert v.validate_python('12:00:00.123456')

# Invalid precision (7 digits)
try:
v.validate_python('12:00:00.1234567')
except ValidationError as e:
# Fractional seconds may have no more than 6 digits [type=time_parsing, ...]
pass

Troubleshooting

Inexact Date Errors

When validating a date, providing a datetime object that has a non-zero time component will result in a date_from_datetime_inexact error.

from datetime import datetime
from pydantic_core import SchemaValidator, core_schema, ValidationError

v = SchemaValidator(core_schema.date_schema())

# Fails because the time component is not 00:00:00
try:
v.validate_python(datetime(2022, 6, 8, 12, 0, 0))
except ValidationError as e:
# Datetime components must be all zero [type=date_from_datetime_inexact, ...]
pass

Timedelta Limits

TimedeltaSchema validates durations from ISO8601 strings, numeric seconds, or timedelta objects. Note that durations are capped at 999,999,999 days.

from datetime import timedelta
from pydantic_core import SchemaValidator, core_schema

v = SchemaValidator(core_schema.timedelta_schema(le=timedelta(days=1)))

# Validates numeric seconds
assert v.validate_python(3600) == timedelta(hours=1)

# Validates ISO8601 durations
assert v.validate_python('P1D') == timedelta(days=1)

Strict Mode

Setting strict=True on any temporal schema prevents coercion from strings or integers, requiring the actual Python object (e.g., datetime.date).

v_strict = SchemaValidator(core_schema.date_schema(strict=True))

# Fails for string input in strict mode
try:
v_strict.validate_python('2023-01-01')
except ValidationError:
# Input should be a valid date [type=date_type, ...]
pass