PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Funzione PostgreSQL age():risultati diversi/imprevisti quando si atterra in un mese diverso

age è calcolato dal timestamptz_age funzione in src/backend/utils/adt/timestamp.c . Il commento dice:

/* 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.
 */

Il codice prima converte gli argomenti in struct pg_tm variabili tm1 e tm2 (struct pg_tm è simile a struct tm della libreria C , ma ha campi di fuso orario aggiuntivi) e quindi calcola la differenza tm per campo.

Nel caso di age('2018-07-01','2018-05-20') , i campi pertinenti di tale differenza sarebbero simili a questo:

tm_mday = -19
tm_mon  =   2
tm_year =   0

Ora i campi negativi vengono modificati. per tm_mday , il codice è simile a questo:

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--;
    }
}

Dal momento che dt1 > dt2 , il else viene preso il ramo e il codice aggiunge il numero di giorni di maggio (31) e riduce il mese di 1, finendo con

tm_mday = 12
tm_mon  =  1
tm_year =  0

Questo è il risultato che ottieni.

Ora a prima vista sembra che tm2->tm_mon non è il mese giusto per scegliere, e sarebbe stato meglio prendere il mese precedente dell'argomento sinistro:

day_tab[isleap(tm1->tm_year)][(tm1->tm_mon + 10) % 12]

Ma non posso dire se quella scelta sarebbe sempre migliore, e comunque il commento indennizza la funzione, quindi esiterei a chiamarlo bug.

Potresti voler occuparti della mailing list degli hacker.