C++ chrono库中的本地时间与系统时间计算对比
请简要阅读下面霍华德的精彩回答。
当在本地时间进行计算时,夏令时的影响会被忽略。举例来说,本地午夜再加4小时始终是本地时间的4点,无论2点是否发生了UTC偏移变化。
当在系统时间进行计算时,对本地午夜加4小时会考虑到可能在2点发生或未发生的UTC偏移变化。结果将是按秒表测量的“实际时间”晚4个小时,而不是按本地时钟测量的4点。
--ORIGINAL QUESTION--
我有一个时间序列,我在任意点将其划分,这些点定义为某一周内从周日当地午夜起的秒数。
该序列中的时间戳记录为自1970-01-01 00:00:00 UTC起经过的毫秒数。
例如其中一个时间序列的时区为America/New_York,我们将第一段的边界定为周日午夜后经过64860秒,到147540秒之间——也就是周日6点到周一5点。
以下是开始时期:
- 周日,2025-11-02 — 注意本地时钟向后拨回了一小时,因夏令时结束,凌晨2点回拨至1点。
- 周日,2026-03-08 — 注意本地时钟向前拨快了一小时,凌晨2点跳到3点,因夏令时开始。
WEEK1
Midnight:
周日,2025-11-02,0:00:00,时区America/New_York(EDT)
对应的UTC时间是1762056000,偏移量 -04:00 (-14400秒)。——此日期处于夏令时。
START:
周日,2025-11-02,18:01:00,时区America/New_York(EST)
对应的UTC时间是1762124460,偏移量 -05:00 (-18000秒)。——不再是夏令时
END:
周一,2025-11-03,16:59:00,时区America/New_York(EST)
对应的UTC时间是1762207140,偏移量 -05:00 (-18000秒)。
WEEK2
Midnight:
周日,2026-03-08,0:00:00,时区America/New_York(EST)
对应的UTC时间是1772946000,偏移量 -05:00 (-18000秒)。——此日期不处于夏令时
START:
周日,2026-03-08,18:01:00,时区America/New_York(EDT)
对应的UTC时间是1773007260,偏移量 -04:00 (-14400秒)。——此日期处于夏令时
END:
周一,2026-03-09,16:59:00,时区America/New_York(EDT)
对应的UTC时间是1773089940,偏移量 -04:00 (-14400秒)。
下面的代码是我当前计算区间起始和结束时间戳的尝试:
#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();
// is UTC 1762056000000 == 1762056000000
std::cout << std::format("{:%Y-%m-%d %X} ", sunday) << duration_cast<milliseconds>(sunday.time_since_epoch()) <<std::endl;
// not in local time ?
auto start = sunday + seconds(64860);
auto end = sunday + seconds(147540);
// should be UTC 1762124460000 != 1762120860000
std::cout << std::format("{:%Y-%m-%d %X} ", start) << duration_cast<milliseconds>(start.time_since_epoch()).count() << std::endl;
// should be UTC 1762207140000 != 1762203540000
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时间?
解决方案
摘要:
我现在才意识到问题的根本原因:
你把 start.time_since_epoch()).count() 解释为 microseconds 的Unix Time,但 start 是一个 local_time,不是一个 sys_time。真正建模Unix Time的是 sys_time。
我在下方的回答中改为对每个时间戳报告Unix Time。它通过把 local_time(或 zoned_time)转换为 sys_time,然后再获取 .time_since_epoch() 来实现。
下面我展示了在本地时间和系统时间下对偏移量进行相加的过程。两者都是正确的。不同的应用场景有不同的需求。
你对 <chrono> 的用法是正确的。你只是误解了 local_time 与 sys_time 之间的区别。
#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
sys_time tp{timestamp};
// zoned timepoint
zoned_time zt{scheduleTimeZone, 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;
zt = local_date - days_ago;
return zt;
}
void
display(std::chrono::zoned_time<std::chrono::milliseconds> zt)
{
std::cout << std::format("{:%F %T %Z}", zt) << " which is "
<< std::format("{:%F %T %Z}", zt.get_sys_time())
<< "\n which has a Unix Time count of milliseconds of "
<< zt.get_sys_time().time_since_epoch().count() << '\n';
}
int
main()
{
using namespace std::chrono;
auto sunday = week_begining( 1762146000000ms, "America/New_York");
std::cout << "sunday = ";
display(sunday);
{
// local time
std::cout << "\nDo computation in local time\n";
zoned_time start{sunday.get_time_zone(), sunday.get_local_time() + 18h + 1min};
zoned_time end{sunday.get_time_zone(), sunday.get_local_time() + days{1} + 16h + 59min};
std::cout << "start = ";
display(start);
std::cout << "end = ";
display(end);
}
{
// sys time
std::cout << "\nDo computation in system time\n";
zoned_time start{sunday.get_time_zone(), sunday.get_sys_time() + 18h + 1min};
zoned_time end{sunday.get_time_zone(), sunday.get_sys_time() + days{1} + 16h + 59min};
std::cout << "start = ";
display(start);
std::cout << "end = ";
display(end);
}
}
这个程序输出:
sunday = 2025-11-02 00:00:00.000 EDT which is 2025-11-02 04:00:00.000 UTC
which has a Unix Time count of milliseconds of 1762056000000
Do computation in local time
start = 2025-11-02 18:01:00.000 EST which is 2025-11-02 23:01:00.000 UTC
which has a Unix Time count of milliseconds of 1762124460000
end = 2025-11-03 16:59:00.000 EST which is 2025-11-03 21:59:00.000 UTC
which has a Unix Time count of milliseconds of 1762207140000
Do computation in system time
start = 2025-11-02 17:01:00.000 EST which is 2025-11-02 22:01:00.000 UTC
which has a Unix Time count of milliseconds of 1762120860000
end = 2025-11-03 15:59:00.000 EST which is 2025-11-03 20:59:00.000 UTC
which has a Unix Time count of milliseconds of 1762203540000
当计算在本地时间下进行时,夏令时的影响会被忽略。例如,本地午夜加4小时始终是本地时间的4点,无论2点是否发生了UTC偏移变化。
当计算在系统时间下进行时,对本地午夜加4小时会考虑到可能在2点发生或未发生的UTC偏移变化。结果将是按秒表记录的实际时间晚4个小时,而不是按本地时钟测得的4点。
我在若干地方尝试简化了语法。但我的简化并没有改变你的逻辑,除了在第二个示例中,使用系统时间计算 start 和 end 的细节。