C++ 日期和时间
C++ 日期和时间处理:<chrono>
Section titled “C++ 日期和时间处理:<chrono>”现代 C++ 通过 <chrono> 头文件提供了强大的日期和时间操作工具,该头文件在 C++11 中引入并在 C++20 中得到了显著增强。虽然 C++ 仍然支持旧的 C 风格 <ctime> 库以保持兼容性,但 <chrono> 提供了一种类型安全、灵活且更具表达力的方法。
<chrono> 中的关键概念:
Section titled “<chrono> 中的关键概念:”- 时钟(Clocks):表示时间点来源。常用的时钟包括
std::chrono::system_clock(墙上时钟时间,可能可调整)、std::chrono::steady_clock(单调时钟,适用于测量时间间隔)和std::chrono::high_resolution_clock(可用精度最高的时钟)。 - 时间点(Time Points):表示相对于时钟纪元(起始点)的特定时间点。
std::chrono::time_point通常从时钟的now()函数获得。 - 时长(Durations):表示一段时间跨度,例如“5 秒”或“200 毫秒”。
std::chrono::duration是一个模板类,存储一个计数和一个时间单位(例如,std::chrono::seconds、std::chrono::milliseconds、std::chrono::nanoseconds)。
获取当前时间
Section titled “获取当前时间”要获取当前的墙上时钟时间,请使用 std::chrono::system_clock::now():
#include <iostream>#include <chrono>
int main() { // Get the current time point from the system clock // 从系统时钟获取当前时间点 std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
// A time_point itself isn't directly printable in a human-readable format easily pre-C++20. // 在 C++20 之前,time_point 本身不容易直接以人类可读的格式打印。 // We often convert it for output or calculations. // 我们通常将其转换为用于输出或计算。
// Example: Get time since epoch (useful for some APIs) // 示例:获取自纪元以来经过的时间(对某些 API 有用) auto duration_since_epoch = now.time_since_epoch();
// Convert duration to seconds (as a double for fractional part) // 将时长转换为秒(使用 double 以包含小数部分) std::chrono::duration<double> seconds_since_epoch = duration_since_epoch; std::cout << "Seconds since epoch (Jan 1, 1970 UTC): " << seconds_since_epoch.count() << std::endl; // 自纪元(1970 年 1 月 1 日 UTC)以来的秒数:
return 0;}格式化日期和时间(C++20 之前的方法)
Section titled “格式化日期和时间(C++20 之前的方法)”在 C++20 向 <chrono> 添加格式化功能之前,获取人类可读输出的常用方法是将 system_clock::time_point 转换为旧的 C 类型 time_t,然后可以将其与 std::localtime(或用于 UTC 的 std::gmtime)和 std::put_time(来自 <iomanip>)等函数一起用于格式化输出。
注意:std::localtime 在所有平台上都不是线程安全的,因为它通常使用静态内部缓冲区。存在更安全的替代方法,如 localtime_s(Windows)或 localtime_r(POSIX),但它们是平台特定的。对于跨平台、线程安全的格式化,建议使用 C++20 的 <chrono> 或外部库,如 fmt 或 Boost.Locale。
以下是使用 std::put_time 的示例:
#include <iostream>#include <chrono>#include <ctime> // For time_t, tm, localtime // 用于 time_t, tm, localtime#include <iomanip> // For std::put_time // 用于 std::put_time#include <sstream> // For formatting to string (optional) // 用于格式化到字符串(可选)
int main() { // Get the current time point // 获取当前时间点 auto now = std::chrono::system_clock::now();
// Convert time_point to time_t // 将 time_point 转换为 time_t std::time_t now_c = std::chrono::system_clock::to_time_t(now);
// Convert time_t to tm struct for local time (potentially not thread-safe) // 将 time_t 转换为 tm 结构体以获取本地时间(可能不是线程安全的) // Note: std::localtime returns a pointer to a statically allocated struct. // 注意:std::localtime 返回指向静态分配的结构体的指针。 std::tm now_tm = *std::localtime(&now_c);
// Format the time using std::put_time // 使用 std::put_time 格式化时间 std::cout << "Current local time: "; // 当前本地时间: std::cout << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S %Z") << std::endl; // ISO 8601-like format + timezone // 类似 ISO 8601 格式 + 时区
// Example: Format date only // 示例:仅格式化日期 std::cout << "Current date: "; // 当前日期: std::cout << std::put_time(&now_tm, "%A, %B %d, %Y") << std::endl; // e.g., Tuesday, August 22, 2023 // 例如,星期二,2023 年 8 月 22 日
// For UTC time, use std::gmtime instead of std::localtime (also potentially not thread-safe) // 对于 UTC 时间,使用 std::gmtime 代替 std::localtime(也可能不是线程安全的) std::tm utc_tm = *std::gmtime(&now_c); std::cout << "Current UTC time: "; // 当前 UTC 时间: std::cout << std::put_time(&utc_tm, "%Y-%m-%d %H:%M:%S UTC") << std::endl;
return 0;}std::put_time 中使用的格式代码(如 %Y、%m、%d)与 C 函数 strftime 使用的格式代码相同。常用的代码包括:
%Y:带世纪的年份(例如,2023)%m:作为零填充十进制数字的月份(01-12)%d:作为零填充十进制数字的月中的日期(01-31)%H:作为零填充十进制数字的小时(24 小时制)(00-23)%M:作为零填充十进制数字的分钟(00-59)%S:作为零填充十进制数字的秒(00-60)%Z:时区名称或缩写(如果可用)%A:完整的星期几名称(例如,Sunday)%B:完整的月份名称(例如,January)
C++20 增强功能
Section titled “C++20 增强功能”C++20 通过 std::format(需要 <format> 头文件)添加了日历、时区和直接格式化功能,显著增强了 <chrono>。这避免了 time_t 转换及其相关的线程安全问题。
使用 C++20 std::format 的示例(需要符合 C++20 标准的编译器和标准库):
// Requires C++20 features // 需要 C++20 功能#include <iostream>#include <chrono>#include <format> // C++20 header // C++20 头文件
int main() { auto now = std::chrono.system_clock.now();
// Direct formatting using std::format // 使用 std::format 直接格式化 std::cout << std::format("Current local time: {:%Y-%m-%d %H:%M:%S %Z}\n", now); // 当前本地时间: std::cout << std::format("Current UTC time: {:%Y-%m-%d %H:%M:%S UTC}\n", now); // 当前 UTC 时间:
// Formatting with specific time zones (if supported by implementation) // 使用特定时区格式化(如果实现支持) try { auto zt = std::chrono::zoned_time{ "America/New_York", now }; std::cout << std::format("New York time: {:%Y-%m-%d %H:%M:%S %Z}\n", zt); // 纽约时间: } catch (const std::chrono::nonexistent_local_time& e) { std::cerr << "Time zone error: " << e.what() << '\n'; // 时区错误: } catch (const std::chrono::ambiguous_local_time& e) { std::cerr << "Time zone error: " << e.what() << '\n'; // 时区错误: }
return 0;}处理时长(Durations)
Section titled “处理时长(Durations)”<chrono> 使时长处理类型安全且直观。
#include <iostream>#include <chrono>#include <thread> // For std::this_thread::sleep_for // 用于 std::this_thread::sleep_for
int main() { using namespace std::chrono_literals; // Enable convenient literals like 10s, 200ms // 启用方便的字面量,如 10s, 200ms
auto start = std::chrono::high_resolution_clock::now();
// Simulate some work // 模拟一些工作 std::this_thread::sleep_for(1500ms); // Sleep for 1.5 seconds // 休眠 1.5 秒
auto end = std::chrono::high_resolution_clock::now();
// Calculate the duration // 计算时长 std::chrono::duration<double> elapsed_seconds = end - start; auto elapsed_milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Elapsed time: " << elapsed_seconds.count() << " s\n"; // 经过时间: std::cout << "Elapsed time: " << elapsed_milliseconds.count() << " ms\n"; // 经过时间:
return 0;}<chrono> 库是现代 C++ 处理日期和时间的标准。它提供类型安全的时钟、时间点和时长。虽然在 C++20 之前格式化需要转换为 C 类型,但现代 C++ 提供了直接格式化功能,使得日期/时间操作更安全、更方便。