age
é calculado pelo timestamptz_age
função em src/backend/utils/adt/timestamp.c
. O comentário diz:/* 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.
*/
O código primeiro converte os argumentos para
struct pg_tm
variáveis tm1
e tm2
(struct pg_tm
é semelhante ao struct tm
da biblioteca C , mas tem campos de fuso horário adicionais) e calcula a diferença tm
por campo. No caso de
age('2018-07-01','2018-05-20')
, os campos relevantes dessa diferença ficariam assim:tm_mday = -19
tm_mon = 2
tm_year = 0
Agora os campos negativos são ajustados. para
tm_mday
, o código fica assim: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--;
}
}
Desde
dt1 > dt2
, o else
ramo é tomado, e o código adiciona o número de dias em maio (31) e reduz o mês em 1, terminando com tm_mday = 12
tm_mon = 1
tm_year = 0
Esse é o resultado que você obtém.
Agora, à primeira vista, parece que
tm2->tm_mon
não é o mês certo para escolher, e teria sido melhor pegar o mês anterior do argumento da esquerda:day_tab[isleap(tm1->tm_year)][(tm1->tm_mon + 10) % 12]
Mas não posso dizer se essa escolha seria melhor em todos os casos e, de qualquer forma, o comentário indeniza a função, então hesito em chamá-lo de bug.
Você pode querer falar com a lista de discussão de hackers.