Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented UTCDatetime constrained type #8158

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions pydantic/__init__.py
Expand Up @@ -176,6 +176,7 @@
'PastDatetime',
'FutureDatetime',
'AwareDatetime',
'UTCDatetime',
'NaiveDatetime',
'AllowInfNan',
'EncoderProtocol',
Expand Down Expand Up @@ -325,6 +326,7 @@
'PastDatetime': (__package__, '.types'),
'FutureDatetime': (__package__, '.types'),
'AwareDatetime': (__package__, '.types'),
'UTCDatetime': (__package__, '.types'),
'NaiveDatetime': (__package__, '.types'),
'AllowInfNan': (__package__, '.types'),
'EncoderProtocol': (__package__, '.types'),
Expand Down
21 changes: 21 additions & 0 deletions pydantic/types.py
Expand Up @@ -92,6 +92,7 @@
'FutureDatetime',
'condate',
'AwareDatetime',
'UTCDatetime',
'NaiveDatetime',
'AllowInfNan',
'EncoderProtocol',
Expand Down Expand Up @@ -2067,6 +2068,7 @@ def condate(

if TYPE_CHECKING:
AwareDatetime = Annotated[datetime, ...]
UTCDatetime = Annotated[datetime, ...]
NaiveDatetime = Annotated[datetime, ...]
PastDatetime = Annotated[datetime, ...]
FutureDatetime = Annotated[datetime, ...]
Expand All @@ -2092,6 +2094,25 @@ def __get_pydantic_core_schema__(
def __repr__(self) -> str:
return 'AwareDatetime'

class UTCDatetime:
"""A datetime that needs UTC as the timezone."""

@classmethod
def __get_pydantic_core_schema__(
cls, source: type[Any], handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
if cls is source:
# used directly as a type
return core_schema.datetime_schema(tz_constraint=0)
else:
schema = handler(source)
_check_annotated_type(schema['type'], 'datetime', cls.__name__)
schema['tz_constraint'] = 0
return schema

def __repr__(self) -> str:
return 'UTCDatetime'

class NaiveDatetime:
"""A datetime that doesn't require timezone info."""

Expand Down
2 changes: 2 additions & 0 deletions tests/mypy/modules/success.py
Expand Up @@ -39,6 +39,7 @@
StrictInt,
StrictStr,
UrlConstraints,
UTCDatetime,
WrapValidator,
create_model,
field_validator,
Expand Down Expand Up @@ -243,6 +244,7 @@ class PydanticTypes(BaseModel):
my_past_datetime: PastDatetime = datetime.now() - timedelta(1)
my_future_datetime: FutureDatetime = datetime.now() + timedelta(1)
my_aware_datetime: AwareDatetime = datetime.now(tz=timezone.utc)
my_utc_datetime: UTCDatetime = datetime.now(tz=timezone.utc)
my_naive_datetime: NaiveDatetime = datetime.now()


Expand Down
2 changes: 2 additions & 0 deletions tests/mypy/outputs/1.0.1/mypy-default_ini/success.py
Expand Up @@ -14,6 +14,7 @@
from pydantic import (
UUID1,
AwareDatetime,
UTCDatetime,
BaseModel,
ConfigDict,
DirectoryPath,
Expand Down Expand Up @@ -249,6 +250,7 @@ class PydanticTypes(BaseModel):
my_past_datetime: PastDatetime = datetime.now() - timedelta(1)
my_future_datetime: FutureDatetime = datetime.now() + timedelta(1)
my_aware_datetime: AwareDatetime = datetime.now(tz=timezone.utc)
my_utc_datetime: UTCDatetime = datetime.now(tz=timezone.utc)
my_naive_datetime: NaiveDatetime = datetime.now()


Expand Down
2 changes: 2 additions & 0 deletions tests/mypy/outputs/1.0.1/pyproject-default_toml/success.py
Expand Up @@ -14,6 +14,7 @@
from pydantic import (
UUID1,
AwareDatetime,
UTCDatetime,
BaseModel,
ConfigDict,
DirectoryPath,
Expand Down Expand Up @@ -249,6 +250,7 @@ class PydanticTypes(BaseModel):
my_past_datetime: PastDatetime = datetime.now() - timedelta(1)
my_future_datetime: FutureDatetime = datetime.now() + timedelta(1)
my_aware_datetime: AwareDatetime = datetime.now(tz=timezone.utc)
my_utc_datetime: UTCDatetime = datetime.now(tz=timezone.utc)
my_naive_datetime: NaiveDatetime = datetime.now()


Expand Down
7 changes: 7 additions & 0 deletions tests/test_datetime.py
Expand Up @@ -13,6 +13,7 @@
NaiveDatetime,
PastDate,
PastDatetime,
UTCDatetime,
ValidationError,
condate,
)
Expand Down Expand Up @@ -49,6 +50,11 @@ def aware_datetime_type(request):
return request.param


@pytest.fixture(scope='module', params=[UTCDatetime, Annotated[datetime, UTCDatetime()]])
def utc_datetime_type(request):
return request.param


@pytest.fixture(scope='module', params=[NaiveDatetime, Annotated[datetime, NaiveDatetime()]])
def naive_datetime_type(request):
return request.param
Expand Down Expand Up @@ -605,6 +611,7 @@ class Model(BaseModel):
FutureDatetime,
NaiveDatetime,
AwareDatetime,
UTCDatetime,
),
)
def test_invalid_annotated_type(annotation):
Expand Down
3 changes: 3 additions & 0 deletions tests/test_types.py
Expand Up @@ -101,6 +101,7 @@
StringConstraints,
Tag,
TypeAdapter,
UTCDatetime,
ValidationError,
conbytes,
condate,
Expand Down Expand Up @@ -4305,6 +4306,7 @@ class Foobar(BaseModel):
PastDatetime,
FutureDatetime,
AwareDatetime,
UTCDatetime,
NaiveDatetime,
],
)
Expand Down Expand Up @@ -6128,6 +6130,7 @@ def test_annotated_default_value_functional_validator() -> None:
(PastDate, 'PastDate'),
(FutureDate, 'FutureDate'),
(AwareDatetime, 'AwareDatetime'),
(UTCDatetime, 'UTCDatetime'),
(NaiveDatetime, 'NaiveDatetime'),
(PastDatetime, 'PastDatetime'),
(FutureDatetime, 'FutureDatetime'),
Expand Down