芽萌丸プログラミング部 @programming
@programming
2021/2/10 8:10 更新✏

C++で日付時間の取得と変換

C++の標準ライブラリstd::chronoで日付を扱う方法です。

目次:

前提

サンプルコード

以下のサンプルコードは、

を行う方法を示しています。

time-test.cpp

#include <iostream>
#include <chrono>

using namespace std;

int main(int argc, char* argv[]) {
  //
  // 現在時間(unixエポック)を取得する方法
  //
  // 1. 現在のtime_pointを取得:
  auto tp = chrono::system_clock::now();
  // 2. time_pointからdurationを取得:
  auto duration = tp.time_since_epoch();
  // 3. durationからunixエポック(ミリ秒)を取得:
  auto ms = chrono::duration_cast<chrono::milliseconds>(duration).count();
  cout << "現在時間(unixエポックミリ秒): " << ms << endl;
  // or durationからunixエポック(秒)を取得:
  auto s = chrono::duration_cast<chrono::seconds>(duration).count();
  cout << "現在時間(unixエポック秒): " << s << endl;

  //
  // unixエポック(ミリ秒)からISO世界標準時間文字列を取得する方法
  //
  long int sec = ms / 1000;
  char isoStr[sizeof "2021-01-31T23:59:59.000Z"];
  strftime(isoStr, sizeof isoStr, "%FT%T", gmtime(&sec));
  int delta = ms - (sec * 1000);
  sprintf(isoStr, "%s.%03dZ", isoStr, delta);
  cout << "ISO世界標準時間文字列: " << isoStr << endl;

  //
  // unixエポック(ミリ秒)からローカル時間文字列を取得する方法
  //
  long int sec1 = ms / 1000;
  struct tm* timeinfo;
  timeinfo = localtime(&sec1);
  int charsize = sizeof "2021/02/09(Tue)23:59:59";
  char localStr[charsize];
  strftime(localStr, charsize, "%Y/%m/%d(%a)%H:%M:%S", timeinfo);
  cout << "ローカル時間文字列: " << localStr << endl;

  return 0;
}

コンパイル&実行:

## コンパイル&実行:
$ g++ -o a.out time-test.cpp && ./a.out 

現在時間(unixエポックミリ秒): 1612910593925
現在時間(unixエポック秒): 1612910593
ISO世界標準時間文字列: 2021-02-09T22:43:13.925Z
ローカル時間文字列: 2021/02/10(Wed)07:43:13

おまけ:自作日付操作クラス

せっかくなので、コピペで使えるシンプルなDateクラスも自作してみました。

Date.h

#ifndef DATE_H
#define DATE_H

#include <chrono>
#include <sstream>
#include <regex>

using namespace std;

/**
 * Date utility class
 *
 * Usage
 * ```
 * #include "path/to/Date.h"
 *
 * Date date;
 *
 * // Get a current unix epoch time in milliseconds.
 * long int now = date.getTime();
 * // or from ISO date string:
 * // long int now = date.getTime("2021-02-09T07:20:29.096Z");
 *
 * // Get an ISO date string:
 * std::string isoStr = date.getISOString();
 * // or from unix epoch time in milliseconds:
 * // std::string isoStr = date.getISOString(1612855960407);
 *
 * // Get a local date string:
 * std::string localStr = date.getLocaleString();
 * // or from unix epoch time in milliseconds:
 * // std::string localStr = date.getLocaleString(1612855960407);
 * // also, from unix epoch time in milliseconds with a format:
 * // std::string localStr = date.getLocaleString(1612855960407, "%Y/%m/%d(%a)%H:%M:%S");
 *
 * ```
 */
class Date {
private:
  /**
   * Check whether ISO date string with milliseconds or not. 
   * @param  isoStr {string} A ISO date string. e.g. "2021-02-09T01:46:45.595Z".
   * @return {bool} e.g. Passing "2021-02-09T01:46:45.595Z" (has millisecond's part) returns true.
   */
  static bool isLongISOString(std::string isoStr) {
    return true;
    regex re(R"(^(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)\.(\d+)Z$)");
    smatch m;
    regex_match(isoStr, m, re);
    if (m.size() >= 1) return true;
    return false;
  }

public:
  /**
   * Get a current time in millisecond.
   * @return {long int} a current unix epoch time in millisecond.
   */
  static long int getTime() {
    auto tp = std::chrono::system_clock::now();
    auto duration = tp.time_since_epoch();
    auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
    return msec;
  }
  /**
   * Converts an ISO date string to unix epoch time and returns it.
   * @param  isoStr {string} A ISO date string. e.g. "2021-02-09T01:46:45.595Z"
   * @return {long int} An unix epoch time in milliseconds.
   */
  static long int getTime(std::string isoStr) {
    int y, M, d, h, m, s;
    int ms = 0;
    if (Date::isLongISOString(isoStr)) {
      // with milliseconds' part:
      sscanf(isoStr.c_str(), "%d-%d-%dT%d:%d:%d.%dZ", &y, &M, &d, &h, &m, &s, &ms);
    } else {
      // without milliseconds' part:
      sscanf(isoStr.c_str(), "%d-%d-%dT%d:%d:%dZ", &y, &M, &d, &h, &m, &s);
    }
    std::tm tm = {
      /* .tm_sec  = */ s,
      /* .tm_min  = */ m,
      /* .tm_hour = */ h,
      /* .tm_mday = */ (d),
      /* .tm_mon  = */ (M) - 1,
      /* .tm_year = */ (y) - 1900,
    };
    tm.tm_isdst = -1; // Use DST value from local time zone
    auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
    auto duration = tp.time_since_epoch();
    auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
    return msec + ms; // Add actual millisecond's value to the rough result in millisecond.
  }
  /**
   * Get an ISO date string.
   * @param  msec {long int}
   * @return {std::string} An ISO date string. e.g. "2021-02-09T01:46:45.595Z".
   */
  static std::string getISOString(long int msec) {
    long int sec = msec / 1000;
    char isoStr[sizeof "2021-01-31T23:59:59.000Z"];
    strftime(isoStr, sizeof isoStr, "%FT%T", gmtime(&sec));
    int delta = msec - (sec * 1000);
    sprintf(isoStr, "%s.%03dZ", isoStr, delta);
    return isoStr;
  }
  /**
   * Get a current ISO date string.
   * @return {std::string} An ISO date string. e.g. "2021-02-09T01:46:45.595Z".
   */
  static std::string getISOString() {
    auto msec = Date::getTime();
    return getISOString(msec);
  }
  /**
   * Get a local date string.
   * @param msec {long int} An unix epoch time in milliseconds.
   * @return {std::string} A local date string. e.g. "2021/02/09(Tue)16:20:30".
   */
  static std::string getLocaleString(long int msec) {
    return Date::getLocaleString(msec, "%Y/%m/%d(%a)%H:%M:%S");
  }
  /**
   * Get a local date string.
   * @param msec {long int} An unix epoch time in milliseconds.
   * @param dateformat {char*} A date format. e.g. "%Y/%m/%d(%a)%H:%M:%S"
   * @return {std::string} A local date string. e.g. "2021/02/09(Tue)16:20:30".
   */
  static std::string getLocaleString(long int msec, const char* dateformat) {
    long int sec = msec / 1000;
    struct tm* timeinfo;
    timeinfo = localtime(&sec);
    int charsize = sizeof "2021/02/09(Tue)23:59:59";
    char output[charsize];
    strftime(output, charsize, dateformat, timeinfo);
    return string(output);
  }
  /**
   * Get a current local date string.
   * @return {std::string} A local date string. e.g. "2021/02/09(Tue)16:20:30".
   */
  static std::string getLocaleString() {
    return getLocaleString(Date::getTime());
  }
};

#endif

上記のDateクラスは以下のstaticメソッドを持っています:

使い方は以下のコードを参考にしてください。

Date-test.cpp

#include <iostream>
// 自作Dateクラスをinclude:
#include "Date.h"

using namespace std;

int main(int argc, char* argv[]) {

  // 自作Dateクラスを定義:
  const Date date;
  // テスト用のISO世界標準時間文字列(ミリ秒部分無し):
  const string isoShortStr = "2021-02-09T07:20:29Z";
  // テスト用のISO世界標準時間文字列:
  const string isoStr = "2021-02-09T07:20:29.096Z";

  //
  // ISO世界標準時間文字列 => unixエポックミリ秒
  //

  // ISO世界標準時間文字列(ミリ秒部分無し)をunixエポックミリ秒に変換:
  const long int time = date.getTime(isoShortStr); // TIP: 引数無しなら現在のunixエポックミリ秒を取得
  cout << isoShortStr << " => " << time << " (unixエポックミリ秒)" << endl;

  // ISO世界標準時間文字列をunixエポックミリ秒に変換:
  const long int time1 = date.getTime(isoStr);
  cout << isoStr << " => " << time1 << " (unixエポックミリ秒)" << endl;

  //
  // unixエポックミリ秒 => ISO世界標準時間文字列
  //

  // unixエポックミリ秒をISO世界標準時間文字列に変換:
  string isoStr1 = date.getISOString(time); // TIP: 引数無しなら現在のISO世界標準時間を取得
  cout << time << " => " << isoStr1 << " (ISO世界標準時間)" << endl;

  //
  // unixエポックミリ秒 => ロケール時間文字列
  //

  // unixエポックミリ秒をロケール時間文字列に変換:
  std::string localStr1 = date.getLocaleString(time); // TIP: 引数無しなら現在のロケール時間を取得
  cout << time << " => " << localStr1 << " (ロケール時間)" << endl;

  // unixエポックミリ秒をロケール時間文字列に変換(フォーマット指定):
  string localStr2 = date.getLocaleString(time, "%Y-%m-%d %a %H:%M:%S");
  cout << time << " => " << localStr2 << " (ロケール時間 with 形式指定)" << endl;

  return 0;
}

コンパイル&実行:

## コンパイル&実行:
$ g++ -o a.out Date-test.cpp && ./a.out

2021-02-09T07:20:29Z => 1612822829000 (unixエポックミリ秒)
2021-02-09T07:20:29.096Z => 1612822829096 (unixエポックミリ秒)
1612822829000 => 2021-02-08T22:20:29.000Z (ISO世界標準時間)
1612822829000 => 2021/02/09(Tue)07:20:29 (ロケール時間)
1612822829000 => 2021-02-09 Tue 07:20:29 (ロケール時間 with 形式指定)

以上です。

関連

C++

芽萌丸プログラミング部 @programming
芽萌丸プログラミング部@programming
プログラミング関連アカウント。Web標準技術を中心に書いていきます。フロントエンドからサーバサイドまで JavaScript だけで済ませたい人たちの集いです。記事は主に @TanakaSoftwareLab が担当。
オススメ:Zattoyomiで時事ネタチェックの時間節約!