PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

PostgreSQL-Funktion age():unterschiedliche/unerwartete Ergebnisse bei Landung in einem anderen Monat

age wird durch das timestamptz_age berechnet Funktion in src/backend/utils/adt/timestamp.c . Der Kommentar sagt:

/* timestamptz_age()
 * Calculate time difference while retaining year/month fields.
 * Note that this does not result in an accurate absolute time span
 *  since year and month are out of context once the arithmetic
 *  is done.
 */

Der Code wandelt zuerst die Argumente in struct pg_tm um Variablen tm1 und tm2 (struct pg_tm ähnelt dem struct tm der C-Bibliothek , hat aber zusätzliche Zeitzonenfelder) und berechnet dann die Differenz tm pro Feld.

Im Fall von age('2018-07-01','2018-05-20') , würden die relevanten Felder dieses Unterschieds wie folgt aussehen:

tm_mday = -19
tm_mon  =   2
tm_year =   0

Jetzt werden negative Felder angepasst. für tm_mday , sieht der Code so aus:

while (tm->tm_mday < 0)
{
    if (dt1 < dt2)
    {
        tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
        tm->tm_mon--;
    }
    else
    {
        tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
        tm->tm_mon--;
    }
}

Seit dt1 > dt2 , das else Verzweigung wird genommen, und der Code addiert die Anzahl der Tage im Mai (31) und reduziert den Monat um 1 und endet mit

tm_mday = 12
tm_mon  =  1
tm_year =  0

Das ist das Ergebnis, das Sie erhalten.

Nun scheint es auf den ersten Blick, dass tm2->tm_mon ist nicht der richtige Monat zu wählen, und es wäre besser gewesen, den vorherigen Monat des linken Arguments zu nehmen:

day_tab[isleap(tm1->tm_year)][(tm1->tm_mon + 10) % 12]

Aber ich kann nicht sagen, ob diese Wahl in allen Fällen besser wäre, und in jedem Fall entschädigt der Kommentar die Funktion, also würde ich zögern, es einen Fehler zu nennen.

Vielleicht möchten Sie es mit der Hacker-Mailingliste aufnehmen.