C ++ 20 chrono kullanarak, bir tarih hakkında çeşitli gerçekleri hesaplama


19

https://www.timeanddate.com/date/weekday.html yılın bir günü hakkında çeşitli gerçekleri hesaplar, örneğin:

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

Keyfi bir tarih verildiğinde, bu sayılar C ++ 20 krono spesifikasyonu ile nasıl hesaplanabilir ?


2
“... ve ISO 1'in ne zaman olduğunu hepimiz biliyoruz, değil mi? ...” - “Hayır, ama bir kütüphanem var” ... :-) - Bravo Howard!
Ted Lyngmo

Görüntü stackoverflow.com/q/59391132/560648 (şimdi silindi) ' den alınmıştır . Yazık ki bu soruya cevap olması gerektiği için silinmiş.
Yörüngedeki Hafiflik Yarışları

Doğru. Bunu yeniden açmak için oy verdim.
Howard Hinnant

Yanıtlar:


22

Bu, C ++ 20 krono spesifikasyonu ile oldukça kolaydır . Aşağıda gelişigüzel bir tarih girip bu bilgiyi üzerine yazdıran bir işlevi gösteriyorum cout. Bu yazının yazıldığı sırada, C ++ 20 krono spesifikasyonu henüz gönderilmese de, ücretsiz, açık kaynaklı bir kütüphane ile yaklaşıyor . Böylece bugün deneyebilir ve hatta C ++ 11 veya üstünü benimsediğiniz sürece nakliye uygulamalarına dahil edebilirsiniz.

Bu cevap bir işlev şeklinde olacaktır:

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

sys_daysGünde hassasiyetli bir time_pointde system_clockailesi. Bu, 1970-01-01 00:00:00 UTC'den bu yana geçen gün sayısı anlamına gelir. Tür takma adı sys_daysC ++ 20 ile yenidir, ancak altta yatan tür C ++ 11 ( time_point<system_clock, duration<int, ratio<86400>>>) 'den beri mevcuttur . Eğer kullanırsanız açık kaynak C ++ 20 önizleme kütüphanesi , sys_daysiçindedir namespace date.

Aşağıdaki kodda yerel işlev varsayılmaktadır:

using namespace std;
using namespace std::chrono;

ayrıntı düzeyini azaltmak için. Eğer deneme yapıyorsanız açık kaynak C ++ 20 önizleme kütüphanede , farz edin ki:

using namespace date;

Başlık

İlk iki satırı çıkarmak basittir:

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

Tarihi alın sdve tarihi ve metni yazdırmak formatiçin tanıdık strftime/ put_timebayraklarla kullanın. Açık kaynak C ++ 20 önizleme kütüphanesi henüz entegre edilmemiştir fmt kütüphane ve benzeri biraz değişmiş biçim dizesi kullanır "%d %B %Y is a %A\n".

Bu çıktı (örneğin):

26 December 2019 is a Thursday

Additional facts

Bir kez hesaplanan ortak ara sonuçlar

Fonksiyonun bu bölümü en son yazılır, çünkü hangi hesaplamaların birden fazla kez gerekli olacağını henüz bilmiyoruz. Ancak bir kez bildiğinizde, bunları nasıl hesaplayacağınız aşağıda açıklanmıştır:

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;

Yıl ve ay alanlarına sdve weekday(haftanın günü) ihtiyacımız olacak. Bunları bir kez ve herkes için bu şekilde hesaplamak etkilidir. Ayrıca bu yılın ilk ve son günlerine de (birden çok kez) ihtiyacımız olacak. Noktada söylemek zor, ama türü olarak bu değerleri depolamak için etkilidir sys_daysonların daha sonraki kullanım sadece gündüz odaklı aritmetik ile olduğu gibi sys_daysolduğu çok (alt nanosaniye hızlarında) de etkili.

Gerçek 1: yılın gün sayısı ve yılda kalan gün sayısı

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

Bu, 1 Ocak 1 gün olmak üzere yılın gün sayısını yazdırır ve daha sonra dahil olmayan yıl içinde kalan gün sayısını yazdırır sd. Bunu yapmak için yapılan hesaplama önemsizdir. Her bir sonucu bölmek, biçimlendirme amacıyla days{1}gün sayısını dnve dlayrılmaz bir türe ayırmanın bir yoludur .

Gerçek 2: Bu hafta içi gün sayısı ve yıl içindeki toplam hafta içi gün sayısı

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

wdbu makalenin en üstünde hesaplanan haftanın günüdür (Pazartesi-Pazar). Bu hesaplamayı yapabilmek için önce wdyıl içindeki ilk ve son tarihlere ihtiyacımız var y. y/1/wd[1]ilk wdOcak ayında ve y/12/wd[last]son bir wdAralık ayında.

Yıldaki toplam wds sayısı, bu iki tarih arasındaki hafta sayısıdır (artı 1). Alt ifade last_wd - first_wd, iki tarih arasındaki gün sayısıdır. Bu sonucun 1 haftaya bölünmesi, iki tarih arasındaki hafta sayısını tutan ayrılmaz bir türle sonuçlanır.

Hafta numarasını yerine geçen akım gün biri başlar hariç toplam hafta sayısı ile aynı şekilde yapılır wdyılın: sd - first_wd.

Gerçek 3: Bu hafta içi gün sayısı ve aydaki toplam hafta içi gün sayısı

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

Gerçek 2 gibi çalışır, ancak wdyıl boyunca çiftin ilk ve son s'si ile başlarız y/m.

Gerçek 4: Yıl içindeki gün sayısı

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

Kod hemen hemen kendisi için konuşur.

Gerçek 5 Aydaki gün sayısı

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

İfade y/m/last, yıl-ay çiftinin son günü y/mve elbette y/m/1ayın ilk günüdür. Her ikisi de, sys_daysaralarındaki gün sayısını almak için çıkarılabilecek şekilde dönüştürülür . 1 tabanlı sayı için 1 ekleyin.

kullanım

info şu şekilde kullanılabilir:

info(December/26/2019);

ya da bunun gibi:

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

İşte örnek çıktı:

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.

Düzenle

"Geleneksel sözdizimine" düşkün olmayanlar için, bunun yerine kullanılabilecek eksiksiz bir "yapıcı sözdizimi" vardır.

Örneğin:

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

ile değiştirilebilir:

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

5
Bölme operatörünün bu yeni kötüye kullanımı, bit kaydırma operatörlerinin eski kötüye kullanımından bile daha kötüdür. Beni üzüyor :(
Dave

2
Daha ciddi bir not olarak, önceden hesaplanmış değişkenlerinizin bazılarını bunları kullanan bölümlere taşımanızı önerebilir miyim? Değerlerin nereden geldiğini ve nasıl oluşturulduğunu görmek için yukarı ve aşağı kaydırmak gerektiğinde takip etmek biraz gariptir. Ve haftada yaptığınız gibi, önce bölümü yaparak yılın gününü biraz karıştırabilirsiniz.
Dave

1
Tamamen katılmıyorum. İyi görünüyor, anlaşılması kolay ve özellikle daha ayrıntılı versiyondan okumak daha kolay.
Cássio Renan

@ CássioRenan olabilir, ancak sözdizimi kötüye kullanımının sıklıkla beklenmedik davranışlarla geldiğini unutmayın. Yukarıda belirtilen bit kaymaları ile, örneğin, davranışına dikkat edin std::cout << "a*b = " << a*b << "; a^b = " << a^b << '\n';(ki, neyse ki, neredeyse her zaman derleme zamanında yakalanır, ancak yine de bir sıkıntıdır). Bu nedenle, bu yeni bölüm operatörünü kötüye kullanırken dikkatli olurum.
Ruslan

@Ruslan Yeni kitaplıklar için daima dikkatli olunmalıdır. Bu yüzden bu 2015'ten beri serbestçe ve halka açık bir şekilde test edildi. Müşterilerden gelen geri bildirimler tasarıma dahil edildi. Yıllarca süren olumlu saha deneyiminin sağlam bir temeli olana kadar standardizasyon için önerilmemiştir. Özellikle, operatörlerin kullanımı, operatör önceliği göz önünde bulundurularak tasarlanmıştır, sahada yaygın olarak test edilmiştir ve eşdeğer bir "kurucu API" ile birlikte gelir. Bkz. Star-history.t9t.io/#HowardHinnant/date&google/cctz ve youtube.com/watch?v=tzyGjOm8AKo .
Howard Hinnant
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.