age
се изчислява чрез timestamptz_age
функция в src/backend/utils/adt/timestamp.c
. Коментарът гласи:
/* 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.
*/
Кодът първо преобразува аргументите в struct pg_tm
променливи tm1
и tm2
(struct pg_tm
е подобен на struct tm
на C библиотеката , но има допълнителни полета за часова зона) и след това изчислява разликата tm
на поле.
В случай на age('2018-07-01','2018-05-20')
, съответните полета на тази разлика ще изглеждат така:
tm_mday = -19
tm_mon = 2
tm_year = 0
Сега отрицателните полета се коригират. за tm_mday
, кодът изглежда така:
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--;
}
}
Тъй като dt1 > dt2
, else
клонът се взема и кодът добавя броя на дните през май (31) и намалява месеца с 1, завършвайки с
tm_mday = 12
tm_mon = 1
tm_year = 0
Това е резултатът, който получавате.
Сега на пръв поглед изглежда, че tm2->tm_mon
не е правилният месец за избор и би било по-добре да вземете предходния месец от левия аргумент:
day_tab[isleap(tm1->tm_year)][(tm1->tm_mon + 10) % 12]
Но не мога да кажа дали този избор би бил по-добър във всички случаи и във всеки случай коментарът обезщетява функцията, така че бих се поколебал да го нарека грешка.
Може да искате да го включите в пощенския списък на хакерите.