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.