gla*_*des 2 c++ floor c++-chrono
我想计算自一天开始以来的秒数。问题是,它std::chrono::floor给了我 UTC 的日开始时间,而不是我本地时区的日开始时间。比较一下:
演示:
#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";
}
Run Code Online (Sandbox Code Playgroud)
这产生:
time start: Mon May 1 02:00:00 2023
/ time now: Mon May 1 16:26:04 2023
seconds since start of day = 51964
Run Code Online (Sandbox Code Playgroud)
从一天开始算起的秒数当然是错误的,因为它计算的是从凌晨 2 点到下午 4 点,而实际上应该从 开始00:00:00。,因为我确实想计算该时区自这一天开始以来的秒数!我该如何做到这一点?
这个问题实际上有一些棘手的细节,当夏令时等奇怪的事情使事情变得复杂时,必须准确地做出你想要发生的事情的决定。但无论你想发生什么,都可以在计时中完成。
我假设您需要从一天开始以来的物理秒数。这意味着,如果午夜和现在之间存在 UTC 偏移量变化(例如减去一小时),则将考虑此减法。
这意味着我们需要:
由于您对秒精度感兴趣,我们将从一开始就截断它,然后不再担心它:
#include <chrono>
#include <format>
#include <iostream>
int
main()
{
using namespace std;
using namespace chrono;
auto utc_now = floor<seconds>(system_clock::now());
Run Code Online (Sandbox Code Playgroud)
出于可读性的目的,我使用 localusing directives来减少冗长。
接下来获取对应的本地时间utc_now:
auto local = zoned_time{current_zone(), utc_now};
Run Code Online (Sandbox Code Playgroud)
current_zone()查找当前设置的时区。Andzoned_time只是一个方便的“对”,将 atime_zone const*和 asys_time结合在一起。可以使用此结构来提取当地时间。或者您可以直接格式化本地时间:
cout << "time now : " << format("{:%a %b %e %T %Y}", local) << '\n';
Run Code Online (Sandbox Code Playgroud)
对我来说,这只是输出我的本地时间和日期:
time now : Mon May 1 10:56:07 2023
Run Code Online (Sandbox Code Playgroud)
现在要获取当地午夜,截断必须days发生在当地时间,而不是系统时间:
local = floor<days>(local.get_local_time());
Run Code Online (Sandbox Code Playgroud)
这会提取本地时间,将其截断为days,然后将该本地时间分配回zoned_time. 这将更改基础 UTC 时间点 (sys_time),但不会更改时区。
现在您可以local再次格式化:
cout << "time start: " << format("{:%a %b %e %T %Y}", local) << '\n';
Run Code Online (Sandbox Code Playgroud)
输出示例:
time start: Mon May 1 00:00:00 2023
Run Code Online (Sandbox Code Playgroud)
最后,从本地午夜(存储在local)中提取 UTC 时间,并从起始 UTC 时间中减去它:
auto delta = utc_now - local.get_sys_time();
cout << "seconds since start of day = " << delta << '\n';
Run Code Online (Sandbox Code Playgroud)
输出示例:
seconds since start of day = 39367s
Run Code Online (Sandbox Code Playgroud)
还有一个更复杂的问题:如果存在 UTC 偏移量更改导致当地时间完全跳过当地午夜怎么办?或者如果有两个当地午夜怎么办?
你想怎么处理?
有几种可能:
您可以忽略这种可能性,因为您确定您当地的时区永远不会这样做。在这种情况下,上面的代码就可以了。如果发生这种情况,将本地午夜分配回 的代码行将引发异常local。
如果有两个午夜,您想选择第一个,如果午夜为零,您想从午夜之后的第一个本地时间开始计数。例如,如果当地时间从 23:30 跳到 00:30,则从当地时间 00:30 开始计数。
在这种情况下更改:
local = floor<days>(local.get_local_time());
Run Code Online (Sandbox Code Playgroud)
到:
local = zoned_time{local.get_time_zone(),
floor<days>(local.get_local_time()),
choose::earliest};
Run Code Online (Sandbox Code Playgroud)
这:
days精确的时间。zoned_time与 相同 time_zone 的新对象local。current_zone()如果您使用移动的移动设备,您不想再次拨打电话。choose::earliest选择第一个本地时间,以防存在从本地到 UTC 的两个映射。这也将映射到与本地时间间隙(零午夜)相关的 UTC 时间点。zoned_time回local.现在永远不会抛出异常,并且您将始终从今天的第一个时刻开始计算秒数,即使昨天也计算了一些时间。
如果您想要第二个午夜,这样就不会计算昨天的内容,然后更改choose::earliest为choose::latest。
如果您不想实际计算物理秒,而是想计算“日历秒”,请在本地时间(而不是 UTC)中进行算术。矛盾的是,这不会涉及白天更改 UTC 偏移量的复杂性,因此会更简单。在此计算中,凌晨 4 点始终是午夜后 4 小时,即使凌晨 2 点 UTC 偏移量发生变化,将当地时间设置回凌晨 1 点。
auto utc_now = floor<seconds>(system_clock::now());
auto local_now = zoned_time{current_zone(), utc_now}.get_local_time();
auto local_midnight = floor<days>(local_now);
auto delta = local_now - local_midnight;
cout << "time now : " << format("{:%a %b %e %T %Y}", local_now) << '\n';
cout << "time start: " << format("{:%a %b %e %T %Y}", local_midnight) << '\n';
cout << "seconds since start of day = " << delta << '\n';
Run Code Online (Sandbox Code Playgroud)
在此版本中,本地时间永远不会映射回 UTC,因此没有机会发现所述映射不唯一并随后引发异常。
需要明确的是,该代码的所有三个不同版本几乎总是给出相同的结果。只有当 UTC 偏移量在“现在”和上一个本地午夜之间发生变化时,这些不同的版本才会给出不同的结果。
| 归档时间: |
|
| 查看次数: |
892 次 |
| 最近记录: |