Skip to content

Instantly share code, notes, and snippets.

@afonasev
Created June 9, 2020 13:56
Show Gist options
  • Save afonasev/9e0a21a6437697a4604b6e22ab7682c3 to your computer and use it in GitHub Desktop.
Save afonasev/9e0a21a6437697a4604b6e22ab7682c3 to your computer and use it in GitHub Desktop.
UTCDateTime field for sqlalchemy
from datetime import timezone
import sqlalchemy as sa
class UTCDateTime(sa.TypeDecorator): # pylint:disable=W0223
"""By default if we provide datetime object without timezone Postgres applies
his timezone to that datetime. To prevent unexpected behaviour we add utc timezone
before every write. And convert back to utc, after every read.
From PostgreSQL docs:
For timestamp with time zone, the internally stored value is always in UTC.
An input value that has an explicit time zone specified is converted to UTC
using the appropriate offset for that time zone. If no time zone is stated
in the input string, then it is assumed to be in the time zone indicated
by the system's timezone parameter, and is converted to UTC using the offset
for the timezone zone.
When a timestamp with time zone value is output, it is always converted from UTC
to the current timezone zone, and displayed as local time in that zone.
"""
impl = sa.DateTime
def process_bind_param(self, value, dialect):
"""Way into the database.
Convert datetime to utc timezone, with tzinfo=timezone.utc, so
Postgres doesnt apply his own timezone
"""
if value is not None:
if not value.tzinfo:
value = value.replace(tzinfo=timezone.utc)
return value.astimezone(timezone.utc)
return None
def process_result_value(self, value, dialect):
"""Way out of the database.
Here we receive datetime with Postgres timezone and convert to UTC,
then replace timezone with None
"""
if value:
return value.astimezone(timezone.utc).replace(tzinfo=None)
return None
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment