23
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

C++20標準ライブラリ <chrono>ヘッダ Tips

C++20標準ライブラリ<chrono>ヘッダに追加予定の カレンダー(Calendar)/タイムゾーン(Time Zone) サポートのTips集。

注意: 本ページの内容は、2018年3月のJacksonville会合で採用された P0355R7 Extending <chrono> to Calendars and Time Zones にもとづきます。2018年4月現在、後述サンプルコードを実行できるC++処理系はまだ存在しません。

基本編

現在時刻の出力

C++20
#include <chrono>
#include <iostream>

auto now_utc = std::chrono::system_clock::now();
std::cout << now_utc << std::endl;
// 出力例: 2018-04-01 01:23:45.678901 UTC

auto now_local = std::chrono::zoned_time{std::chrono::current_zone(), now_utc};
std::cout << now_local << std::endl;
// 出力例: 2018-04-01 10:23:45.678901 JST

auto now_jst = std::chrono::zoned_time{"Asia/Tokyo", now_utc};
std::cout << now_jst << std::endl;
// 出力例: 2018-04-01 10:23:45.678901 JST

// ISO 8601形式
std::cout << format("%FT%RZ", now_utc) << std::endl;
// 出力例: 2018-04-01T01:23Z
std::cout << format("%FT%TZ", floor<std::chrono::seconds>(now_utc)) << std::endl;
// 出力例: 2018-04-01T01:23:45Z
std::cout << format("%FT%TZ", now_utc) << std::endl;
// 出力例: 2018-04-01T01:23:45.678901Z

std::cout << format("%FT%T%Ez", now_jst) << std::endl;
// 出力例: 2018-04-01T10:23:45.678901+09:00

年月日時分秒フィールドへの分解

C++20
#include <cassert>
#include <chrono>

// 2000-09-01 23:45:56 UTC
using namespace std::chrono_literals;  // y,d,h,min,s
using std::chrono::September;
auto tp = std::chrono::sys_days{2000y/September/1d} + 23h + 45min + 56s;

auto dp = floor<std::chrono::days>(tp);

// 年月日
auto ymd = std::chrono::year_month_day{dp};
assert(ymd.year()  == std::chrono::year{2000});
assert(ymd.month() == std::chrono::month{9});
assert(ymd.day()   == std::chrono::day{1});

// 時分秒
auto hms = std::chrono::hh_mm_ss{tp - dp};
assert(hms.hours()   == std::chrono::hours{23});
assert(hms.minutes() == std::chrono::minutes{45});
assert(hms.seconds() == std::chrono::seconds{56});

year, month, day, hours, minutes, seconds 型の値は、明示キャストによって符号なし整数型(unsigned)へも変換可能。レガシーAPIに渡すなどの特殊ケースを除いて、型安全のためそれぞれの型のまま扱う方が好ましい。

日付の正規化

C++20
#include <cassert>
#include <chrono>

using namespace std::chrono_literals;  // y
using std::chrono::year_month_day, std::chrono::sys_days;
// 2018-03-32 == 2018-04-01
year_month_day canonical_ymd = sys_days{ 2018y/3/32 };
assert( canonical_ymd == 2018y/4/1 );

曜日の計算

C++20
#include <cassert>
#include <chrono>

using namespace std::chrono_literals;  // y
using std::chrono::weekday, std::chrono::sys_days;
// 2018-04-01 == 日曜日(0 or 7)
auto wd = weekday{sys_days{2018y/4/1}};
assert( wd == std::chrono::Sunday );
assert( wd.c_encoding() == 0 );
assert( wd.iso_encoding() == 7 );

日数計算

C++20
#include <cassert>
#include <chrono>

using namespace std::chrono_literals;  // y
using std::chrono::sys_days;
// 期間(日数)
auto olympic_days = 1 + (sys_days{2020y/8/9} - sys_days{2020y/7/24}).count();
assert( olympic_days == 17 );

// 1000日前の日付
auto olympic_pre1000d = sys_days{2020y/7/24} - std::chrono::days{1000};
assert( olympic_pre1000d == 2017y/10/28 );

応用編

万年カレンダー

C++20
#include <chrono>
#include <iostream>
#include <iomanip>

void print_calendar(std::ostream& os, const std::chrono::year_month& ym)
{
  using namespace std::chrono;

  unsigned weekday_offset{ weekday{sys_days{ym/1}}.c_encoding() };
  unsigned lastday_in_month{ (ym/last).day() };

  os << "      " << ym << "\n"
     << "Su Mo Tu We Th Fr Sa\n";
  unsigned wd = 0;
  while (wd++ < weekday_offset)
    os << "   ";
  for (unsigned d = 1; d <= lastday_in_month; ++d, ++wd)
    os << std::setw(2) << d << (wd % 7 == 0 ? '\n' : ' ');
}

int main()
{
  using namespace std::chrono_literals;
  print_calendar(std::cout, 2018y/3);
}
/*
      2018/Mar
Su Mo Tu We Th Fr Sa
             1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
*/

年内進捗率の計算

C++20
#include <chrono>

double year_progress()
{
  using namespace std::chrono;

  auto today = floor<days>(system_clock::now());
  auto this_year = year_month_day{today}.year();

  auto elapsed_days = (today - sys_days{this_year/1/1}).count();
  auto days_in_year = 1 + (sys_days{this_year/12/31} - sys_days{this_year/1/1}).count();

  return (elapsed_days * 100. / days_in_year);
}

// 2018-03-27現在は 85/365=23.2877%

うるう年の判定

ある年が うるう年(leap year) か否かを判定する。

C++20
#include <cassert>
#include <chrono>

inline
bool is_uruu(int y)
{
  return std::chrono::year{y}.is_leap();
}

assert(  is_uruu(2000) );
assert( !is_uruu(2011) );
assert(  is_uruu(2016) );
assert( !is_uruu(2018) );

海の日の計算

ある年の 7月第3月曜日(海の日)の日付を計算する。

C++20
#include <cassert>
#include <chrono>

std::chrono::year_month_day
marine_day(std::chrono::year y)
{
  using std::chrono::Monday;
  // 7月第3月曜日
  return std::chrono::sys_days{ y/7/Monday[3] };
}

using namespace std::chrono_literals;  // y
assert( marine_day(2018y) == 2018y/7/16 );

プレミアムフライデーの計算

ある月の 最終金曜日(aka プレミアムフライデー)の日付を計算する。

C++20
#include <cassert>
#include <chrono>

std::chrono::year_month_day
premium_friday(std::chrono::year_month ym)
{
  using std::chrono::last;
  using std::chrono::Friday;
  // 最終金曜日
  return std::chrono::sys_days{ ym/Friday[last] };
}

using namespace std::chrono_literals;  // y,d
assert( premium_friday(2018y/4) == 2018y/4/27d );
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
23
Help us understand the problem. What are the problem?