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

PostgreSQL age() funktion:forskellige/uventede resultater ved landing i en anden måned

age beregnes af timestamptz_age funktion i src/backend/utils/adt/timestamp.c . Kommentaren siger:

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

Koden konverterer først argumenterne til struct pg_tm variabler tm1 og tm2 (struct pg_tm ligner C-bibliotekets struct tm , men har yderligere tidszonefelter) og beregner derefter forskellen tm pr. felt.

I tilfælde af age('2018-07-01','2018-05-20') , vil de relevante felter for denne forskel se sådan ud:

tm_mday = -19
tm_mon  =   2
tm_year =   0

Nu er negative felter justeret. for tm_mday , koden ser sådan ud:

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

Siden dt1 > dt2 , den else gren tages, og koden tilføjer antallet af dage i maj (31) og reducerer måneden med 1, og ender med

tm_mday = 12
tm_mon  =  1
tm_year =  0

Det er resultatet, du får.

Nu ved første øjekast ser det ud til, at tm2->tm_mon er ikke den rigtige måned at vælge, og det ville have været bedre at tage den foregående måned med venstre argument:

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

Men jeg kan ikke sige, om det valg ville være bedre i alle tilfælde, og under alle omstændigheder skadesløser kommentaren funktionen, så jeg vil tøve med at kalde det en fejl.

Du vil måske tage det op med hackernes mailingliste.



  1. understreng i orakel

  2. SQL Server Teksttype vs. varchar datatype

  3. MySQL trigger efter indsættelse og efter opdatering

  4. Hvordan INSERT INTO Tabel fra dynamisk forespørgsel?