首页 > 解决方案 > Java 到 C++ UTC 时间 - 以秒为单位查找 Utc 区域时间偏移

问题描述

我需要使用无符号字符的纯 ByteBuffer 的最小长度将 UTC 日期时间实例从 java 序列化为 c++。我需要能够支持最小纳秒精度的时间点。

从 java 方面,我查看了 ZonedDateTime 和 OffsetDateTime 类,并查看了它们用于存储时间的原语类型,总共等于 17 个字节,这有点多。

请注意,我在这里并没有转移区域本身,因为我还没有找到将这些区域字符串从 IANA 映射到整数的方法。因此,我想区域偏移量就足够了。由于买方必须在将其时间点发送到服务器之前将其时间点转换为特定的偏移量,因此可以说这将是 UTC 0。我正在寻找一种解决本地时间问题的解决方案,即不涉及 NTP 服务器!

java端看起来像这样:

        // 4 byte
        Integer year = offsetDateTime.getYear();
        // 1 byte           
        Integer month = offsetDateTime.getMonthValue();
        // 1 byte
        Integer day = offsetDateTime.getDayOfMonth();
        // 1 byte
        Integer hour = offsetDateTime.getHour();
        // 1 byte
        Integer minutes = offsetDateTime.getMinute();
        // 1 byte
        Integer seconds = offsetDateTime.getSecond();
        // 4 byte
        Integer nanoSeconds = offsetDateTime.getNano();
        // 4 byte
        Integer utcZoneTimeTotalSecondsOffset = offsetDateTime.getOffset().getTotalSeconds(); 

在 c++ 方面,我需要做同样的事情,并且能够从上面的所有这些整数中构造一个时间点。除了 utcOffset 即最后一个整数之外,我已经找到了如何获取上述信息的方法。

我想使用 chrono 来消耗这个缓冲区并从中实例化一个时间点。但是我无法找到一种方法来获取 C++ 中的时间偏移量。如何使用 chrono 获取时间偏移,然后根据上述信息构造一个时间点?

auto now = std::chrono::system_clock::now();
time_t itt = std::chrono::system_clock::to_time_t(now);

tm utc_tm = *gmtime(&itt);

std::cout << utc_tm.tm_year + 1900 << '-';
std::cout << utc_tm.tm_mon + 1 << '-';
std::cout << utc_tm.tm_mday << ' ';
std::cout << utc_tm.tm_hour << ':';
std::cout << utc_tm.tm_min << ':';
std::cout << utc_tm.tm_sec << '\n';

? wher is the time zoneOffset here ?

如果我使用

 utc_tm.tm_gmtoff 

这给了我错误的信息。至少在我的情况下。所以我相信 gmtoff 不是要走的路,但如果 gmtoff 不是路,那又是什么呢?

标签: c++utcchrono

解决方案


不是一个完整的答案,但这是使用Howard Hinnant 的免费开源时区库序列化 C++ 本地时间点的一种方法。

#include "date/tz.h"
#include <iostream>
#include <sstream>

std::string
to_string(date::zoned_time<std::chrono::nanoseconds> t)
{
    using namespace std;
    using namespace date;
    ostringstream out;
    out << t.get_sys_time().time_since_epoch().count()
        << ' ' << t.get_time_zone()->name();
    return out.str();
}

date::zoned_time<std::chrono::nanoseconds>
from_string(const std::string& s)
{
    using namespace std;
    using namespace std::chrono;
    using namespace date;
    istringstream in{s};
    long long ns;
    std::string tz_name;
    in >> ns >> tz_name;
    return {tz_name, sys_time<nanoseconds>{nanoseconds{ns}}};
}

int
main()
{
    using namespace std;
    using namespace std::chrono;
    using namespace date;
    zoned_time<nanoseconds> zt{current_zone(), system_clock::now()};
    cout << zt << '\n';
    auto s = to_string(zt);
    cout << s << '\n';
    zt = from_string(s);
    cout << zt << '\n';
}

我使用 astd::string来表示可能通过网络发送的序列化。

此示例首先使用计算机的当前本地时区和当前 UTC 时间 main创建存储在 中的纳秒精度本地时间点。首先简单地打印这个时间戳,对我来说只是输出:date::zoned_time<nanoseconds>main

2019-09-11 12:37:04.846272000 EDT

这是我当前的本地日期/时间和时区缩写。

接下来,程序将其转换为包含少量(但不一定是最少)ASCII 字符的字符串,以完整描述本地时间点。我选择了格式:

<Number of nanoseconds since epoch> <IANA time zone name>

在此示例中,该字符串是:

1568219824846272000 America/New_York

string是在 中形成的to_string,它简单地流出了系统时间的“自纪元以来的时间”,zoned_time。然后添加一个空格,并流出时区的名称。

from_string通过读取纳秒数,然后读取时区名称来反转此操作。然后,它zoned_time<nanoseconds>通过将时区名称配对并sys_time<nanoseconds>与解析的整数形成 a 来形成 a。

main打印出解析zoned_time以确保我们有一个无损的往返:

2019-09-11 12:37:04.846272000 EDT

为了获得包含本地时间的无损转换,确实需要传输 IANA 时区名称,而不仅仅是当前的 UTC 偏移量。仅使用 UTC 偏移量,就可以恢复准确的 UTC 时间点。但是不能执行任何本地时间算术,或与其他本地时间点进行比较,因为人们无法知道何时更改 UTC 偏移量的规则。只有使用完整的 IANA 时区名称,才能传递有关这些规则的信息。


推荐阅读