Pada hari Minggu, saya diam seperti biasa, browsing melalui Reddit. Menggulir melalui kesenangan anak anjing dan humor buruk programmer, satu posting tertentu menarik perhatian saya. Itu adalah
bug di calc.exe .
Penghitungan rentang tanggal salah dalam Windows Calculator"Yah, itu kedengarannya seperti kesalahan yang aneh, aku ingin tahu apa yang menyebabkannya," aku berpikir dalam hati. Jumlah minggu, tentu saja, membuat bug terlihat seperti semacam kesalahan overflow atau range, Anda tahu, alasan khas. Tapi itu selalu bisa sedikit dibalik oleh sinar berenergi tinggi dari tetangga kosmik yang ramah.
Karena tertarik pada alasannya, saya melakukan apa yang Anda lakukan dalam kasus-kasus seperti itu: Saya mencobanya di komputer saya untuk memposting "Semuanya bekerja untuk saya." Dan pengulangan situasi dari pos "31 Juli - 31 Desember" di komputer saya memberikan hasil yang benar dari "5 bulan". Tetapi setelah menguji sedikit, saya menemukan bahwa "31 Juli - 30 Desember" sebenarnya menyebabkan kesalahan. Nilai yang salah "5 bulan, 613566756 minggu, 3 hari" ditampilkan.
Saya belum selesai mengguncang program, dan kemudian saya ingat: "Oh, bukankah kalkulator adalah salah satu dari hal-hal yang Microsoft telah membuka sumbernya?"
Dan sungguh . Kesalahan ini tidak bisa terlalu rumit, jadi saya pikir saya akan berusaha menemukannya. Mengunduh sumbernya cukup sederhana, dan menambahkan beban kerja UWP yang diperlukan ke Visual Studio juga berjalan tanpa hambatan.
Menavigasi basis kode yang tidak Anda kenal adalah sesuatu yang terbiasa dengan waktu. Terutama ketika Anda ingin berkontribusi pada proyek open source di mana Anda menemukan bug. Namun, ketidaktahuan XAML atau WinRT, tentu saja, tidak membuat segalanya menjadi lebih mudah.
Saya membuka file solusi dan melihat ke dalam proyek "Kalkulator" untuk mencari file yang terkait dengan bug. Saya menemukan
DateCalculator.xaml
, maka sepertinya cocok untuk nama
DateDiff_FromDate to DateCalculatorViewModel.cpp
dan, akhirnya,
DateCalculator.cpp
.
Setelah menetapkan breakpoint dan melihat beberapa variabel, saya melihat bahwa nilai
DateDifference
akhir sudah salah. Artinya, itu bukan hanya kesalahan konversi menjadi string, tetapi kesalahan perhitungan yang sebenarnya.
Perhitungan aktual dalam pseudo-code sederhana terlihat seperti ini:
DateDifference calculate_difference(start_date, end_date) { uint[] diff_types = [year, month, week, day] uint[] typical_days_in_type = [365, 31, 7, 1] uint[] calculated_difference = [0, 0, 0, 0] date temp_pivot_date date pivot_date = start_date uint days_diff = calculate_days_difference(start_date, end_date) for(type in differenceTypes) { temp_pivot_date = pivot_date uint current_guess = days_diff /typicalDaysInType[type] if(current_guess !=0) pivot_date = advance_date_by(pivot_date, type, current_guess) int diff_remaining bool best_guess_hit = false do{ diff_remaining = calculate_days_difference(pivot_date, end_date) if(diff_remaining < 0) {
Itu terlihat baik. Tidak ada masalah dalam logika. Pada dasarnya, fungsi melakukan hal berikut:
- dihitung tahun penuh sejak tanggal mulai
- dari tanggal tahun penuh terakhir dihitung bulan
- dihitung minggu dari tanggal bulan penuh terakhir
- dari tanggal minggu penuh terakhir menghitung hari yang tersisa
Sebenarnya, masalahnya adalah asumsi bahwa mulai berurutan
date = advance_date_by(date, month, somenumber) date = advance_date_by(date, month, 1)
sama dengan
date = advance_date_by(date, month, somenumber + 1)
Ini biasanya hal yang sama. Tetapi muncul pertanyaan:
"Jika Anda mencapai hari ke-31 bulan itu, bulan berikutnya 30 hari, Anda menambahkan satu bulan, lalu ke mana Anda akan pergi?"Sepertinya untuk
Windows.Globalization.Calendar.AddMonths (Int32) jawabannya adalah "pada tanggal 30".
Dan ini berarti:
โ31 Juli + 4 bulan = 30 Novemberโ
"30 November + 1 bulan = 30 Desember"
โ31 Juli + 5 bulan = 31 Desemberโ
Dengan demikian, operasi AddMonths tidak
distributif (dengan AddMonth-multiplikasi), atau
komutatif , atau
asosiatif . Apa yang sebenarnya harus menjadi operasi "penambahan". Bukankah menyenangkan bekerja dengan waktu dan kalender?
Mengapa dalam kasus ini, kesalahan pengaturan rentang menyebabkan sejumlah besar minggu? Seperti yang mungkin sudah Anda duga, ini disebabkan oleh fakta bahwa
days_diff
adalah tipe yang tidak ditandatangani. Ini mengubah -1 hari menjadi jumlah yang sangat besar, yang kemudian diteruskan ke iterasi siklus berikutnya dengan minggu. Yang kemudian mencoba untuk memperbaiki situasi dengan mengurangi
current_guess
tetapi tidak mengurangi variabel yang tidak ditandatangani.
Ya, itu cara yang menarik untuk menghabiskan hari Minggu. Saya membuat
permintaan tarik di Github dengan "perbaikan" minimal. Saya meletakkan "koreksi" dalam tanda kutip, karena sekarang perhitungannya terlihat seperti ini:

Saya pikir secara teknis ini adalah hasil yang benar, jika kita asumsikan bahwa "31 Juli + 4 bulan = 30 November." Meskipun opsi ini tidak sepenuhnya konsisten dengan intuisi manusia tentang perbedaan tanggal. Tetapi bagaimanapun juga, ini tidak salah seperti sebelumnya.