age
beräknas av timestamptz_age
funktion i src/backend/utils/adt/timestamp.c
. Kommentaren säger:
/* 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 konverterar först argumenten till struct pg_tm
variabler tm1
och tm2
(struct pg_tm
liknar C-bibliotekets struct tm
, men har ytterligare tidszonsfält) och beräknar sedan skillnaden tm
per fält.
I fallet med age('2018-07-01','2018-05-20')
, de relevanta fälten för den skillnaden skulle se ut så här:
tm_mday = -19
tm_mon = 2
tm_year = 0
Nu är negativa fält justerade. för tm_mday
, koden ser ut så här:
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--;
}
}
Sedan dt1 > dt2
, den else
gren tas, och koden lägger till antalet dagar i maj (31) och minskar månaden med 1, vilket slutar med
tm_mday = 12
tm_mon = 1
tm_year = 0
Det är resultatet du får.
Nu vid första anblicken verkar det som tm2->tm_mon
är inte rätt månad att välja, och det hade varit bättre att ta föregående månad av vänsterargumentet:
day_tab[isleap(tm1->tm_year)][(tm1->tm_mon + 10) % 12]
Men jag kan inte säga om det valet skulle vara bättre i alla fall, och i vilket fall som helst frigör kommentaren funktionen, så jag skulle tveka att kalla det en bugg.
Du kanske vill ta upp det med hackarnas e-postlista.