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
🌐
FastAPI
fastapi.tiangolo.com › tutorial › extra-data-types
Extra Data Types - FastAPI
September 15, 2008 - 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: Annotated[time | None, Body()] = 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, } 🤓 Other versions and variants ·
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

Discussions

FastAPI Query Parameters with datetime.date not updating default value?
First Check I added a very descriptive title here. I used the GitHub search to find a similar question and didn't find it. I searched the FastAPI documentation, with the integrated search. I al... More on github.com
🌐 github.com
3
1
python - FastAPI: datetime with timezone in request doesn't work - Stack Overflow
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: More on stackoverflow.com
🌐 stackoverflow.com
Using default for datetime query parameter without timezone leads to OpenAPI SpecValidationException
Using default for datetime query parameter without timezone leads to OpenAPI SpecValidationException#2519 ... 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. More on github.com
🌐 github.com
9
December 15, 2020
How to maintain the datetime format same as input
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 ... More on github.com
🌐 github.com
7
May 6, 2021
🌐
Orchestra
getorchestra.io › guides › fastapi-and-datetime-types-a-comprehensive-guide
FastAPI and Datetime Types: A Comprehensive Guide | Orchestra
March 20, 2024 - Use Timezone-Aware Datetimes: Always prefer timezone-aware datetime objects to ensure consistency across different geographies. Validate Dates and Times: Implement custom validation logic if your application requires constraints on dates or times (e.g., future dates only). ISO 8601 Format: Stick to the ISO 8601 format for date and time serialization to ensure compatibility and readability. Handling date and time types effectively in FastAPI is a vital skill for developing robust web applications.
🌐
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 - from pydantic import BaseModel from datetime import datetime class TokenData(BaseModel): access_token: str refresh_token: str issued_at: datetime # will parse and validate ISO8601 UTC datetimes expires_at: datetime · When FastAPI parses or serializes datetime fields:
Find elsewhere
🌐
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 - Example 1 - Using a Pydantic Model and datetime object with custom JSON response for error handling ... FastAPI has made it easy for you to handle responses that deal with all sorts of data types (like datetime objects). However, It’s important to understand how FastAPI handles responses ...
🌐
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
🌐
GitHub
github.com › fastapi › fastapi › issues › 3183
How to maintain the datetime format same as input · Issue #3183 · fastapi/fastapi
May 6, 2021 - fastapi / fastapi Public · There was an error while loading. Please reload this page. Notifications · You must be signed in to change notification settings · Fork 8.5k · Star 94.2k · New issueCopy link · New issueCopy link · Closed · Closed · How to maintain the datetime format same as input#3183 ·
Author   yantrikskaran
🌐
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
🌐
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...
🌐
GitHub
github.com › fastapi › fastapi › issues › 5036
FastAPI doesn't format datetime.datetime output to ISO 8601 as documented · Issue #5036 · fastapi/fastapi
June 14, 2022 - FastAPI doesn't format datetime.datetime output to ISO 8601 as documented#5036 · Copy link · Labels · questionQuestion or problemQuestion or problemquestion-migrate · ferulisses · opened · on Jun 14, 2022 · Issue body actions · I added ...
Author   ferulisses
🌐
GitHub
github.com › fastapi › fastapi › blob › master › tests › test_datetime_custom_encoder.py
fastapi/tests/test_datetime_custom_encoder.py at master · fastapi/fastapi
FastAPI framework, high performance, easy to learn, fast to code, ready for production - fastapi/tests/test_datetime_custom_encoder.py at master · fastapi/fastapi
Author   fastapi
🌐
Toot
toot.community › @chakie › 110889961436568714
Jan Ekholm: "Weird. I use FastAPI to send some models as JSON.…" - toot.community
August 14, 2023 - Weird. I use FastAPI to send some models as JSON. Models use Python datetime fields for various date related stuff. For some reason the date fields are serialised differently. Some are sent like "2022-05-22T22:00:00" while others are sent like "2023-08-14T20:40:37.110Z".
🌐
Reddit
reddit.com › r/learnpython › custom type validation for fastapi
r/learnpython on Reddit: Custom type validation for FastApi
October 6, 2021 - def date_to_unix(date): format_string = "%d/%m/%Y %H:%M" try: unix_time = datetime.strptime(date, format_string).timestamp() return unix_time except (ValueError, TypeError): return False
🌐
Stac-utils
stac-utils.github.io › stac-fastapi › api › stac_fastapi › types › rfc3339
rfc3339 - stac-fastapi
A datetime object parsed from the date_str. ... If the date_str is empty or contains the placeholder '..'. Source code in stac_fastapi/types/stac_fastapi/types/rfc3339.py