c++20的日历和时区库怎么用 库的史诗级增强【现代C++】

c++kquote>C++20 原生支持日历与时区:引入 year/month/day 等民用时间类型,sys_days 表示 UTC 零点,locate_zone 支持 IANA 时区, 和 parse 实现格式化与解析,需注意有效性检查和时区数据依赖。

库的史诗级增强【现代c++】">

C++20 的 确实带来了日历和时区的原生支持,不再是靠第三方库(如 Howard Hinnant 的 date 库)打补丁了——它直接把 date 库的核心逻辑“收编”进了标准。用起来清晰、类型安全、无运行时依赖,但需要理解几个新概念才能真正上手。

核心新增类型:年月日 + 时分秒 = civil time

C++20 引入了 std::chrono::yearstd::chrono::monthstd::chrono::daystd::chrono::year_month_day 等“日历类型”,它们不带时区、不带偏移,纯粹表示民用时间(civil time)。

例如:

  • std::chrono::year_month_day{2025y/3/15} 表示 2025 年 3 月 15 日(注意 2025y 是字面量后缀,需包含
  • auto ymd = sys_days{2025y/3/15}; 把日期转成系统时间点(即自 1970-01-01 的天数)
  • ymd.time_since_epoch() 返回 days 类型,可转为整数天数

把日期转成精确时间点:用 sys_dayssys_time

std::chrono::sys_dayssystem_clock::time_point 的“天粒度”别名,代表 UTC 时间轴上的某一天零点(00:00:00 UTC)。

想得到带具体时刻的时间点?组合使用:

  • auto tp = sys_days{2025y/3/15} + 14h + 30min; → 得到 2025-03-15 14:30:00 UTC
  • auto tp_local = clock_cast(tp); → 转成本地时区(非 UTC,但仍是本地系统时区,不含 IANA 时区名)

⚠️ 注意:local_t 不是某个具体时区(比如 “Asia/Shanghai”),只是当前系统的本地时区(由 std::chrono::current_zone() 返回的时区对象决定)。

真正用上 IANA 时区(如 “America/New_York”、“Asia/Shanghai”)

C++20 支持完整的 IANA 时区数据库,但需满足两个前提:

  • 编译器/标准库实现必须提供时区数据(libstdc++ ≥ 13、libc++ ≥ 16、MSVC 2025 17.5+ 已支持)
  • 运行环境需有可用的时区数据库(Linux/macOS 通常自带;Windows 需启用 _ENABLE_EXTENDED_ALIGNED_STORAGE 或使用 ICU)

典型用法:

  • const auto tz = std::chrono::locate_zone("Asia/Shanghai"); → 获取时区指针
  • auto tp_utc = tz->to_sys(2025y/3/15 / 14h + 30min); → 把本地时间(东八区)转为 UTC 时间点
  • auto tp_local = tz->to_local(tp_utc); → 反向转换(含夏令时自动处理)
  • tz->get_info(tp_utc).offset; → 查该时刻的 UTC 偏移(如 +0800)

格式化与解析: 配合 std::chrono

C++20 的 直接支持日历和时区类型:

  • std::format("{:%Y-%m-%d %H:%M %Z}", tp_utc); → 输出 "2025-03-15 06:30 UTC"
  • std::format("{:%F %T %Z}", tp_local_in_shanghai); → 输出 "2025-03-15 14:30:00 CST"
  • 解析需用 std::chrono::parse(C++20 提供):
  • std::istringstream s{"2025-03-15 14:30"};
    std::chrono::sys_seconds tp;
    s >> std::chrono::parse("%F %T", tp);

⚠️ 注意:parse 默认按 UTC 解析;若要按特定时区解析,需先转成对应时区时间再调用 to_sys

不复杂但容易忽略:所有日历操作默认不检查有效性(如 2025y/2/30),但可通过 .ok() 成员函数验证,例如 (2025y/2/30).ok() 返回 false