C ++ 20 크로노를 사용하여 날짜에 대한 다양한 사실을 계산하는 방법


19

https://www.timeanddate.com/date/weekday.html 은 연중 무휴에 대한 다양한 사실을 계산합니다. 예를 들면 다음과 같습니다.

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

임의의 날짜가 주어지면 C ++ 20 크로노 사양으로 이러한 숫자를 어떻게 계산할 수 있습니까?


2
"... 그리고 우리 모두 ISO 1 주차가 언제인지 알고 있습니까? ..." - "아니요,하지만 도서관이 있어요"... :-)-Bravo Howard!
Ted Lyngmo

stackoverflow.com/q/59391132/560648 (현재 삭제됨) 에서 가져온 이미지 . 이 질문에 대한 답이되었으므로 삭제되었습니다.
궤도에서 가벼움 경주

옳은. 나는 그것을 다시 열기로 투표했다.
Howard Hinnant

답변:


22

이것은 C ++ 20 크로노 사양 에서 매우 쉽다 . 아래에서는 임의의 날짜를 입력 하고이 정보를에 인쇄하는 함수를 보여줍니다 cout. 이 글을 쓰는 시점에서 C ++ 20 크로노 사양 은 아직 출시되지 않았지만 무료 오픈 소스 라이브러리로 추정됩니다 . 따라서 오늘 시험해보고 C ++ 11 이상을 채택하는 한 배송 응용 프로그램에 포함시킬 수도 있습니다.

이 답변은 함수 형태를 취합니다.

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

sys_days하루 정밀도입니다 time_pointsystem_clock가족. 이는 1970-01-01 00:00:00 UTC 이후의 일 수임을 의미합니다. 유형 별명 sys_days은 C ++ 20에서 새로 추가되었지만 C ++ 11 ( time_point<system_clock, duration<int, ratio<86400>>>) 이후 기본 유형을 사용할 수 있습니다 . 당신이 사용하는 경우 오픈 소스 C ++ 20 미리보기 라이브러리를 , sys_days이다 namespace date.

아래 코드는 함수 로컬을 가정합니다.

using namespace std;
using namespace std::chrono;

자세한 정보를 줄입니다. 오픈 소스 C ++ 20 미리보기 라이브러리를 실험하고 있다면 다음과 같이 가정하십시오.

using namespace date;

표제

처음 두 줄을 출력하는 것은 간단합니다 :

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

날짜 를 입력하고 익숙한 / 플래그 와 함께 sd사용 하여 날짜와 텍스트를 인쇄하십시오. 오픈 소스 C ++ 20 미리보기 라이브러리는 아직 통합되지 않은 FMT 라이브러리를 , 그래서 약간 변경 형식 문자열을 사용합니다 .formatstrftimeput_time"%d %B %Y is a %A\n"

예를 들어 다음과 같이 출력됩니다.

26 December 2019 is a Thursday

Additional facts

한 번 계산 된 일반적인 중간 결과

함수의이 섹션은 마지막으로 작성됩니다. 아직 계산이 여러 번 필요한지 알 수 없기 때문입니다. 그러나 일단 알면 계산 방법은 다음과 같습니다.

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;

의 연도 및 월 필드 sdweekday(요일)이 필요합니다. 이러한 방식으로 한 번에 모두 계산하는 것이 효율적입니다. 우리는 또한 현재 연도의 첫 날과 마지막 날이 필요합니다 이 시점에서 말하기는 어렵지만, sys_days이후의 사용은 (나노초 이하의 속도) 매우 효율적인 일 지향적 인 산술에만 사용되므로 이러한 값을 유형 으로 저장 하는 sys_days것이 효율적입니다.

사실 1 : 일 수와 연도 남은 일수

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

이렇게하면 1 월 1 일이 1 일인 연도의 일수가 인쇄되고를 포함하지 않은 연도의 남은 일수가 인쇄됩니다 sd. 이를위한 계산은 간단합니다. 각 결과에 의해 제산하여 days{1}일수를 추출하는 방법 dndl목적 서식을 일체형으로.

사실 2 :이 요일의 수와 연중 총 요일 수

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

wd이 기사의 맨 위에 계산 된 요일 (월요일부터 일요일까지)입니다. 이 계산을 수행하려면 먼저 wd연도의 첫 번째와 마지막 날짜가 필요합니다 y. 1 월 y/1/wd[1]의 첫 번째 이며 12 월 의 마지막 입니다.wdy/12/wd[last]wd

wd해당 연도 의 총 개수는 이 두 날짜 사이의 주 수 (1을 더한 값)입니다. 하위 표현식 last_wd - first_wd은 두 날짜 사이의 일 수입니다. 이 결과를 1 주일로 나누면 두 날짜 사이의 주 수가 유지되는 정수 유형이됩니다.

주 번호는 wd연도가 아닌 현재 날짜로 시작하는 것을 제외하고 총 주 수와 동일한 방식으로 수행됩니다 sd - first_wd.

사실 3 :이 요일의 수와 월의 총 요일 수

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

이것은 wd연도 y/m대신 첫 달 과 마지막 달로 시작한다는 점을 제외하고는 사실 2와 동일하게 작동합니다 .

사실 4 : 연중 일수

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

코드는 거의 대부분을 말합니다.

사실 5 달의 일수

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

표현식 y/m/last은 연도의 마지막 날 y/m이며 물론 y/m/1첫 번째 날입니다. 둘 sys_days사이의 일수를 얻기 위해 빼기 위해 둘 다로 변환됩니다 . 1 기준 카운트에 1을 더합니다.

사용하다

info 다음과 같이 사용할 수 있습니다 :

info(December/26/2019);

또는 이와 같이 :

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

다음은 예제 출력입니다.

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.

편집하다

"기존 구문"을 좋아하지 않는 사람들을 위해 대신 사용할 수있는 완전한 "구문 구문"이 있습니다.

예를 들면 다음과 같습니다.

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

다음으로 대체 할 수 있습니다.

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
부서 운영자의이 새로운 남용은 비트 시프트 운영자의 이전 남용보다 훨씬 나쁩니다. 그것은 나를 슬프게한다 :(
Dave

2
더 중요한 것은 미리 계산 된 변수 중 일부를 해당 변수를 사용하는 섹션으로 옮길 것을 제안 할 수 있습니까? 위아래로 스크롤해야 값이 어디에서 왔으며 어떻게 생성되었는지 확인할 때 따라 가기가 다소 어색합니다. 그리고 몇 주 동안했던 것처럼 먼저 부서를 수행하여 일상적인 물건을 정리할 수 있습니다.
Dave

1
완전히 동의하지 않습니다. 보기 좋고 이해하기 쉽고 특히 장황한 버전보다 읽기 쉽습니다.
카시오 레난

@CssioRenan이있을 수 있지만 구문 남용은 종종 예기치 않은 동작이 발생한다는 것을 기억하십시오. 예를 들어, 앞서 언급 한 비트 시프트를 사용하면 동작이 std::cout << "a*b = " << a*b << "; a^b = " << a^b << '\n';거의 다를 수 있습니다 (다행히 컴파일 타임에 거의 항상 잡히지 만 여전히 성가심입니다). 따라서이 새로운 부서 운영자 남용을 사용할 때는주의해야합니다.
Ruslan

@Ruslan주의는 항상 새 라이브러리에 대해 보증됩니다. 그렇기 때문에 2015 년부터이 테스트를 무료로 공개적으로 테스트 한 것입니다. 고객의 의견이 디자인에 반영되었습니다. 그것은 수년간의 긍정적 인 현장 경험의 탄탄한 기초를 가질 때까지 표준화를 위해 제안되지 않았다. 특히, 연산자의 사용은 연산자 우선 순위를 염두에두고 설계되었으며 광범위하게 현장에서 테스트되었으며 동등한 "생성자 API"와 함께 제공됩니다. star-history.t9t.io/#HowardHinnant/date&google/cctzyoutube.com/watch?v=tzyGjOm8AKo를 참조하십시오 .
Howard Hinnant
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.