我想计算从一天开始到现在的秒数。问题是std::chrono::floor
给我的是UTC的daystart,而不是我的本地时区。比较一下:
演示:
#include <time.h>
#include <cstdio>
#include <iostream>
#include <cstdint>
#include <chrono>
#include <ctime>
int main() {
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0", 1);
tzset();
const auto tp_daystart = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now());
const auto tp_now = std::chrono::system_clock::now();
const auto daysec = std::chrono::duration_cast<std::chrono::seconds>(tp_now - tp_daystart);
std::time_t ttp = std::chrono::system_clock::to_time_t(tp_now);
std::time_t ttp_s = std::chrono::system_clock::to_time_t(tp_daystart);
std::cout << "time start: " << std::ctime(&ttp_s) << " / time now: " << std::ctime(&ttp) << "seconds since start of day = " << daysec.count() << "\n";
}
这产生:
time start: Mon May 1 02:00:00 2023
/ time now: Mon May 1 16:26:04 2023
seconds since start of day = 51964
从一天开始的秒数当然是错误的,因为它计算的是从凌晨2点到下午4点,而实际上应该从00:00:00
开始。,因为我确实想计算这个时区自这一天开始以来的秒数!我该怎么做?
2条答案
按热度按时间u7up0aaq1#
这个问题实际上有一些棘手的细节,当像夏令时这样的奇怪事情使事情复杂化时,必须做出决定,确定你希望发生什么。但无论你想发生什么,都可以在chrono中完成。
我假设你想要从一天开始的物理秒。这意味着如果在午夜和现在之间存在UTC偏移改变(e.例如,减去了一个小时),将考虑该减法。
这意味着我们需要:
1.现在获取UTC时间。
1.获取 * 本地 * 午夜的UTC时间。
1.减去两个UTC时间。
由于您对秒精度感兴趣,因此我们将从一开始就截断它,然后不再担心它:
出于可读性的考虑,我使用本地
using directives
来减少冗长。接下来获取对应于
utc_now
的本地时间:current_zone()
查找当前设置的时区。而zoned_time
只是一个方便的“对”,它将time_zone const*
和sys_time
放在一起。可以使用此结构提取本地时间。或者你可以直接格式化本地时间:对我来说,这只是输出我的本地时间和日期:
现在,要获取本地午夜,必须在本地时间(而不是系统时间)截断为
days
:这将提取本地时间,将其截断为
days
,然后将该本地时间分配回zoned_time
。这将更改基础UTC时间点(sys_time),但不会更改时区。现在您可以再次格式化
local
:示例输出:
最后,从本地午夜提取UTC时间(存储在
local
中),并从起始UTC时间中减去它:示例输出:
还有一个问题如果存在UTC偏移量变化,导致本地时间完全跳过本地午夜,该怎么办?或者如果有两个当地的午夜呢?
你想怎么处理?
有几种可能性:
1.您可以忽略这种可能性,因为您确信本地时区不会这样做。在这种情况下,上面的代码是好的。如果发生这种情况,将本地午夜赋值回
local
的代码行将抛出异常。1.如果有两个午夜,你想选择第一个,如果没有午夜,你想从午夜之后的第一个当地时间开始计数。例如,如果本地时间从23:30跳到00:30,则从本地时间00:30开始计数。
在这种情况下,更改:
致:
这一点:
days
精度。zoned_time
,其时区与local
相同。如果您在移动移动终端上,您不希望第二次调用current_zone()
。choose::earliest
选择第一个本地时间,以防存在两个从本地到UTC的Map。这也将Map到与本地时间间隙(午夜零点)相关联的UTC时间点。zoned_time
分配回local
。现在,异常将永远不会被抛出,并且您将始终从今天的第一个时刻开始计数秒,即使也有昨天的时间也被计数。
如果你想要第二个午夜,这样就不会有昨天的比特数,那么把
choose::earliest
改为choose::latest
。如果您不想实际计算物理秒数,而是“日历秒数”,那么就用本地时间而不是UTC来计算。矛盾的是,这将不涉及在白天更改UTC偏移量的复杂性,因此更简单。在此计算中,4am将始终是午夜后的4小时,即使在2am有UTC偏移更改,将本地时间设置回1am。
在这个版本中,本地时间永远不会Map回UTC,因此没有机会发现所述Map不是唯一的,并随后抛出异常。
需要说明的是,这段代码的三个不同版本几乎总是给予相同的结果。只有当UTC偏移量在“现在”和前一个当地午夜之间发生变化时,这些不同的版本才会给予不同的结果。
ep6jt1vc2#
您正在寻找
std::chrono::time_zone
。一旦你有了一个有效的
time_zone
,这就很容易了在c++20中添加
time_zone
。您可以直接在系统数据库中搜索特定的时区,或者使用您的进程的当前时区(如上所述)。你必须接受失败,因为有时候一个系统没有时区。