Menggunakan C ++ 20 chrono, cara menghitung berbagai fakta tentang kencan

19

https://www.timeanddate.com/date/weekday.html menghitung berbagai fakta tentang hari dalam setahun, misalnya:

https://i.stack.imgur.com/WPWuO.png

Diberikan tanggal sewenang-wenang, bagaimana angka-angka ini dapat dihitung dengan spesifikasi chrono C ++ 20 ?

Howard Hinnant
sumber
2
"... dan kita semua tahu kapan ISO 1 minggu, kan? ..." - "Tidak, tapi aku punya perpustakaan" ... :-) - Bravo Howard!
Ted Lyngmo
Gambar diambil dari stackoverflow.com/q/59391132/560648 (sekarang dihapus). Malu itu sudah dihapus karena ini seharusnya menjadi jawaban untuk pertanyaan itu.
Lightness Races in Orbit
Benar. Saya memilih untuk membuka kembali yang itu.
Howard Hinnant

Jawaban:

22

Ini sangat mudah dengan spesifikasi chrono C ++ 20 . Di bawah ini saya menunjukkan fungsi yang menginput tanggal sewenang-wenang, dan mencetak informasi ini ke cout. Padahal pada saat penulisan ini, spesifikasi ch ++ C ++ 20 belum dikirimkan, diperkirakan oleh pustaka sumber terbuka yang gratis . Jadi Anda dapat bereksperimen dengannya hari ini, dan bahkan memasukkannya dalam aplikasi pengiriman selama Anda mengadopsi C ++ 11 atau lebih baru.

Jawaban ini akan mengambil bentuk fungsi:

void info(std::chrono::sys_days sd);

sys_daysadalah ketelitian sehari time_pointdalam system_clockkeluarga. Itu berarti hanya hitungan hari sejak 1970-01-01 00:00:00 UTC. Tipe alias sys_daysbaru dengan C ++ 20, tetapi tipe yang mendasarinya telah tersedia sejak C ++ 11 ( time_point<system_clock, duration<int, ratio<86400>>>). Jika Anda menggunakan pustaka pratinjau C ++ 20 sumber terbuka , sys_daysada di namespace date.

Kode di bawah ini mengasumsikan fungsi-lokal:

using namespace std;
using namespace std::chrono;

untuk mengurangi verbositas. Jika Anda bereksperimen dengan pustaka pratinjau C ++ 20 sumber terbuka , asumsikan juga:

using namespace date;

Tajuk

Untuk menghasilkan dua baris pertama adalah sederhana:

cout << format("{:%d %B %Y is a %A}\n", sd)
     << "\nAdditional facts\n";

Ambil saja tanggal sddan gunakan formatdengan familiar strftime/ put_timebendera untuk mencetak tanggal dan teks. The open-source C ++ 20 perpustakaan pratinjau belum terintegrasi perpustakaan fmt , dan menggunakan format string sedikit diubah"%d %B %Y is a %A\n" .

Ini akan menampilkan (misalnya):

26 December 2019 is a Thursday

Additional facts

Hasil antara umum dihitung sekali

Bagian fungsi ini ditulis terakhir, karena seseorang belum tahu perhitungan apa yang akan dibutuhkan beberapa kali. Tapi begitu Anda tahu, inilah cara menghitungnya:

year_month_day ymd = sd;
auto y = ymd.year();
auto m = ymd.month();
weekday wd{sd};
sys_days NewYears = y/1/1;
sys_days LastDayOfYear = y/12/31;

Kita akan membutuhkan bidang tahun dan bulan sd, dan weekday(hari dalam seminggu). Adalah efisien untuk menghitungnya sekali dan untuk semua dengan cara ini. Kami juga akan membutuhkan (beberapa kali) hari-hari pertama dan terakhir tahun berjalan. Sulit untuk mengatakan pada saat ini, tetapi efisien untuk menyimpan nilai-nilai ini sebagai jenis sys_dayssebagai penggunaan selanjutnya mereka hanya dengan hari-berorientasi aritmatika yang sys_daysmerupakan sangat efisien (kecepatan sub-nanodetik).

Fakta 1: jumlah hari dalam setahun, dan jumlah hari tersisa dalam tahun

auto dn = sd - NewYears + days{1};
auto dl = LastDayOfYear - sd;
cout << "* It is day number " << dn/days{1} << " of the year, "
     << dl/days{1} << " days left.\n";

Ini mencetak jumlah hari dalam setahun, dengan 1 Januari menjadi hari 1, dan kemudian juga mencetak jumlah hari yang tersisa di tahun itu, tidak termasuk sd. Perhitungan untuk melakukan ini sepele. Membagi setiap hasil dengan days{1}cara untuk mengekstraksi jumlah hari dalam dndan dlmenjadi tipe integral untuk tujuan pemformatan.

Fakta 2: Jumlah hari kerja ini dan jumlah hari kerja dalam setahun

sys_days first_wd = y/1/wd[1];
sys_days last_wd = y/12/wd[last];
auto total_wd = (last_wd - first_wd)/weeks{1} + 1;
auto n_wd = (sd - first_wd)/weeks{1} + 1;
cout << format("* It is {:%A} number ", wd) << n_wd << " out of "
     << total_wd << format(" in {:%Y}.\n}", y);

wdadalah hari dalam seminggu (Senin hingga Minggu) yang dihitung di bagian atas artikel ini. Untuk melakukan perhitungan ini, pertama-tama kita perlu tanggal yang pertama dan terakhir wddi tahun tersebut y. y/1/wd[1]adalah yang pertama wddi bulan Januari, dan y/12/wd[last]yang terakhir wddi bulan Desember.

Jumlah total wds dalam tahun ini hanya jumlah minggu antara kedua tanggal ini (ditambah 1). Sub-ekspresi last_wd - first_wdadalah jumlah hari antara dua tanggal. Membagi hasil ini dengan 1 minggu menghasilkan tipe integral yang menahan jumlah minggu antara kedua tanggal.

Jumlah minggu dilakukan dengan cara yang sama dengan jumlah total minggu kecuali satu dimulai dengan hari saat ini bukan yang terakhir wdtahun ini: sd - first_wd.

Fakta 3: Jumlah hari kerja ini dan jumlah hari kerja dalam sebulan

first_wd = y/m/wd[1];
last_wd = y/m/wd[last];
total_wd = (last_wd - first_wd)/weeks{1} + 1;
n_wd = (sd - first_wd)/weeks{1} + 1;
cout << format("* It is {:%A} number }", wd) << n_wd << " out of "
     << total_wd << format(" in {:%B %Y}.\n", y/m);

Ini berfungsi seperti Fakta 2, kecuali kita mulai dengan pasangan pertama dan terakhir wddari pasangan tahun-bulan, y/mbukan sepanjang tahun.

Fakta 4: Jumlah hari dalam setahun

auto total_days = LastDayOfYear - NewYears + days{1};
cout << format("* Year {:%Y} has ", y) << total_days/days{1} << " days.\n";

Kode ini cukup banyak berbicara sendiri.

Fakta 5 Jumlah hari dalam sebulan

total_days = sys_days{y/m/last} - sys_days{y/m/1} + days{1};
cout << format("* {:%B %Y} has ", y/m) << total_days/days{1} << " days.\n";

Ungkapannya y/m/lastadalah hari terakhir dari pasangan tahun-bulan y/m, dan tentu saja y/m/1adalah hari pertama bulan itu. Keduanya dikonversi sys_dayssehingga mereka dapat dikurangkan untuk mendapatkan jumlah hari di antara mereka. Tambahkan 1 untuk hitungan berbasis 1.

Menggunakan

info dapat digunakan seperti ini:

info(December/26/2019);

atau seperti ini:

info(floor<days>(system_clock::now()));

Berikut ini contoh output:

26 December 2019 is a Thursday

Additional facts
* It is day number 360 of the year, 5 days left.
* It is Thursday number 52 out of 52 in 2019.
* It is Thursday number 4 out of 4 in December 2019.
* Year 2019 has 365 days.
* December 2019 has 31 days.

Edit

Bagi mereka yang tidak menyukai "sintaksis konvensional", ada "sintaks konstruktor" lengkap yang dapat digunakan sebagai gantinya.

Sebagai contoh:

sys_days NewYears = y/1/1;
sys_days first_wd = y/1/wd[1];
sys_days last_wd = y/12/wd[last];

dapat diganti dengan:

sys_days NewYears = year_month_day{y, month{1}, day{1}};
sys_days first_wd = year_month_weekday{y, month{1}, weekday_indexed{wd, 1}};
sys_days last_wd = year_month_weekday_last{y, month{12}, weekday_last{wd}};
Howard Hinnant
sumber
5
Penyalahgunaan baru dari operator divisi ini bahkan lebih buruk daripada penyalahgunaan lama dari operator bitshift. Itu membuatku sedih :(
Dave
2
Pada catatan yang lebih serius, dapatkah saya menyarankan Anda memindahkan beberapa variabel pra-komputasi Anda ke bagian yang menggunakannya? Agak canggung untuk mengikuti ketika harus menggulir ke atas dan ke bawah untuk melihat dari mana nilai berasal dan bagaimana mereka dihasilkan. Dan Anda dapat sedikit mengacaukan hal-hal harian Anda dengan melakukan pembagian pertama, seperti yang Anda lakukan selama berminggu-minggu.
Dave
1
Sepenuhnya tidak setuju. Itu terlihat bagus, mudah dimengerti dan, terutama, lebih mudah dibaca daripada versi yang lebih verbose.
Cássio Renan
@ CássioRenan mungkin, tetapi ingat bahwa penyalahgunaan sintaksis sering disertai dengan perilaku yang tidak terduga. Dengan bit shift tersebut di atas, misalnya, perhatikan perilaku std::cout << "a*b = " << a*b << "; a^b = " << a^b << '\n';(yang, untungnya, hampir selalu tertangkap pada waktu kompilasi, tetapi masih mengganggu). Jadi saya akan berhati-hati ketika menggunakan penyalahgunaan operator divisi baru ini.
Ruslan
@Ruslan Caution selalu dijamin dengan pustaka baru. Itu sebabnya yang satu ini telah diuji secara bebas dan publik sejak 2015. Umpan balik dari klien telah dimasukkan kembali ke dalam desain. Itu tidak diusulkan untuk standardisasi sampai ia memiliki dasar yang kuat dari pengalaman lapangan yang positif selama bertahun-tahun. Khususnya, penggunaan operator telah dirancang dengan mempertimbangkan prioritas operator, bidang diuji secara luas, dan dilengkapi dengan "konstruktor API" yang setara. Lihat star-history.t9t.io/#HowardHinnant/date&google/cctz dan youtube.com/watch?v=tzyGjOm8AKo .
Howard Hinnant