C++ 20 std::chrono 库使用 | std::chrono::year_month_day |std::chrono::hh_mm_ss 使用

最近在写异步日志库,结果为了偷懒完全用 C++20 的功能发现 C++20 的 g++11 的 timezone 支持还没有做完,根据 cppreference 的统计 g++ 只是 partial 完成。我一用就直接报错了。。。

在 C++ 有一个日期时间库是 Howard Hinnant 的 date 库,但是太复杂了。最后我决定直接用 UTC+0 也就是格林威治时间了属于是(等 C++20 timezone 被 g++ 实现了再做时间转换吧)。

这里 mark 一下 C++20 怎么获取日期和时间(cppreference 对hh_mm_ss的没有样例属于,我一开始没搞清楚durationtime_point的区别搞不出来,搜索也没搜到什么帮助,看来搜索能力还要提高):

void test_chrono() {
  auto now = std::chrono::system_clock::now();
  // this is a duration
  std::chrono::duration<int64_t, std::nano> ss = now.time_since_epoch();
  // so is this one
  auto hh = std::chrono::floor<std::chrono::days>(ss);
  // this is a time point
  std::chrono::time_point<std::chrono::system_clock, std::chrono::days> tp = std::chrono::floor<std::chrono::days>(now);
  // hhmmss use a duration
  std::chrono::hh_mm_ss<std::chrono::milliseconds> tod{std::chrono::duration_cast<std::chrono::milliseconds>(ss - hh)};
  // ymd use a time point
  const std::chrono::year_month_day ymd(tp);
  std::cout << static_cast<int>(ymd.year()) << static_cast<unsigned>(ymd.month()) << static_cast<unsigned>(ymd.day()) << tod.hours().count() << tod.minutes().count() << tod.seconds().count()
            << tod.subseconds().count();
}

然后就可以大概地包装套娃一下写一个一个时间戳类了,不过只是简略包装一下了属于,没有大量测试之前知道这样用起来靠不靠谱,根据 C++ 20 的说明理论上是有类似 fmtlib 的 format_to 的实现的,不过目前好像只有 MSVC++ 支持,为了方便暂时还是先用 cstdio 做格式化了,而且这里因为做日志用的时间戳要缓存格式化好的日期避免重复格式化(格式化大概是 O(len) ? 如果只是比较一下就只是 O(1) 一秒内多次格式化差距还是挺大的),所以我直接做成写到 c style char array 里面的接口了,最简化了,由于日志功能主要给单个项目用的画,格式一般直接写死了避免太多转换的复杂度(虽然的确可以做成静态编译期处理),不做太多 accidental 的工作了:

class timestamp {
 private:
  static constexpr char TS_FMT_DATE[] = "%4d-%2u-%2u ";
  static constexpr char TS_FMT_BASE_TIME[] = "%02ld:%02ld:%02ld ";
  static constexpr char TS_FMT_MICRO_SEC[] = ".%06ld";

 public:
  static constexpr int fmt_date_len = sizeof TS_FMT_DATE;
  static constexpr int fmt_base_time_len = sizeof TS_FMT_BASE_TIME;
  static constexpr int fmt_micro_second_len = sizeof TS_FMT_MICRO_SEC;
  timestamp()
      : sys_timestamp_{std::chrono::system_clock::now()},
        ymd_data_{std::chrono::floor<std::chrono::days>(sys_timestamp_)},
        hms_data_{std::chrono::duration_cast<std::chrono::microseconds>(sys_timestamp_.time_since_epoch() - std::chrono::duration_cast<std::chrono::days>(sys_timestamp_.time_since_epoch()))} {}

  int year() { return static_cast<int>(ymd_data_.year()); }
  unsigned month() { return static_cast<unsigned>(ymd_data_.month()); }
  unsigned day() { return static_cast<unsigned>(ymd_data_.day()); }
  long hour() { return hms_data_.hours().count(); }
  long minute() { return hms_data_.minutes().count(); }
  long second() { return hms_data_.seconds().count(); }
  long microsecond() { return hms_data_.subseconds().count(); }

  bool compare_date(timestamp &rhs) {
    // TODO: is there a performance problem?
    return std::chrono::floor<std::chrono::days>(sys_timestamp_) == std::chrono::floor<std::chrono::days>(rhs.sys_timestamp_);
  }
  bool compare_second(timestamp &rhs) {
    // TODO:  is there a performance problem?
    return std::chrono::floor<std::chrono::seconds>(sys_timestamp_) == std::chrono::floor<std::chrono::seconds>(rhs.sys_timestamp_);
  }
  // use ptr if as output, use ref if as const reference (lvalue)
  // buf must larger than 4+2+2 + blanks
  char *format_date_to(char *buf) {
    int w = ::snprintf(buf, 50, TS_FMT_DATE, year(), month(), day());
    return buf + w;
  }
  char *format_base_time_to(char *buf) {
    int w = ::snprintf(buf, 50, TS_FMT_BASE_TIME, hour(), minute(), second());
    return buf + w;
  }
  char *format_micro_to(char *buf) {
    int w = ::snprintf(buf, 50, TS_FMT_MICRO_SEC, microsecond());
    return buf + w;
  }
  char *format_time_to(char *buf) {
    auto n = format_base_time_to(buf);
    auto w = format_micro_to(n);
    return w;
  }
  char *format_to(char *buf) {
    auto n = format_date_to(buf);
    auto w = format_time_to(n);
    return w;
  }

  std::string to_formatted_string() {
    char buf[40];
    auto n = format_to(buf);
    return buf;
  }

 private:
  const std::chrono::time_point<std::chrono::system_clock> sys_timestamp_;
  const std::chrono::year_month_day ymd_data_;
  const std::chrono::hh_mm_ss<std::chrono::microseconds> hms_data_;
};

打印结果是这样的,应该没写错了:
在这里插入图片描述
对于floortime_since_epoch以及 duration cast 的性能, 看了一下实现,要么是直接返回数据要么就是进行O(1)的 算数运算,应该不成问题。之后写完了会测一下。

参考:
https://en.cppreference.com/w/cpp/chrono/hh_mm_s
std::chrono::system_clock - cppreference.com
https://stackoverflow.com/questions/12927169/how-can-i-initialize-c-object-member-variables-in-the-constructor/12927220
http://www.cpp.re/forum/general/266667/ | Equivalent of in - C++ Forum
https://github.com/HowardHinnant/date
https://gcc.gnu.org/projects/cxx-status.html | C++ Standards Support in GCC - GNU Project
https://docs.microsoft.com/en-us/cpp/standard-library/zoned-time-class?view=msvc-170 |

你可能感兴趣的:(C++,编程语言,笔记,c++)