使用C++ Chrono计算本地时间区间对应的UTC时间
问题
我有一组时间序列,需要以每周周日午夜本地时间为起点的秒数划分区间。序列中的时间戳为UTC 1970年1月1日午夜以来的毫秒数。以America/New_York时区为例,第一个区间为周日午夜后64860秒至147540秒(即周日18:01至周一16:59),需处理2025年11月2日(夏令时结束)、2026年3月8日(夏令时开始)这两个特殊周的时间转换。
当前使用C++ Chrono编写的代码计算出的UTC时间戳不符合预期,具体代码及错误输出如下:
#include <iostream> #include <chrono> #include <format> auto week_begining(std::chrono::milliseconds timestamp, std::string_view scheduleTimeZone) { // get zoned_time for previous SUNDAY 00:00 local time using namespace std::chrono; // timepoint based on utc auto tp = system_clock::time_point( timestamp ); // zoned timepoint auto tz = locate_zone(scheduleTimeZone); std::chrono::zoned_time zt{tz, tp}; auto local_time = zt.get_local_time(); auto local_date = floor<days>( local_time ); // count back to Sunday auto days_ago = weekday{local_date} - Sunday; auto sunday = local_date - days_ago; return std::chrono::zoned_time{tz, std::chrono::local_days{sunday} }; } int main() { using namespace std::chrono; auto sunday = week_begining( milliseconds(1762146000000), "America/New_York").get_sys_time(); std::cout << std::format("{:%Y-%m-%d %X} ", sunday) << duration_cast<milliseconds>(sunday.time_since_epoch()) <<std::endl; auto start = sunday + seconds(64860); auto end = sunday + seconds(147540); std::cout << std::format("{:%Y-%m-%d %X} ", start) << duration_cast<milliseconds>(start.time_since_epoch()).count() << std::endl; std::cout << std::format("{:%Y-%m-%d %X} ", end) << duration_cast<milliseconds>(end.time_since_epoch()).count() << std::endl; return 0; }
错误输出:
2025-11-02 04:00:00 1762056000000ms -- correct 2025-11-02 22:01:00 1762120860000 -- incorrect this has returned 2nd @ 5pm 2025-11-03 20:59:00 1762203540000 -- incorrect this has returned 3rd @ 4pm
请问如何正确计算本地时间区间对应的UTC时间?
解决方案
错误原因
你的代码错误在于:将周日午夜对应的UTC时间点直接加上秒数偏移,这是在UTC时间轴上做计算,没有考虑本地时区的夏令时切换。比如2025年11月2日纽约时区结束夏令时,本地时间会回拨1小时,UTC时间轴的偏移无法反映这个本地时间的变化,导致计算出的UTC时间对应错误的本地时刻。
正确的思路应该是:在本地时间轴上基于周日午夜计算偏移,再将本地时间转换为UTC时间,这样Chrono会自动处理夏令时的时区转换逻辑。
修改后的代码
#include <iostream> #include <chrono> #include <format> auto week_beginning_local(std::chrono::milliseconds timestamp, std::string_view scheduleTimeZone) { using namespace std::chrono; // 将UTC时间戳转换为目标时区的本地时间 auto tp = system_clock::time_point(timestamp); auto tz = locate_zone(scheduleTimeZone); zoned_time zt{tz, tp}; auto local_time = zt.get_local_time(); auto local_date = floor<days>(local_time); // 回退到上一个周日的本地午夜 auto days_ago = weekday{local_date} - Sunday; local_days sunday_local{local_date - days_ago}; // 返回本地时间的周日午夜(而非转换后的UTC时间) return local_time_point<system_clock::duration>(sunday_local); } int main() { using namespace std::chrono; const auto tz = locate_zone("America/New_York"); // 获取目标周的本地周日午夜 auto sunday_local = week_beginning_local(milliseconds(1762146000000), "America/New_York"); // 在本地时间轴上计算区间起止 auto start_local = sunday_local + seconds(64860); auto end_local = sunday_local + seconds(147540); // 将本地时间转换为UTC时间点 zoned_time start_zt{tz, start_local}; zoned_time end_zt{tz, end_local}; zoned_time sunday_zt{tz, sunday_local}; // 输出结果 auto sunday_sys = sunday_zt.get_sys_time(); std::cout << std::format("{:%Y-%m-%d %X} ", sunday_sys) << duration_cast<milliseconds>(sunday_sys.time_since_epoch()) << "\n"; auto start_sys = start_zt.get_sys_time(); std::cout << std::format("{:%Y-%m-%d %X} ", start_sys) << duration_cast<milliseconds>(start_sys.time_since_epoch()).count() << "\n"; auto end_sys = end_zt.get_sys_time(); std::cout << std::format("{:%Y-%m-%d %X} ", end_sys) << duration_cast<milliseconds>(end_sys.time_since_epoch()).count() << "\n"; return 0; }
代码说明
week_beginning_local函数:不再返回转换后的UTC时间,而是返回本地时间轴上的周日午夜local_time_point,确保后续的秒数偏移是基于本地时间计算。- 本地时间偏移:直接在
local_time_point上加上64860和147540秒,得到本地时间的区间起止点。 - 时区转换:通过
zoned_time将本地时间转换为UTC时间,Chrono会自动处理夏令时切换的时区偏移计算,比如2025年11月2日纽约时区的回拨逻辑。
正确输出示例
针对2025年11月的夏令时结束周,正确输出应为:
2025-11-02 04:00:00 1762056000000ms 2025-11-02 23:01:00 1762124460000 -- 对应纽约本地时间11月2日18:01 2025-11-03 20:59:00 1762203540000 -- 对应纽约本地时间11月3日16:59(夏令时结束后UTC偏移变为-5)
内容的提问来源于stack exchange,提问作者ArchNemSyS
相关产品推荐
相关产品推荐

