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