7

Assume I have a pydantic model

class EventEditRequest(BaseModel):
    uid: UUID
    name: str
    start_dt: datetime
    end_dt: datetime

I send request with body b'{"uid":"a38a7543-20ca-4a50-ab4e-e6a3ae379d3c","name":"test event2222","start_dt":1600414328,"end_dt":1600450327}'

So both start_dt and end_dt are unix timestamps. But in endpoint they become datetimes with timezones.

@app.put('...')
def edit_event(event_data: EventEditRequest):
    event_data.start_dt.tzinfo is not None  # True

I don't want to manually edit start_dt and end_dt in the endpoint function to get rid of timezones. How can I set up my pydantic model so it will make datetime without timezones?

sashaaero
  • 2,618
  • 5
  • 23
  • 41
  • One solution I came to is to make middleware that will replace unix timestamp to datetime w/o timezone before it will become pydantic object. But it doesn't look the best solution to me. – sashaaero Sep 18 '20 at 11:05

3 Answers3

7

You can use own @validator to parse datetime manually:

from datetime import datetime

from pydantic import BaseModel, validator


class Model(BaseModel):
    dt: datetime = None


class ModelNaiveDt(BaseModel):
    dt: datetime = None

    @validator("dt", pre=True)
    def dt_validate(cls, dt):
        return datetime.fromtimestamp(dt)


print(Model(dt=1600414328))
print(ModelNaiveDt(dt=1600414328))

Output:

dt=datetime.datetime(2020, 9, 18, 7, 32, 8, tzinfo=datetime.timezone.utc)
dt=datetime.datetime(2020, 9, 18, 10, 32, 8)
alex_noname
  • 26,459
  • 5
  • 69
  • 86
5

Like sashaaero mentions in their answer, you can achieve this with a custom type. But instead of creating your own validation, you could just use the existing validator from Pydantic and then just remove the timezone info.

from pydantic.datetime_parse import parse_datetime


class OffsetNaiveDatetime(datetime):

    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, v):
        v = parse_datetime(v)
        v = v.replace(tzinfo=None)
        return v

But be careful to only use it where timezone information is not needed at all or doesn't apply.

3

One more way to achieve it is to create custom type

class UnixDatetime(datetime):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, v):
        if isinstance(v, datetime):
            print('Some request sends datetime not in UNIX format', file=sys.stderr)
            return v.replace(tzinfo=None)
        elif isinstance(v, int):
            return datetime.fromtimestamp(v)
        assert False, 'Datetime came of %s type' % type(v)
sashaaero
  • 2,618
  • 5
  • 23
  • 41