Skip to content

Commit

Permalink
Merge pull request #324 from lsst-sqre/tickets/DM-47262
Browse files Browse the repository at this point in the history
DM-47262: Minor documentation fixes
  • Loading branch information
rra authored Nov 12, 2024
2 parents d38c138 + 54ebaa1 commit 470e512
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 3 deletions.
9 changes: 8 additions & 1 deletion docs/user-guide/database/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@ Handling datetimes in database tables

When a database column is defined using the SQLAlchemy ORM using the `~sqlalchemy.types.DateTime` generic type, it cannot store a timezone.
The SQL standard type `~sqlalchemy.types.DATETIME` may include a timezone with some database backends, but it is database-specific.
It is therefore normally easier to store times in the database in UTC without timezone information.
For PostgreSQL, if the column is timezone-aware, PostgreSQL will convert the time to what it believes to be the local timezone rather than leaving it in UTC.

It is therefore less error-prone to set a strict standard that all times must be stored in the database in UTC without timezone information.
This ensures the database always returns times in UTC.

However, `~datetime.datetime` objects in regular Python code should always be timezone-aware and use the UTC timezone.
Timezone-naive datetime objects are often interpreted as being in the local timezone, whatever that happens to be.
Keeping all datetime objects as timezone-aware in the UTC timezone will minimize surprises from unexpected timezone conversions.

This unfortunately means that the code for storing and retrieving datetime objects from the database needs a conversion layer.

Converting datetimes
====================

asyncpg_ wisely declines to convert datetime objects.
It therefore returns timezone-naive objects from the database, and raises an exception if a timezone-aware datetime object is stored in a `~sqlalchemy.types.DateTime` field.
The conversion must therefore be done in the code making SQLAlchemy calls.
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/database/schema.rst
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ You're now ready to create the database migration.
op.execute("ALTER TYPE tokentype ADD VALUE 'oidc' IF NOT EXISTS")
You may want to connect to the PostgreSQL database with the :command:`psql` command-line tool so that you can examine the schema to understand what the migration needs to do.
For example, you can see a description of a table with :samp:`\d {table}`, which will tell you the name of an enum type that you may need to modify.
For example, you can see a description of a table with :samp:`\\d {table}`, which will tell you the name of an enum type that you may need to modify.
To do this, run:

.. prompt:: bash
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/pydantic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Normalizing datetime fields
Pydantic supports several input formats for `~datetime.datetime` fields, but the resulting `~datetime.datetime` object may be timezone-naive.
Best practice for Python code is to only use timezone-aware `~datetime.datetime` objects in the UTC time zone.

Pydantic provides a utility function, `~safir.pydantic.normalize_datetime`, that can be used as a field validator for a `~datetime.datetime` model field.
Safir provides a utility function, `~safir.pydantic.normalize_datetime`, that can be used as a field validator for a `~datetime.datetime` model field.
It ensures that any input is converted to UTC and is always timezone-aware.

Here's an example of how to use it:
Expand Down

0 comments on commit 470e512

Please sign in to comment.