TIME
nilai selalu disimpan pada 3 byte di MySQL. Tapi formatnya berubah pada versi 5.6 .4
. Saya menduga ini bukan pertama kalinya ketika itu berubah. Tetapi perubahan lain, jika ada, terjadi sejak lama dan tidak ada bukti publik tentangnya. Riwayat kode sumber MySQL di GitHub dimulai dengan versi 5.5 (komit tertua adalah dari Mei 2008) tetapi perubahan yang saya cari terjadi di suatu tempat sekitar tahun 2001-2002 (MySQL 4 diluncurkan pada tahun 2003)
Format saat ini, seperti yang dijelaskan dalam dokumentasi, menggunakan 6 bit untuk detik (nilai yang mungkin:0
ke 63
), 6 bit untuk menit, 10 bit untuk jam (nilai yang mungkin:0
ke 1023
), 1 bit untuk tanda (tambahkan nilai negatif dari interval yang telah disebutkan) dan 1 bit tidak digunakan dan diberi label "dicadangkan untuk ekstensi mendatang".
Ini dioptimalkan untuk bekerja dengan komponen waktu (jam, menit, detik) dan tidak membuang banyak ruang. Dengan menggunakan format ini dimungkinkan untuk menyimpan nilai antara -1023:59:59
dan +1023:59:59
. Namun MySQL membatasi jumlah jam menjadi 838
, mungkin untuk kompatibilitas mundur dengan aplikasi yang ditulis beberapa waktu lalu, ketika saya pikir ini adalah batasnya.
Sampai versi 5.6.4, TIME
nilai juga disimpan dalam 3 byte dan komponen dikemas sebagai days * 24 * 3600 + hours * 3600 + minutes * 60 + seconds
. Format ini dioptimalkan untuk bekerja dengan stempel waktu (karena, pada kenyataannya, adalah stempel waktu). Dengan menggunakan format ini, dimungkinkan untuk menyimpan nilai dalam kisaran sekitar -2330
ke +2330
jam. Meskipun memiliki rentang nilai yang besar ini, MySQL masih membatasi nilainya menjadi -838
ke +838
jam.
Ada bug #11655
di MySQL 4. Dimungkinkan untuk mengembalikan TIME
nilai di luar -838..+838
rentang menggunakan SELECT
. bersarang pernyataan. Itu bukan fitur tetapi bug dan telah diperbaiki.
Satu-satunya alasan untuk membatasi nilai pada rentang ini dan untuk secara aktif mengubah setiap bagian kode yang menghasilkan TIME
nilai di luarnya adalah kompatibilitas ke belakang.
Saya menduga MySQL 3 menggunakan format berbeda yang, karena cara data dikemas, membatasi nilai valid ke kisaran -838..+838
jam.
Dengan melihat kode sumber MySQL Saya menemukan rumus yang menarik ini:
#define TIME_MAX_VALUE (TIME_MAX_HOUR*10000 + TIME_MAX_MINUTE*100 + TIME_MAX_SECOND)
Mari kita abaikan sejenak MAX
bagian dari nama yang digunakan di atas dan mari kita ingat hanya TIME_MAX_MINUTE
dan TIME_MAX_SECOND
adalah angka di antara 00
dan 59
. Rumus hanya menggabungkan jam, menit dan detik dalam satu bilangan bulat. Misalnya, nilai 170:29:45
menjadi 1702945
.
Rumus ini menimbulkan pertanyaan berikut:mengingat bahwa TIME
nilai disimpan dalam 3 byte dengan tanda, berapa nilai positif maksimum yang dapat direpresentasikan dengan cara ini?
Nilai yang kita cari adalah 0x7FFFFF
bahwa dalam notasi desimal adalah 8388607
. Sejak empat digit terakhir (8607
) harus dibaca sebagai menit (86
) dan detik (07
) dan nilai valid maksimumnya adalah 59
, nilai terbesar yang dapat disimpan pada 3 byte dengan tanda menggunakan rumus di atas adalah 8385959
. Yang mana, sebagai TIME
adalah +838:59:59
. Ta-da!
Tebak apa? Fragmen C
kode yang tercantum di atas diambil dari ini:
/* Limits for the TIME data type */
#define TIME_MAX_HOUR 838
#define TIME_MAX_MINUTE 59
#define TIME_MAX_SECOND 59
#define TIME_MAX_VALUE (TIME_MAX_HOUR*10000 + TIME_MAX_MINUTE*100 + TIME_MAX_SECOND)
Saya yakin ini adalah bagaimana MySQL 3 digunakan untuk menjaga TIME
nilai secara internal. Format ini memberlakukan batasan jangkauan, dan persyaratan kompatibilitas mundur pada versi berikutnya menyebarkan batasan tersebut hingga saat ini.