C++ 日期 & 时间

在现代 C++ 中,处理日期和时间主要有两大途径:

  1. 传统 C 接口(<ctime>
  2. C++11 及之后的 <chrono> 库(并在 C++20 增强了日历/格式化支持)

下面分别介绍它们的用法,并结合示例说明如何获取、表示、计算、格式化和解析时间。


1. 传统 C 接口(<ctime>

#include <ctime>
#include <iostream>

int main() {
    // 获取当前时间(自 1970-01-01 00:00:00 UTC 起的秒数)
    std::time_t t = std::time(nullptr);
    
    // 转为本地时间结构
    std::tm local = *std::localtime(&t);
    
    // 格式化输出
    char buf[64];
    std::strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S", &local);
    std::cout << "本地时间: " << buf << "\n";
    
    // UTC 时间
    std::tm utc = *std::gmtime(&t);
    std::strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S UTC", &utc);
    std::cout << "UTC 时间: " << buf << "\n";
}
  • std::time_t:通常是秒级别的整数类型。
  • std::localtime / std::gmtime:将 time_t 转为 std::tm(年、月、日、时、分、秒等字段)。
  • std::strftime:格式化 std::tm,支持常见 %Y, %m, %d, %H, %M, %S 等控制符。

缺点:精度到秒,不便做高精度计时;易受时区/夏令时影响;接口不够类型安全。


2. C++11 <chrono> 库

2.1 时钟(Clocks)、时刻(Time Point)与持续时间(Duration)

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    // 以高精度时钟获取当前时刻
    auto t0 = high_resolution_clock::now();
    
    // 模拟耗时操作
    for (volatile int i = 0; i < 1000000; ++i) {}
    
    auto t1 = high_resolution_clock::now();
    
    // 计算两次时刻之间的持续时间
    auto dt = t1 - t0;  // dt 是一个 duration
    
    // 转换为毫秒
    auto ms = duration_cast<milliseconds>(dt).count();
    std::cout << "耗时: " << ms << " ms\n";
}
  • 时钟system_clocksteady_clockhigh_resolution_clock
    • system_clock:可转换为 time_t,对应系统时间。
    • steady_clock:单调递增,适合做精确计时,不受系统时间调整影响。
  • time_point<Clock>:某个时刻;与 Clock::now() 配合使用。
  • duration<Rep, Period>:时间间隔;可通过 duration_cast 转换到所需单位。

2.2 时间点与传统接口互转

// system_clock <-> time_t
auto now_tp = std::chrono::system_clock::now();
std::time_t now_t = system_clock::to_time_t(now_tp);
std::tm local = *std::localtime(&now_t);

// 从 time_t 回到 time_point
auto tp2 = system_clock::from_time_t(now_t);

3. C++20 日历与格式化(<chrono> 扩展)

C++20 在 <chrono> 中引入了对日历(calendar)和文本格式化的支持。

#include <chrono>
#include <iostream>
#include <format>

int main() {
    using namespace std::chrono;
    
    // 获取当前系统时刻并转换为日历日期
    auto today = floor<days>(system_clock::now());
    year_month_day ymd = today;
    
    std::cout << "日期: " 
              << int(ymd.year()) << "-" 
              << unsigned(ymd.month()) << "-" 
              << unsigned(ymd.day()) << "\n";
    
    // 格式化输出 datetime
    auto now = system_clock::now();
    std::cout << std::format("现在是 {:%Y-%m-%d %H:%M:%S}\n", now);
}
  • std::chrono::year_month_dayyear_month_weekday 等类型,支持日历运算(加减月、年等)。
  • std::format(C++20)结合 <chrono> 支持直接格式化输出时间点。

4. 解析日期字符串

4.1 传统方式:std::get_time

#include <sstream>
#include <iomanip>
#include <iostream>

int main() {
    std::string s = "2025-07-09 15:30:45";
    std::istringstream in(s);
    std::tm tm = {};
    in >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
    if (in.fail()) {
        std::cerr << "解析失败\n";
    } else {
        std::time_t t = std::mktime(&tm);
        std::cout << "解析后的 timestamp: " << t << "\n";
    }
}

4.2 C++20:std::chrono::parse(配合 <format> 扩展库)

(注意:目前 GCC/Clang 下部分支持库才有)

#include <chrono>
#include <iostream>
#include <format>   // 或 <chrono> 中的 parse

using namespace std::chrono;

int main() {
    std::string s = "2025-07-09";
    year_month_day ymd;
    std::istringstream in(s);
    in >> parse("%F", ymd);  // %F 等同于 %Y-%m-%d
    std::cout << "年: " << int(ymd.year())
              << " 月: " << unsigned(ymd.month())
              << " 日: " << unsigned(ymd.day()) << "\n";
}

5. 常见操作示例

操作示例代码片段
加/减 天数ymd += days{7};
比较两个时间点if (t1 < t2) …
定时执行(sleep)std::this_thread::sleep_for(milliseconds{100});
计算某月的天数days{year / month / 1}.end_of_month().day() (C++23)

6. 小结

  • 若仅需简单获取和格式化当前时间,传统 <ctime> 足够;
  • 若需高精度、类型安全的计时、时间算术,优先使用 <chrono>
  • 在 C++20 环境下,享受日历类型与直接格式化的便利;
  • 对复杂时区处理或日历算法需求,可考虑第三方库,如 Howard Hinnant date 库、ICU 等。

掌握以上内容,便能在 C++ 各种场景下灵活、精准地处理日期与时间。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注