32
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2018-03-27

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 );
32
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?