age
wordt berekend door de timestamptz_age
functie in src/backend/utils/adt/timestamp.c
. De opmerking zegt:
/* 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.
*/
De code converteert eerst de argumenten naar struct pg_tm
variabelen tm1
en tm2
(struct pg_tm
is vergelijkbaar met struct tm
. van de C-bibliotheek , maar heeft extra tijdzonevelden) en berekent vervolgens het verschil tm
per veld.
In het geval van age('2018-07-01','2018-05-20')
, zouden de relevante velden van dat verschil er als volgt uitzien:
tm_mday = -19
tm_mon = 2
tm_year = 0
Nu worden negatieve velden aangepast. voor tm_mday
, ziet de code er als volgt uit:
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--;
}
}
Sinds dt1 > dt2
, de else
tak wordt genomen, en de code telt het aantal dagen in mei (31) op en vermindert de maand met 1, eindigend met
tm_mday = 12
tm_mon = 1
tm_year = 0
Dat is het resultaat dat je krijgt.
Op het eerste gezicht lijkt het erop dat tm2->tm_mon
is niet de juiste maand om te kiezen, en het zou beter zijn geweest om de vorige maand van het linkerargument te nemen:
day_tab[isleap(tm1->tm_year)][(tm1->tm_mon + 10) % 12]
Maar ik kan niet zeggen of die keuze in alle gevallen beter zou zijn, en in ieder geval vrijwaart de opmerking de functie, dus ik zou aarzelen om het een bug te noemen.
Misschien wil je het opnemen met de hackers-mailinglijst.