]> git.ipfire.org Git - thirdparty/psycopg.git/commit
fix: make sure that intervals with days and monts have the same PostgreSQL epoch fix-interval-epoch 1075/head
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Wed, 7 May 2025 15:33:10 +0000 (17:33 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Wed, 7 May 2025 15:57:35 +0000 (17:57 +0200)
commit7657f3ed2c742ba40de1bff2a49157a78e7ecbd1
tree1e5a63b407f1a3c1dfcfbc740f43434a770ac0d9
parenta6d367a3e1cf67be901d44e37752fcf99784df02
fix: make sure that intervals with days and monts have the same PostgreSQL epoch

While fixing #1071 I noticed that Postgres returns an approximation of
leap years in intervals with specified months and years. More precisely:

- Postgres internal representation keeps separate (micros, days, months)
  (as can be seen in the binary adapter).

- Years are converted to months. When pressed for an equivalence with
  days, this shows with:

    =# select '1 year'::interval = '360 days'::interval;
     ?column?
    ----------
     t

- When converting the interval to seconds, Postgres adds 1/4 of a day
  every 12 months (rounding to an integer number of years towards 0).

    =# select extract('epoch' from '23 months'::interval) / 60. / 60 / 24 - (365 + 11 * 30);
          ?column?
    --------------------
     0.2500000000000000
    (1 row)

    =# select extract('epoch' from '24 months'::interval) / 60. / 60 / 24 - (2 * 365);
          ?column?
    --------------------
     0.5000000000000000
    (1 row)

This MR implements a conversion from Postgres interval to Python
following the same rule. As a consequence, the `extract('epoch' from
interval)` function now returns the same number of seconds returned by
the `datetime.timedelta.total_seconds()` of the value returned. The
difference though is that the hours shows in the seconds:

    >>> conn.execute("select '1 year'::interval").fetchone()[0]
    datetime.timedelta(days=365, seconds=21600)

    >>> conn.execute("select '4 year'::interval").fetchone()[0].days, 365 * 4
    (1461, 1460)

This changeset only changes the Python implementation, not the C one.
docs/news.rst
psycopg/psycopg/types/datetime.py
tests/types/test_datetime.py