age
beregnes af timestamptz_age
funktion i src/backend/utils/adt/timestamp.c
. Kommentaren siger:
/* 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.
*/
Koden konverterer først argumenterne til struct pg_tm
variabler tm1
og tm2
(struct pg_tm
ligner C-bibliotekets struct tm
, men har yderligere tidszonefelter) og beregner derefter forskellen tm
pr. felt.
I tilfælde af age('2018-07-01','2018-05-20')
, vil de relevante felter for denne forskel se sådan ud:
tm_mday = -19
tm_mon = 2
tm_year = 0
Nu er negative felter justeret. for tm_mday
, koden ser sådan ud:
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--;
}
}
Siden dt1 > dt2
, den else
gren tages, og koden tilføjer antallet af dage i maj (31) og reducerer måneden med 1, og ender med
tm_mday = 12
tm_mon = 1
tm_year = 0
Det er resultatet, du får.
Nu ved første øjekast ser det ud til, at tm2->tm_mon
er ikke den rigtige måned at vælge, og det ville have været bedre at tage den foregående måned med venstre argument:
day_tab[isleap(tm1->tm_year)][(tm1->tm_mon + 10) % 12]
Men jeg kan ikke sige, om det valg ville være bedre i alle tilfælde, og under alle omstændigheder skadesløser kommentaren funktionen, så jeg vil tøve med at kalde det en fejl.
Du vil måske tage det op med hackernes mailingliste.