You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++20:zoned_time与本地时间组件互转及时间差计算

C++ chrono:zoned_time与时间组件转换及时间跨度计算问题

问题

  1. 如何在zoned_time与时间组件(年、月、日、时、分、秒等)之间进行双向转换(需考虑夏令时DST)?
  2. 计算两个zoned_time之间经过时间的正确方法是什么?

已尝试的操作

可以从时钟获取当前时间生成zoned_time,但对其操作存在困难。参考Stack Overflow代码实现了时间组件提取:

#include <chrono>

int
main()
{
    using namespace std::chrono;
    auto tp = zoned_time{current_zone(), system_clock::now()}.get_local_time();
    auto dp = floor<days>(tp);
    year_month_day ymd{dp};
    hh_mm_ss time{floor<milliseconds>(tp-dp)};
    auto y = ymd.year();
    auto m = ymd.month();
    auto d = ymd.day();
    auto h = time.hours();
    auto M = time.minutes();
    auto s = time.seconds();
    auto ms = time.subseconds();
}

这段代码可获取当前日期并修改时间组件,但无法将修改后的组件转回zoned_time。尝试了以下写法:

auto rec = zoned_time(current_zone(), std::chrono::local_days{y / m / d} + h + M + s + ms);

if (tp_s == rec) std::cout << "equal" << std::endl;
else {
    std::cout << "different" << std::endl;
    std::cout << tp  << "\t" << rec << std::endl;
}

输出结果:

different
2023-03-21 10:54:30.0128147 2023-03-21 10:54:30.012 GMT

同时对std::chrono::local_days{y / m / d} + h + M + s + ms的含义存在困惑:该加法是时间间隔偏移还是直接设置时间字段?夏令时切换场景下,添加5小时与直接设置小时为5是完全不同的操作。

最后认为计算真实时间跨度的方法是:

auto diff = duration_cast<chr::minutes>(tp_s.get_sys_time() - rec.get_sys_time());

但不清楚以下代码的含义:

auto diff = duration_cast<chr::minutes>(tp_s.get_local_time() - rec.get_local_time());

解答

1. zoned_time与时间组件的双向转换

从zoned_time提取时间组件

你使用的提取代码是完全正确的,核心逻辑如下:

  • 调用zoned_time.get_local_time()获取对应时区的本地时间点(local_time类型)
  • 通过floor<days>(tp)截断时间点到日期边界,将结果转换为year_month_day即可提取年、月、日组件
  • 计算tp - dp得到当日的时间偏移量,用hh_mm_ss封装后可提取时、分、秒及亚秒组件

将修改后的组件转回zoned_time

你尝试的local_days{y/m/d} + h + M + s + ms是正确的构造方式,这里的加法是直接设置时间字段,而非时间间隔偏移:

  • local_days{y/m/d}生成的是指定年月日当天午夜0点的本地时间点
  • 后续添加的小时、分钟等时长,是直接构造出对应时分秒的本地时间点,等同于直接设置时间字段,和夏令时切换时的时间偏移操作完全不同

至于输出的时间精度差异,是因为原始tp的精度是微秒(10:54:30.0128147),而你构造时仅保留了毫秒精度(hh_mm_ss<milliseconds>的亚秒是3位)。保持精度一致即可解决:

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std::chrono;
    auto zt = zoned_time{current_zone(), system_clock::now()};
    auto tp = zt.get_local_time();
    auto dp = floor<days>(tp);
    year_month_day ymd{dp};
    // 用microseconds精度匹配原始时间点
    hh_mm_ss time{floor<microseconds>(tp - dp)};

    // 修改时间组件示例:设置为当日5点整
    auto new_h = hours{5};
    auto new_M = minutes{0};
    auto new_s = seconds{0};
    auto new_subs = microseconds{0};

    // 构造新的本地时间点
    auto new_local_tp = local_days{ymd.year()/ymd.month()/ymd.day()} + new_h + new_M + new_s + new_subs;
    auto new_zt = zoned_time{current_zone(), new_local_tp};

    std::cout << "原始时间:" << zt << "\n";
    std::cout << "修改后时间:" << new_zt << "\n";
}

关于夏令时的处理:zoned_time在构造时会自动处理夏令时带来的特殊情况:

  • 夏令时回退时,同一个本地时间可能对应两个系统时间,zoned_time默认选择较早的那个
  • 夏令时前进时,不存在的本地时间会被自动调整为下一个有效时间

2. 计算两个zoned_time的时间跨度

正确方法:使用系统时间计算

你提到的tp_s.get_sys_time() - rec.get_sys_time()是计算真实时间跨度的正确方式:

  • get_sys_time()返回的是system_clock时间点,对应UTC时间的映射,完全不受夏令时影响,代表物理时间的真实流逝
  • 两个系统时间点的差值,就是两个时间之间真实经过的时间,结果准确可靠

本地时间差值的含义

tp_s.get_local_time() - rec.get_local_time()计算的是本地时钟显示的时间差,不能代表真实的时间流逝:

  • 夏令时前进时,本地时钟跳过1小时,此时两个跨切换点的本地时间差值会比真实时间少1小时
  • 夏令时回退时,本地时钟重复1小时,同一个本地时间可能对应两个不同的系统时间,差值可能为0或3600秒,完全无法反映真实时间

因此,这种方法仅适用于计算本地时钟的显示差值,不能用于真实时间跨度的计算。

内容的提问来源于stack exchange,提问作者AndrewBloom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 03:33:13