Bootstrap

C++软件开发中的时间

计算机系统中的时间

计算机系统中的时间是一个比较重要的东西。现代社会的各个信息系统,例如电力、交通、金融等各行各业都依赖精确的时间系统,一个时间系统的bug很可能造成巨大的经济损失;在普通的开发工作中,也会涉及时间的概念,例如时间戳、日志、性能测试等。因此软件开发人员需要了解一些时间上的概念,包括标准时间、常用的时间和格式等

几种概念的区别和联系

  • GMT时间:所谓的格林威治时间,历史上曾经作为标准时间

  • UTC时间:协调世界时,标准的时间,基于原子钟,非常精确,通过不规则地添加闰秒保持与平太阳的时的一致性

  • 本地时间:UTC+时区,例如北京时间就是UTC+8

  • Posix/Unix时间:从UTC 1970年1月1日0时0分起至现在的总秒数,通常叫做时间戳,不考虑闰秒.(如果是32位系统,2038年的某一天将会溢出(秒之后),这就是所谓的Unix2038问题)

  • ISO8601:UTC日期时间格式的国际标准,例如:“2020-12-23T16:53:02.418Z”

北京时间(东八区)与UTC 时间的时差为8h, 即 “2020-12-23T16:53:02.418Z”对应的北京时间为"2020-12-24 0:53:02.418",ISO8601表示为“2020-12-23T16:53:02.418+8:00”

小结:为了国际化,应当采用标准的时间,因此应该使用UTC的时间,并且用ISO8601的时间日期表示作为持久化格式。

C++中的日期时间

C++ 兼容C语言,因此提供的日期时间的概念比较多。主要有C语言的日期时间工具和

C库中的日期时间

  • time_ttime_t 定义在中,表示 time since epoch(1970-01-01T00:00::00),精确到秒,因此表示的其实是Posix时间。

  • tmtm 定义在中,tm是一个数据结构,保存的是日历时间,年月日时分秒的信息都保存了,并且提供了夏令时的标志位(tm_isdst)。

  • timespectimespec 是C11之后引入的数据结构,支持纳秒级别的精度。

  • clock_t. C语言可以通过 返回程序启动后的到现在的时钟数(处理器概念中的时钟),可以通过 来转化为秒数

除了这些类型定义, C语言还提供了一些时间操作、格式转换的函数,可以参考 了解更多信息

C++中的chrono

C++11之后引入的比C语言更加强大,主要提供了以下三种主要的类型

  • : clock中定义了诸如system_clock、steady_clock、high_resolution_clock 乃至utc_clock(C++20)、gps_clock(C++20)等应用于不同的场景。

  • : time_point 表示某个时刻,例如system_clock::now()返回的就是当前时刻的时间。

  • : 两个时刻的差为duration。通常用于计算耗时、间隔等场景

具体可以参考

time_point 和 UTC-ISO8601格式互转

可以把system_clock的time_point和UTC-ISO8601的格式进行相互转换。

以下代码来自

#include 
inline std::string to_iso_8601(std::chrono::time_point t) {
// convert to time_t which will represent the number of
// seconds since the UNIX epoch, UTC 00:00:00 Thursday, 1st. January 1970
auto epoch_seconds = std::chrono::system_clock::to_time_t(t);
 
// Format this as date time to seconds resolution
// e.g. 2016-08-30T08:18:51
std::stringstream stream;
stream << std::put_time(gmtime(&epoch_seconds), "%FT%T");
 
// If we now convert back to a time_point we will get the time truncated
// to whole seconds
auto truncated = std::chrono::system_clock::from_time_t(epoch_seconds);
 
// Now we subtract this seconds count from the original time to
// get the number of extra microseconds..
auto delta_us = std::chrono::duration_cast(t - truncated).count();
 
// And append this to the output stream as fractional seconds
// e.g. 2016-08-30T08:18:51.867479
stream << "." << std::fixed << std::setw(6) << std::setfill('0') << delta_us;
 
return stream.str();
}

从 iso_8601转为system_clock::time_point,自己实现的:

inline std::chrono::system_clock::time_point utc_from_iso_8601(std::string 
datetime_in_iso) {
  std::tm t = {};
  std::istringstream ss(datetime_in_iso);
  ss >> std::get_time(&t, "%Y-%m-%dT%H:%M:%SZ");
  auto epoch_seconds = std::mktime(&t) - _timezone; // only for Windows
  if (-1 == epoch_seconds) return std::chrono::system_clock::time_point();

  int y, m, d, h, M;
  float fraction_second = 0;
  sscanf(datetime_in_iso.c_str(), "%d-%d-%dT%d:%d:%fZ", &y, &m, &d, &h, &M, 
&fraction_second);
  float delta_s = fraction_second - t.tm_sec;

  auto truncated = std::chrono::system_clock::from_time_t(epoch_seconds);
  auto precise_time = truncated + std::chrono::microseconds(int(delta_s * 
1000000));

  return precise_time;
}

ref