C++ chrono库中的本地时间与系统时间计算对比

编程语言 2026-07-08

请简要阅读下面霍华德的精彩回答。

当在本地时间进行计算时,夏令时的影响会被忽略。举例来说,本地午夜再加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_timesys_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点。

我在若干地方尝试简化了语法。但我的简化并没有改变你的逻辑,除了在第二个示例中,使用系统时间计算 startend 的细节。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章