代码之家  ›  专栏  ›  技术社区  ›  PeterT

为什么我的时间转换不一致?

  •  1
  • PeterT  · 技术社区  · 2 年前

    快速后台:我的应用程序访问多个数据源,这些数据源报告带有时间戳的数据,每个时间戳都报告为Unix epoch秒,例如数据报告 1656331210 。用户可以输入请求的时间作为ISO日期字符串进行报告,例如 2022-06-27T12:00:10Z 。这两个值被认为是等效的。我的问题是不断地来回转换。

    我的功能 formatTimestamp 如下所示,将给定的时间戳值正确地转换为ISO字符串。但是,我将字符串转换为相同值的函数会产生不同的值。我知道我的逻辑和/或理解有错误。

    (顺便说一句,我已经在文档中搜索了 chrono 以及霍华德·欣南特的date.h图书馆,以便于理解。我在从一种类型转换到另一种类型时遇到了很大的困难,似乎永远无法想出一个简单一致的解决方案。我知道有一个,但我还没有到。)

    请查看下面的示例代码,并更正我的方法中的错误:

    #include <iomanip>
    #include <iostream>
    #include <sstream>
    #include <string>
    
    uint64_t from_isoTimeString(std::string iso_time_string)
    {
       //--- takes an UTC ISO formatted time/date string and converts it to a timestamp value
       std::tm tmb;
       std::stringstream ss(iso_time_string);
       ss >> std::get_time(&tmb, "%Y-%m-%dT%TZ");
       if (ss.fail())
       {
          std::string errmsg = "unable to convert Timestamp '" + iso_time_string + "' from ISO 8601 format";
          throw std::invalid_argument(errmsg);
       }
    
       std::time_t tt;
       tt = mktime(&tmb);              // returns 1656345610, expecting 1656331210
       tt = mktime(&tmb) - _timezone;  // returns 1656327610, expecting 1656331210
       return static_cast<uint64_t>(tt);
    }
    
    std::string formatTimestamp(uint64_t epoch_seconds, std::string timestamp_format)
    {
       std::time_t tt = epoch_seconds;
    
       std::tm tmb;
       gmtime_s(&tmb, &tt);
    
       std::stringstream ss;
       ss << std::put_time(&tmb, timestamp_format.c_str());
       if (ss.fail())
       {
          std::string err_msg = "unable to convert Timestamp to " + timestamp_format + " format";
          throw std::invalid_argument(err_msg);
       }
       return ss.str();
    }
    
    
    int main() 
    {
       uint64_t time1_uint = 1656331210;
       std::string time1_str = "2022-06-27T12:00:10Z";
    
       std::cout << "            time1_str: " << time1_str << std::endl;
       std::cout << " time1_uint as string: " << formatTimestamp(time1_uint, "%Y-%m-%dT%TZ" ) << std::endl;
       std::cout << "           time1_uint: " << time1_uint << std::endl;
       std::cout << "time1_str from string: " << from_isoTimeString(time1_str) << std::endl;
    }
    
    1 回复  |  直到 2 年前
        1
  •  1
  •   Howard Hinnant    2 年前

    C时间API的使用对于您的用例来说可能非常令人困惑,因为它涉及到本地时区之间的转换,这是完全不必要的复杂情况。

    Fwiw,我已经根据下面的date.h标题重写了您的转换函数:

    uint64_t
    from_isoTimeString(std::string iso_time_string)
    {
       //--- takes an UTC ISO formatted time/date string and converts it to a timestamp value
       date::sys_seconds tmb;
       std::stringstream ss(iso_time_string);
       ss >> date::parse("%Y-%m-%dT%TZ", tmb);
       if (ss.fail())
       {
          std::string errmsg = "unable to convert Timestamp '" + iso_time_string + "' from ISO 8601 format";
          throw std::invalid_argument(errmsg);
       }
       return static_cast<uint64_t>(tmb.time_since_epoch().count());
    }
    
    std::string
    formatTimestamp(uint64_t epoch_seconds, std::string timestamp_format)
    {
       date::sys_seconds tt{std::chrono::seconds{epoch_seconds}};
    
       std::stringstream ss;
       ss << date::format(timestamp_format, tt);
       if (ss.fail())
       {
          std::string err_msg = "unable to convert Timestamp to " + timestamp_format + " format";
          throw std::invalid_argument(err_msg);
       }
       return ss.str();
    }
    

    我在每个功能中都尽可能少地进行更改。现在您的本地时区不是计算的一部分。这段代码将很容易地移植到C++20(如果可用),只需更改几个 date:: std::chrono:: std:: .

    你的 main() 根本不需要更改,现在的输出是:

                time1_str: 2022-06-27T12:00:10Z
     time1_uint as string: 2022-06-27T12:00:10Z
               time1_uint: 1656331210
    time1_str from string: 1656331210