If you want the model to still hold an actual date object in that field, you should definitely leave the annotation as date_of_birth: date rather than simply switching to str.

If you want to allow strings with a particular format to be passed as a special case, you should define a pre=True validator that will check for strings and attempt to parse those accordingly. This is similar to @suraj's answer, but has the advantage of type safety.

For even more clarity, you could set your desired date format as a class variable on the module.

For the API, you could leverage the fact that the JSON Schema specs allow the format keyword for string types with custom format specifications. You could extend the automatically generated schema for the model to display for example that same date format for every date type field in the model instead of just showing the standard format called "date". Note that there is no special type for dates in JSON, so the Swagger UI would always have to show that field as string regardless.

Lastly, to make it crystal clear for users of the API what that format represents, you could add an example data object to the schema at the end.

Here is the full code I suggest:

from __future__ import annotations
from datetime import date, datetime
from typing import Any, ClassVar

from pydantic import BaseModel, validator


class Profile(BaseModel):
    date_format: ClassVar[str] = "%d-%b-%Y"
    example_data: ClassVar[dict[str, Any]] = {
        "name": "John Doe",
        "date_of_birth": "15-Jul-1996",
        "gender": "m",
    }

    name: str
    date_of_birth: date
    gender: str

    @validator("date_of_birth", pre=True)
    def string_to_date(cls, v: object) -> object:
        if isinstance(v, str):
            return datetime.strptime(v, cls.date_format).date()
        return v

    class Config:
        @staticmethod
        def schema_extra(schema: dict[str, Any], model: type[Profile]) -> None:
            for name, field in model.__fields__.items():
                if field.type_ is date:
                    schema["properties"][name]["format"] = model.date_format
            schema["examples"] = [model.example_data]

Demo:

profile_example = Profile.parse_obj(Profile.example_data)
print(profile_example.json(indent=4))
print(Profile.schema_json(indent=4))

Output:

{
    "name": "John Doe",
    "date_of_birth": "1996-07-15",
    "gender": "m"
}
{
    "title": "Profile",
    "type": "object",
    "properties": {
        "name": {
            "title": "Name",
            "type": "string"
        },
        "date_of_birth": {
            "title": "Date Of Birth",
            "type": "string",
            "format": "%d-%b-%Y"
        },
        "gender": {
            "title": "Gender",
            "type": "string"
        }
    },
    "required": [
        "name",
        "date_of_birth",
        "gender"
    ],
    "examples": [
        {
            "name": "John Doe",
            "date_of_birth": "15-Jul-1996",
            "gender": "m"
        }
    ]
}

Even if you don't want to go the extra mile with the schema customization, I would still argue for using the proper field type and pre-validating strings instead.

Here is the minimal version:

from datetime import date, datetime

from pydantic import BaseModel, validator


class Profile(BaseModel):
    name: str
    date_of_birth: date
    gender: str

    @validator("date_of_birth", pre=True)
    def string_to_date(cls, v: object) -> object:
        if isinstance(v, str):
            return datetime.strptime(v, "%d-%b-%Y").date()
        return v

The disadvantage is that the format default shown in the JSON Schema is date, which has a different meaning and is equivalent to %d-%m-%Y, which obviously does not fit your actual requirements. But parsing would still work of course with that validator.

Answer from Daniel Fainberg on Stack Overflow
🌐
Orchestra
getorchestra.io › guides › fastapi-and-datetime-types-a-comprehensive-guide
FastAPI and Datetime Types: A Comprehensive Guide | Orchestra
March 20, 2024 - Handling date and time types ... web applications. By leveraging Python's datetime module and FastAPI's seamless integration with Pydantic, you can efficiently manage datetime data in your APIs....
🌐
FastAPI
fastapi.tiangolo.com › tutorial › extra-data-types
Extra Data Types - FastAPI
Standard Python Decimal. In requests and responses, handled the same as a float. You can check all the valid Pydantic data types here: Pydantic data types. Here's an example path operation with parameters using some of the above types. ... from datetime import datetime, time, timedelta from typing import Annotated from uuid import UUID from fastapi import Body, FastAPI app = FastAPI() @app.put("/items/{item_id}") async def read_items( item_id: UUID, start_datetime: Annotated[datetime, Body()], end_datetime: Annotated[datetime, Body()], process_after: Annotated[timedelta, Body()], repeat_at: An
Top answer
1 of 2
7

If you want the model to still hold an actual date object in that field, you should definitely leave the annotation as date_of_birth: date rather than simply switching to str.

If you want to allow strings with a particular format to be passed as a special case, you should define a pre=True validator that will check for strings and attempt to parse those accordingly. This is similar to @suraj's answer, but has the advantage of type safety.

For even more clarity, you could set your desired date format as a class variable on the module.

For the API, you could leverage the fact that the JSON Schema specs allow the format keyword for string types with custom format specifications. You could extend the automatically generated schema for the model to display for example that same date format for every date type field in the model instead of just showing the standard format called "date". Note that there is no special type for dates in JSON, so the Swagger UI would always have to show that field as string regardless.

Lastly, to make it crystal clear for users of the API what that format represents, you could add an example data object to the schema at the end.

Here is the full code I suggest:

from __future__ import annotations
from datetime import date, datetime
from typing import Any, ClassVar

from pydantic import BaseModel, validator


class Profile(BaseModel):
    date_format: ClassVar[str] = "%d-%b-%Y"
    example_data: ClassVar[dict[str, Any]] = {
        "name": "John Doe",
        "date_of_birth": "15-Jul-1996",
        "gender": "m",
    }

    name: str
    date_of_birth: date
    gender: str

    @validator("date_of_birth", pre=True)
    def string_to_date(cls, v: object) -> object:
        if isinstance(v, str):
            return datetime.strptime(v, cls.date_format).date()
        return v

    class Config:
        @staticmethod
        def schema_extra(schema: dict[str, Any], model: type[Profile]) -> None:
            for name, field in model.__fields__.items():
                if field.type_ is date:
                    schema["properties"][name]["format"] = model.date_format
            schema["examples"] = [model.example_data]

Demo:

profile_example = Profile.parse_obj(Profile.example_data)
print(profile_example.json(indent=4))
print(Profile.schema_json(indent=4))

Output:

{
    "name": "John Doe",
    "date_of_birth": "1996-07-15",
    "gender": "m"
}
{
    "title": "Profile",
    "type": "object",
    "properties": {
        "name": {
            "title": "Name",
            "type": "string"
        },
        "date_of_birth": {
            "title": "Date Of Birth",
            "type": "string",
            "format": "%d-%b-%Y"
        },
        "gender": {
            "title": "Gender",
            "type": "string"
        }
    },
    "required": [
        "name",
        "date_of_birth",
        "gender"
    ],
    "examples": [
        {
            "name": "John Doe",
            "date_of_birth": "15-Jul-1996",
            "gender": "m"
        }
    ]
}

Even if you don't want to go the extra mile with the schema customization, I would still argue for using the proper field type and pre-validating strings instead.

Here is the minimal version:

from datetime import date, datetime

from pydantic import BaseModel, validator


class Profile(BaseModel):
    name: str
    date_of_birth: date
    gender: str

    @validator("date_of_birth", pre=True)
    def string_to_date(cls, v: object) -> object:
        if isinstance(v, str):
            return datetime.strptime(v, "%d-%b-%Y").date()
        return v

The disadvantage is that the format default shown in the JSON Schema is date, which has a different meaning and is equivalent to %d-%m-%Y, which obviously does not fit your actual requirements. But parsing would still work of course with that validator.

2 of 2
0

Try this. You can use @validator

from pydantic import BaseModel, validator
from datetime import datetime

class Profile(BaseModel):
    name: str
    DOB: str  # change this to str
    gender: str

    @validator('DOB')
    def parse_dob(cls, v):
        return datetime.strptime(v, '%d-%b-%Y').date()  # convert string to date

🌐
Top Tech Tips
toptechtips.github.io › 2023-05-28-how-to-return-datetime-fastapi
How to return datetime, dicts, lists, xml, pydantic models and other data types response in fastAPI (With Examples)
May 28, 2023 - Convert return data into JSON-compatible data (e.g. a python dict) FastAPI uses JSONReponse(content=JSON-compatible data) behind the scenes ... You can however skip all this and create your own custom response using JSONResponse, Response (or any other subclass of Response) - But more on this after these examples. @app.get("/item") async def get_item(): return {"item": "toy"} ... datetime.datetime: A Python datetime.datetime.
🌐
Readthedocs
fastapi-contrib.readthedocs.io › en › latest › _modules › fastapi_contrib › common › utils.html
fastapi_contrib.common.utils — FastAPI Contrib 0.2.11 documentation
import importlib import sys import pytz from datetime import datetime from functools import wraps from time import time from typing import Any from fastapi import FastAPI from fastapi_contrib.conf import settings · [docs]def resolve_dotted_path(path: str) -> Any: """ Retrieves attribute (var, function, class, etc.) from module by dotted path .. code-block:: python from datetime.datetime import utcnow as default_utcnow utcnow = resolve_dotted_path('datetime.datetime.utcnow') assert utcnow == default_utcnow :param path: dotted path to the attribute in module :return: desired attribute or None """ splitted = path.split(".") if len(splitted) <= 1: return importlib.import_module(path) module, attr = ".".join(splitted[:-1]), splitted[-1] module = importlib.import_module(module) return getattr(module, attr)
Find elsewhere
🌐
GitHub
github.com › fastapi › fastapi › issues › 2519
Using default for datetime query parameter without timezone leads to OpenAPI SpecValidationException · Issue #2519 · fastapi/fastapi
December 15, 2020 - I already checked if it is not related to FastAPI but to ReDoc. ... Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there. I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future. Implement a Pull Request for a confirmed bug. Here's a self-contained, minimal, reproducible, example with my use case: from datetime import datetime from fastapi import FastAPI import uvicorn app = FastAPI() start = datetime.today() @app.get("/") async def root(start_date: datetime = start): print(start_date) return {"start_date": start_date} if __name__ == "__main__": uvicorn.run(app, host="127.0.0.1", port=8000)
Author   johnthagen
🌐
Mintlify
mintlify.com › mintlify atlas › mintlify-atlas/docs-atlas-9ddd80c7 › extra data types
Extra Data Types - FastAPI
March 1, 2026 - Beyond basic types like str, int, and float, FastAPI supports many specialized data types from Python’s standard library and Pydantic. FastAPI/Pydantic support these additional types with automatic validation: ... from datetime import datetime, time, timedelta from uuid import UUID from fastapi import Body, FastAPI app = FastAPI() @app.put("/items/{item_id}") async def read_items( item_id: UUID, start_datetime: datetime = Body(), end_datetime: datetime = Body(), process_after: timedelta = Body(), repeat_at: time | None = Body(default=None), ): start_process = start_datetime + process_after duration = end_datetime - start_process return { "item_id": item_id, "start_datetime": start_datetime, "end_datetime": end_datetime, "process_after": process_after, "repeat_at": repeat_at, "start_process": start_process, "duration": duration, }
🌐
Medium
medium.com › @rameshkannanyt0078 › how-to-handle-timezones-properly-in-fastapi-and-database-68b1c019c1bc
📌 How to Handle Timezones Properly in FastAPI and Database | by Ramesh Kannan s | Medium
April 28, 2025 - Always use timezone-aware datetime objects. pip install pytz python-dateutil fastapi uvicorn · pytz: For working with timezones properly. python-dateutil: For flexible parsing (optional but good practice). If you are using SQLAlchemy or similar ORM, we will ensure that it handles timezone-aware fields too.
🌐
GitHub
github.com › fastapi › fastapi › issues › 3183
How to maintain the datetime format same as input · Issue #3183 · fastapi/fastapi
May 6, 2021 - First check I added a very descriptive title to this issue. I used the GitHub search to find a similar issue and didn't find it. I searched the FastAPI documentation, with the integrated search. I already searched in Google "How to X in ...
Author   yantrikskaran
🌐
Stack Overflow
stackoverflow.com › questions › 74728163 › how-to-force-fastapi-to-return-formatted-datetime
python - How to force FastApi to return formatted DateTime - Stack Overflow
According to the documentation, this is how the datetime type is returned: A Python datetime.datetime. In requests and responses will be represented as a str in ISO 8601 format, like: 2008-09-15T...
🌐
Stack Overflow
stackoverflow.com › questions › 76551204 › how-to-serialize-post-request-with-datetime-in-fastapi
python 3.x - How to serialize post request with datetime in FastAPI - Stack Overflow
class TaskBase(BaseModel): title: str | None = None description: str | None = None deadline: datetime | None = None class TaskCreate(TaskBase): title: str description: str # performers: list[UserBase] | None = None class Config: orm_mode = True
🌐
Fastapiinteractive
fastapiinteractive.com › fastapi-basics › 10-extra-data-types › theory
Extra Data Types - Theory | FastAPI Basics | FastAPI Tutorial
The official FastAPI documentation now recommends using Annotated for better type hints and IDE support: from typing import Annotated # Modern approach (recommended) start_datetime: Annotated[datetime, Body()] # Older approach (still works) start_datetime: datetime = Body() ... # You can perform normal Python operations start_process = start_datetime + process_after # datetime + timedelta duration = end_datetime - start_process # datetime - datetime is_same_day = start_datetime.date() == end_datetime.date()
🌐
GitHub
github.com › fastapi › fastapi › issues › 2677
Datetime with timezone in request doesn't work · Issue #2677 · fastapi/fastapi
January 19, 2021 - from fastapi import FastAPI from datetime import datetime from ..models import Contact from ..database import Database app = FastAPI() # Dependency def get_db(): db = Database() try: yield db finally: db.disconnect() @app.get("/contacts/", response_model=List[Contact]) async def get_contacts(address: int, start_time: datetime, end_time: datetime, duration: int, distance: int, db: Database = Depends(get_db)): contacts = detect_contacts(db, address, start_time, end_time, duration, distance) return contacts
Author   Pei116
🌐
PyPI
pypi.org › project › fastapi-query-conditions
fastapi-query-conditions · PyPI
from typing import Dict from fastapi import Depends, FastAPI from fastapi_query_conditions import query_conditions app = FastAPI() @app.get("/items") def query_items(amount: Dict[str, int] = Depends(query_conditions(field='amount', factory=int))): print(amount) return amount · Then, if you send a request to /items?amount[gte]=1000&amount[lt]=2000, you can check the following results. ... Also, you can use various factory functions for your query parameter. from datetime import datetime from typing import Dict from fastapi import Depends, FastAPI from fastapi_query_conditions import query_conditions app = FastAPI() @app.get("/orders") def query_orders(time: Dict[str, int] = Depends(query_conditions(field='time', factory=datetime.fromisoformat))): print(time) return time
      » pip install fastapi-query-conditions
    
Published   Dec 18, 2022
Version   1.0.2