PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Função age() do PostgreSQL:resultados diferentes/inesperados ao aterrissar em um mês diferente


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.