How to parse unix timestamp into datetime without timezone in Fast API
Asked Answered
C

4

6

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?

Canopus answered 18/9, 2020 at 10:58 Comment(1)
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.Canopus
D
8

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)
Disputation answered 18/9, 2020 at 12:10 Comment(0)
L
6

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.

Landscape answered 16/1, 2022 at 2:36 Comment(0)
C
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)
Canopus answered 22/9, 2020 at 11:1 Comment(0)
E
0

With the new Pydantic version 2, we can expand on @Benedikt's answer like this:

from datetime import datetime
from pydantic import BaseModel, field_validator

class ModelWithDate(BaseModel):
    dt: datetime = None

    @field_validator('dt')
    @classmethod
    def remove_timezone(cls, dt) -> datetime:
        return dt.replace(tzinfo=None)

Pydantic will first parse it into a regular datetime object before passing it to your validator, so you don't have to worry about handling various types like timestamps or strings, you can simply drop the tzinfo from the object.

Errata answered 19/1 at 6:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.