【问题标题】:Java to C++ UTC time - finding the Utc Zone Time Offset in secondsJava 到 C++ UTC 时间 - 以秒为单位查找 Utc 区域时间偏移
【发布时间】:2019-09-11 13:11:06
【问题描述】:

我需要使用无符号字符的纯 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++ 中我们没有时区。直到 C++20 使用的时区是特定于实现的,之后它看起来应该是 UTC。
  • 在 C++ 中,最终产品是 UTC 时间点还是本地时间点?
  • 一个 UTC 时间点可以用 C++ 表示一个纳秒精度的时间点,总共使用 8 个字节,只要时间点大约在 1678 年到 2262 年的范围内。如果需要时区或UTC 偏移量,这将需要更多存储空间。
  • 为什么要处理偏移量?只需使用 UTC ZoneId 创建 OffsetDateTime 或 ZonedDateTime。
  • 如果所有客户端/服务器总是以 UTC 相互通信,那么比较时间戳就变得非常容易了。当然,除非出于某种原因,我们必须知道时间戳是否在客户端/服务器的本地时间打开/关闭期间。

标签: c++ utc chrono


【解决方案1】:

不是一个完整的答案,但这是一种使用 Howard Hinnant's free, open source time zone library 序列化 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';
}

我使用std::string 来表示序列化,它可能会通过网络发送。

此示例从 main 开始,使用计算机当前的本地时区和当前的 UTC 时间创建一个纳秒精度的本地时间点,存储在 date::zoned_time&lt;nanoseconds&gt; 中。 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&lt;nanoseconds&gt;,并与解析的整数形成sys_time&lt;nanoseconds&gt;

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

2019-09-11 12:37:04.846272000 EDT

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

【讨论】:

  • 霍华德,非常感谢您的回复,您知道是否有办法将“America/New_York”区域的字符串映射为整数?由于这个原因,我试图避免使用字符串表示来进行序列化,而是使用固定长度的整数。因为我想为 java 和 C++ 使用相同的 DTO 作为用于交换信息的结构。还有一个问题是你知道“date/tz.h”之前是否已经用 Webassembly 测试过吗?
  • 没有正式名称整数映射。但是,您可以创建自己的。目前有 593 个名称与时区相关联。其中一些名称实际上指的是同一个区域,但大多数操作系统将它们视为区域的不同副本。
  • 我不知道 tz.h 与 Webassembly 有任何用途。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-24
  • 1970-01-01
  • 2015-10-14
  • 1970-01-01
  • 1970-01-01
  • 2018-06-21
  • 2017-10-18
相关资源
最近更新 更多