PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Fonction PostgreSQL age() :résultats différents/inattendus lors de l'atterrissage au cours d'un mois différent

age est calculé par le timestamptz_age fonction dans src/backend/utils/adt/timestamp.c . Le commentaire dit :

/* 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.
 */

Le code convertit d'abord les arguments en struct pg_tm variable tm1 et tm2 (struct pg_tm est similaire au struct tm de la bibliothèque C , mais a des champs de fuseau horaire supplémentaires) et calcule ensuite la différence tm par champ.

Dans le cas de age('2018-07-01','2018-05-20') , les champs pertinents de cette différence ressembleraient à ceci :

tm_mday = -19
tm_mon  =   2
tm_year =   0

Maintenant, les champs négatifs sont ajustés. pour tm_mday , le code ressemble à ceci :

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--;
    }
}

Depuis dt1 > dt2 , le else branche est prise, et le code ajoute le nombre de jours en mai (31) et réduit le mois de 1, se terminant par

tm_mday = 12
tm_mon  =  1
tm_year =  0

C'est le résultat que vous obtenez.

Maintenant, à première vue, il semble que tm2->tm_mon n'est pas le bon mois à choisir, et il aurait été préférable de prendre le mois précédent de l'argument de gauche :

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

Mais je ne peux pas dire si ce choix serait meilleur dans tous les cas, et de toute façon le commentaire indemnise la fonction, donc j'hésiterais à le qualifier de bug.

Vous voudrez peut-être en parler avec la liste de diffusion des pirates.