Sử dụng đồng hồ bấm giờ C ++ 20, cách tính các sự kiện khác nhau về một ngày


19

https://www.timeanddate.com/date/weekday.html tính toán các sự kiện khác nhau về một ngày trong năm, ví dụ:

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

Cho một ngày tùy ý, làm thế nào những con số này có thể được tính toán với đặc tả chrono C ++ 20 ?


2
"... và tất cả chúng ta đều biết khi nào ISO tuần 1, phải không? ..." - "Không, nhưng tôi đã có một thư viện" ... :-) - Bravo Howard!
Ted Lyngmo

Hình ảnh được lấy từ stackoverflow.com/q/59391132/560648 (hiện đã bị xóa). Thật xấu hổ vì nó đã bị xóa vì điều này phải là một câu trả lời cho câu hỏi đó.
Các cuộc đua nhẹ nhàng trong quỹ đạo

Chính xác. Tôi đã bỏ phiếu để mở lại cái đó.
Howard Hinnant

Câu trả lời:


22

Điều này là khá dễ dàng với đặc điểm kỹ thuật chrono C ++ 20 . Dưới đây tôi hiển thị một hàm nhập ngày tùy ý và in thông tin này sang cout. Mặc dù tại thời điểm viết bài này, đặc tả chrono của C ++ 20 chưa được vận chuyển, nhưng nó được xấp xỉ bởi một thư viện mã nguồn mở miễn phí . Vì vậy, bạn có thể thử nghiệm với nó ngay hôm nay và thậm chí đưa nó vào các ứng dụng vận chuyển miễn là bạn áp dụng C ++ 11 trở lên.

Câu trả lời này sẽ có dạng hàm:

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

sys_dayslà một ngày chính xác time_pointtrong system_clockgia đình. Điều đó có nghĩa là nó chỉ đơn giản là một số ngày kể từ 1970-01-01 00:00:00 UTC. Bí danh loại sys_dayslà mới với C ++ 20, nhưng loại cơ bản đã có sẵn kể từ C ++ 11 ( time_point<system_clock, duration<int, ratio<86400>>>). Nếu bạn sử dụng thư viện xem trước C ++ 20 nguồn mở , thì sys_daysnamespace date.

Mã dưới đây giả định hàm-local:

using namespace std;
using namespace std::chrono;

để giảm tính dài dòng. Nếu bạn đang thử nghiệm thư viện xem trước C ++ 20 nguồn mở , cũng giả sử:

using namespace date;

Tiêu đề

Để xuất hai dòng đầu tiên là đơn giản:

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

Chỉ cần lấy ngày sdvà sử dụng formatvới cờ strftime/ put_timecờ quen thuộc để in ra ngày và văn bản. Các C ++ 20 thư viện xem trước mã nguồn mở vẫn chưa tích hợp các thư viện fmt , và do đó sử dụng các chuỗi định dạng một chút thay đổi "%d %B %Y is a %A\n".

Điều này sẽ xuất ra (ví dụ):

26 December 2019 is a Thursday

Additional facts

Kết quả trung gian chung được tính một lần

Phần này của hàm được viết sau cùng, bởi vì người ta chưa biết tính toán nào sẽ cần nhiều lần. Nhưng một khi bạn biết, đây là cách tính toán chúng:

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;

Chúng ta sẽ cần các trường năm và tháng sdweekday(ngày trong tuần). Đó là hiệu quả để tính toán chúng một lần và mãi mãi theo cách này. Chúng tôi cũng sẽ cần (nhiều lần) những ngày đầu tiên và cuối cùng của năm hiện tại. Thật khó có thể nói vào thời điểm này, nhưng nó là hiệu quả để lưu trữ các giá trị như kiểu sys_daysnhư sử dụng tiếp theo của họ là duy nhất có số học ngày theo định hướng sys_daysrất hiệu quả tại (tốc độ sub-nano giây).

Sự thật 1: số ngày trong năm và số ngày còn lại trong năm

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

Điều này in ra số ngày trong năm, với ngày 1 tháng 1 là ngày 1, và sau đó cũng in ra số ngày còn lại trong năm, không bao gồm sd. Tính toán để làm điều này là tầm thường. Chia từng kết quả days{1}là một cách để trích xuất số ngày trong dndlthành một loại tách rời cho mục đích định dạng.

Sự thật 2: Số ngày trong tuần này và tổng số ngày trong năm

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

wdlà ngày trong tuần (Thứ Hai đến Chủ nhật) được tính ở đầu bài viết này. Để thực hiện tính toán này, trước tiên chúng ta cần ngày của lần đầu tiên và lần cuối cùng wdtrong năm y. y/1/wd[1]là lần đầu tiên wdvào tháng Giêng, và y/12/wd[last]là lần cuối cùng wdvào tháng 12.

Tổng số wds trong năm chỉ là số tuần giữa hai ngày này (cộng 1). Biểu thức con last_wd - first_wdlà số ngày giữa hai ngày. Chia kết quả này cho 1 tuần sẽ dẫn đến một loại tích phân giữ số tuần giữa hai ngày.

Số tuần được thực hiện theo cách tương tự như tổng số tuần trừ một bắt đầu bằng ngày hiện tại thay vì cuối cùng wdcủa năm : sd - first_wd.

Sự thật 3: Số ngày trong tuần này và tổng số ngày trong tuần trong tháng

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

Điều này hoạt động giống như Fact 2, ngoại trừ chúng tôi bắt đầu với cặp đầu tiên và cuối cùng wdcủa cặp tháng y/mthay vì cả năm.

Sự thật 4: Số ngày trong năm

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

Các mã khá nhiều nói cho chính nó.

Fact 5 Số ngày trong tháng

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

Biểu thức y/m/lastlà ngày cuối cùng của cặp năm y/mvà tất nhiên y/m/1là ngày đầu tiên của tháng. Cả hai đều được chuyển đổi để sys_dayscó thể trừ đi để có được số ngày giữa chúng. Thêm 1 cho số đếm dựa trên 1.

Sử dụng

info có thể được sử dụng như thế này:

info(December/26/2019);

hoặc như thế này:

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

Đây là ví dụ đầu ra:

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.

Biên tập

Đối với những người không thích "cú pháp thông thường", có một "cú pháp xây dựng" hoàn chỉnh có thể được sử dụng thay thế.

Ví dụ:

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

có thể được thay thế bởi:

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
Sự lạm dụng mới này của toán tử phân chia thậm chí còn tồi tệ hơn so với lạm dụng cũ của các toán tử bithift. Nó làm tôi buồn :(
Dave

2
Trong một lưu ý nghiêm trọng hơn, tôi có thể đề nghị bạn chuyển một số biến được tính toán trước của bạn xuống các phần sử dụng chúng không? Có một chút lúng túng khi làm theo khi phải cuộn lên xuống để xem giá trị đến từ đâu và cách chúng được tạo ra. Và bạn có thể giải quyết công việc hàng ngày một chút bằng cách thực hiện phân chia trước, giống như bạn đã làm trong nhiều tuần.
Dave

1
Không đồng ý hoàn toàn. Nó có vẻ tốt, dễ hiểu và đáng chú ý là nó dễ đọc hơn phiên bản dài dòng hơn.
Cássio Renan

@ CássioRenan có thể, nhưng hãy nhớ rằng việc lạm dụng cú pháp khá thường xuyên đi kèm với hành vi không mong muốn. Với các thay đổi bit đã nói ở trên, ví dụ, lưu ý hành vi của std::cout << "a*b = " << a*b << "; a^b = " << a^b << '\n';(may mắn thay, hầu như luôn bị bắt tại thời gian biên dịch, nhưng vẫn gây phiền toái). Vì vậy, tôi nên thận trọng khi sử dụng lạm dụng toán tử phân chia mới này.
Ruslan

@Ruslan Thận trọng luôn được bảo hành với bất kỳ thư viện mới. Đó là lý do tại sao cái này đã được thử nghiệm tự do và công khai từ năm 2015. Phản hồi từ khách hàng đã được đưa lại vào thiết kế. Nó đã không được đề xuất cho tiêu chuẩn hóa cho đến khi nó có một nền tảng vững chắc của nhiều năm kinh nghiệm lĩnh vực tích cực. Cụ thể, việc sử dụng các toán tử đã được thiết kế với ưu tiên của toán tử, trường được thử nghiệm rộng rãi và đi kèm với một "API xây dựng" tương đương. Xem star-history.t9t.io/#HowardHinnant/date&google/cctzyoutube.com/watch?v=tzyGjOm8AKo .
Howard Hinnant
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.