summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2019-11-13 10:49:01 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2019-11-13 11:47:44 -0500
commit937949fb0cf6def1fe45c2050c17f3ba15928a96 (patch)
tree902acc92467472876f07ff6c44067cbfb3e8d974
parent6e7a5058e32b3bd2566ad13e5ef6246ea6e66bbb (diff)
downloadsqlalchemy-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.rst36
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