date and time doc page
PYTHON-394
This commit is contained in:
87
docs/dates_and_times.rst
Normal file
87
docs/dates_and_times.rst
Normal file
@@ -0,0 +1,87 @@
|
||||
Working with Dates and Times
|
||||
============================
|
||||
|
||||
This document is meant to provide on overview of the assumptions and limitations of the driver time handling, the
|
||||
reasoning behind it, and describe approaches to working with these types.
|
||||
|
||||
timestamps (Cassandra DateType)
|
||||
-------------------------------
|
||||
|
||||
Timestamps in Cassandra are timezone-naive timestamps encoded as microseconds since UNIX epoch. Clients working with
|
||||
timestamps in this database usually find it easiest to reason about them if they are always assumed to be UTC. To quote the
|
||||
pytz documentation, "The preferred way of dealing with times is to always work in UTC, converting to localtime only when
|
||||
generating output to be read by humans." The driver adheres to this tenant, and assumes UTC is always in the database. The
|
||||
driver attempts to make this correct on the way in, and assumes no timezone on the way out.
|
||||
|
||||
Write Path
|
||||
~~~~~~~~~~
|
||||
When inserting timestamps, the driver handles serialization for the write path as follows:
|
||||
|
||||
If the input is a ``datetime.datetime``, the serialization is normalized by starting with the ``utctimetuple()`` of the
|
||||
value.
|
||||
|
||||
- If the ``datetime`` object is timezone-aware, the timestamp is shifted, and represents the UTC timestamp equivalent.
|
||||
- If the ``datetime`` object is timezone-naive, this results in no shift -- any ``datetime`` with no timezone information is assumed to be UTC
|
||||
|
||||
Note the second point above applies even to "local" times created using ``now()``::
|
||||
|
||||
>>> d = datetime.now()
|
||||
|
||||
>>> print(d.tzinfo)
|
||||
None
|
||||
|
||||
|
||||
These do not contain timezone information intrinsically, so they will be assumed to be UTC and not shifted. When generating
|
||||
timestamps in the application, it is clearer to use ``datetime.utcnow()`` to be explicit about it.
|
||||
|
||||
If the input for a timestamp is numeric, it is assumed to be a epoch-relative microsecond timestamp, as specified in the
|
||||
CQL spec -- no scaling or conversion is done.
|
||||
|
||||
Read Path
|
||||
~~~~~~~~~
|
||||
The driver always assumes persisted timestamps are UTC and makes no attempt to localize them. Returned values are
|
||||
timezone-naive ``datetime.datetime``. We follow this approach because the datetime API has deficiencies around daylight
|
||||
saving time, and the defacto package for handling this is a third-party package (we try to minimize external dependencies
|
||||
and not make decisions for the integrator).
|
||||
|
||||
The decision for how to handle timezones is left to the application. For the most part it is straightforward to apply
|
||||
localization to the ``datetime``\s returned by queries. One prevalent method is to use pytz for localization::
|
||||
|
||||
import pytz
|
||||
user_tz = pytz.timezone('US/Central')
|
||||
timestamp_naive = row.ts
|
||||
timestamp_utc = pytz.utc.localize(timestamp_naive)
|
||||
timestamp_presented = timestamp_utc.astimezone(user_tz)
|
||||
|
||||
This is the most robust approach (likely refactored into a function). If it is deemed too cumbersome to apply for all call
|
||||
sites in the application, it is possible to patch the driver with custom deserialization for this type. However, doing
|
||||
this depends depends some on internal APIs and what extensions are present, so we will only mention the possibility, and
|
||||
not spell it out here.
|
||||
|
||||
date, time (Cassandra DateType)
|
||||
-------------------------------
|
||||
Date and time in Cassandra are idealized markers, much like ``datetime.date`` and ``datetime.time`` in the Python standard
|
||||
library. Unlike these Python implementations, the Cassandra encoding supports much wider ranges. To accommodate these
|
||||
ranges without overflow, this driver returns these data in custom types: :class:`.util.Date` and :class:`.util.Time`.
|
||||
|
||||
Write Path
|
||||
~~~~~~~~~~
|
||||
For simple (not prepared) statements, the input values for each of these can be either a string literal or an encoded
|
||||
integer. See `Working with dates <https://github.com/apache/cassandra/blob/trunk/doc/cql3/CQL.textile#working-with-dates>`_
|
||||
or `Working with time <https://github.com/apache/cassandra/blob/trunk/doc/cql3/CQL.textile#working-with-time>`_ for details
|
||||
on the encoding or string formats.
|
||||
|
||||
For prepared statements, the driver accepts anything that can be used to construct the :class:`.util.Date` or
|
||||
:class:`.util.Time` classes. See the linked API docs for details.
|
||||
|
||||
Read Path
|
||||
~~~~~~~~~
|
||||
The driver always returns custom types for ``date`` and ``time``.
|
||||
|
||||
The driver returns :class:`.util.Date` for ``date`` in order to accommodate the wider range of values without overflow.
|
||||
For applications working within the supported range of [``datetime.MINYEAR``, ``datetime.MAXYEAR``], these are easily
|
||||
converted to standard ``datetime.date`` insances using :meth:`.Date.date`.
|
||||
|
||||
The driver returns :class:`.util.Time` for ``time`` in order to retain nanosecond precision stored in the database.
|
||||
For applications not concerned with this level of precision, these are easily converted to standard ``datetime.time``
|
||||
insances using :meth:`.Time.time`.
|
@@ -42,6 +42,9 @@ Contents
|
||||
:doc:`security`
|
||||
An overview of the security features of the driver.
|
||||
|
||||
:doc:`dates_and_times`
|
||||
Some discussion on the driver's approach to working with timestamp, date, time types
|
||||
|
||||
:doc:`faq`
|
||||
A collection of Frequently Asked Questions
|
||||
|
||||
@@ -58,6 +61,7 @@ Contents
|
||||
security
|
||||
user_defined_types
|
||||
object_mapper
|
||||
dates_and_times
|
||||
faq
|
||||
|
||||
Getting Help
|
||||
|
Reference in New Issue
Block a user