diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-11-13 10:49:01 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-11-13 11:47:44 -0500 |
commit | 937949fb0cf6def1fe45c2050c17f3ba15928a96 (patch) | |
tree | 902acc92467472876f07ff6c44067cbfb3e8d974 | |
parent | 6e7a5058e32b3bd2566ad13e5ef6246ea6e66bbb (diff) | |
download | sqlalchemy-937949fb0cf6def1fe45c2050c17f3ba15928a96.tar.gz |
Add TypeDecorator recipe for timezone aware/UTC conversion
Change-Id: I59e6c76a4a53ce3782bcfc4aecdeb1b4fdd7b941
References: https://github.com/sqlalchemy/sqlalchemy/issues/4980
(cherry picked from commit e345864506346700dc4c21ff21bfc18f2c047831)
-rw-r--r-- | doc/build/core/custom_types.rst | 36 |
1 files changed, 36 insertions, 0 deletions
diff --git a/doc/build/core/custom_types.rst b/doc/build/core/custom_types.rst index 7ac23ebcb..1120f04bf 100644 --- a/doc/build/core/custom_types.rst +++ b/doc/build/core/custom_types.rst @@ -127,6 +127,42 @@ many decimal places. Here's a recipe that rounds them down:: value = value.quantize(self.quantize) return value +Store Timezone Aware Timestamps as Timezone Naive UTC +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Timestamps in databases should always be stored in a timezone-agnostic way. For +most databases, this means ensuring a timestamp is first in the UTC timezone +before it is stored, then storing it as timezone-naive (that is, without any +timezone associated with it; UTC is assumed to be the "implicit" timezone). +Alternatively, database-specific types like PostgreSQLs "TIMESTAMP WITH +TIMEZONE" are often preferred for their richer functionality; however, storing +as plain UTC will work on all databases and drivers. When a +timezone-intelligent database type is not an option or is not preferred, the +:class:`.TypeDecorator` can be used to create a datatype that convert timezone +aware timestamps into timezone naive and back again. Below, Python's +built-in ``datetime.timezone.utc`` timezone is used to normalize and +denormalize:: + + import datetime + + class TZDateTime(TypeDecorator): + impl = DateTime + + def process_bind_param(self, value, dialect): + if value is not None: + if not value.tzinfo: + raise TypeError("tzinfo is required") + value = value.astimezone(datetime.timezone.utc).replace( + tzinfo=None + ) + return value + + def process_result_value(self, value, dialect): + if value is not None: + value = value.replace(tzinfo=datetime.timezone.utc) + return value + + .. _custom_guid_type: Backend-agnostic GUID Type |