age
dihitung dengan timestamptz_age
fungsi di src/backend/utils/adt/timestamp.c
. Komentarnya berbunyi:
/* 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.
*/
Kode pertama-tama mengubah argumen menjadi struct pg_tm
variabel tm1
dan tm2
(struct pg_tm
mirip dengan struct tm
perpustakaan C , tetapi memiliki bidang zona waktu tambahan) dan kemudian menghitung perbedaan tm
per bidang.
Dalam kasus age('2018-07-01','2018-05-20')
, bidang yang relevan dari perbedaan itu akan terlihat seperti ini:
tm_mday = -19
tm_mon = 2
tm_year = 0
Sekarang bidang negatif disesuaikan. untuk tm_mday
, kodenya terlihat seperti ini:
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--;
}
}
Sejak dt1 > dt2
, else
cabang diambil, dan kode menambahkan jumlah hari di bulan Mei (31) dan mengurangi bulan sebanyak 1, berakhir dengan
tm_mday = 12
tm_mon = 1
tm_year = 0
Itulah hasil yang Anda dapatkan.
Sekarang pada pandangan pertama tampaknya tm2->tm_mon
bukan bulan yang tepat untuk dipilih, dan akan lebih baik untuk mengambil bulan sebelumnya dari argumen kiri:
day_tab[isleap(tm1->tm_year)][(tm1->tm_mon + 10) % 12]
Tetapi saya tidak dapat mengatakan apakah pilihan itu akan lebih baik dalam semua kasus, dan bagaimanapun juga, komentar tersebut mengganti kerugian fungsi tersebut, jadi saya ragu untuk menyebutnya sebagai bug.
Anda mungkin ingin membawanya ke milis peretas.