Search code examples
pythondatetimeformatisopydantic

Parsing different time formats with pydantic json_decoders not working as expected


Can somebody please explain me the behaviour of the following pydantic model.

from datetime import datetime
from pydantic import BaseModel

first_format = {'time': '2018-01-05T16:59:33+00:00',}
second_format = {'time': '2021-03-05T08:21:00.000Z',}

class TimeModel(BaseModel):
    time: datetime

    class Config:
        json_encoders = {
            datetime: lambda v: v.isoformat(),
        }
        json_decoders = {
            datetime: lambda v: datetime.fromisoformat(v),
        }

print(TimeModel.parse_obj(first_format))
print("first_format successfull")
print(TimeModel.parse_obj(second_format))
print("second_format successfull")

Output:

time=datetime.datetime(2018, 1, 5, 16, 59, 33, tzinfo=datetime.timezone.utc)
first_format successfull
time=datetime.datetime(2021, 3, 5, 8, 21, tzinfo=datetime.timezone.utc)
second_format successfull

Expected behaviour:

Works on first_format as it does now, but fails at second_format as datetime.fromisoformat(second_format) throws an error for the wrong format

What I want to do:

Parse the time only if it has the format of first_format, else raise an exception.

Thanks for your help

Edit: I later realized that there is no such thing as json_decoders, so please do not get confused by this

Problem resolved


Solution

  • You need to use pre validator here, in order to parse and validate incoming datetime string:

    from datetime import datetime
    from pydantic import BaseModel, validator
    
    first_format = {'time': '2018-01-05T16:59:33+00:00',}
    second_format = {'time': '2021-03-05T08:21:00.000Z',}
    
    
    class TimeModel(BaseModel):
        time: datetime
    
        class Config:
            json_encoders = {
                datetime: lambda v: v.isoformat(),
            }
    
        @validator('time', pre=True)
        def time_validate(cls, v):
            return datetime.fromisoformat(v)
    
    
    print(TimeModel.parse_obj(first_format).json())
    print("first_format successfull")
    print(TimeModel.parse_obj(second_format))
    print("second_format successfull")
    

    Update:

    If you are using pydantic v2 you need to use the field_validator instead since the validator was deprecated. The keyword argument is now mode='before'.