簡體   English   中英

使用 std::get_time 將時間字符串轉換為 std::time_t:錯誤結果

[英]Convert time-string to std::time_t using std::get_time: wrong result

我正在嘗試按時間順序對照片進行排序。 因此,我從 EXIF 數據中提取時間作為字符串,然后將其轉換為std::time_t 但是,我有時會得到不正確的結果。 我已將問題簡化為這個最小的示例。 它具有三個時間字符串,相隔一秒:

#include <vector>
#include <string>
#include <iostream>
#include <ctime>
#include <iomanip>
#include <sstream>

int main()
{
  std::vector<std::string> vec;

  vec.push_back("2016:07:30 09:27:06");
  vec.push_back("2016:07:30 09:27:07");
  vec.push_back("2016:07:30 09:27:08");

  for ( auto & i : vec )
  {
    struct std::tm tm;
    std::istringstream iss;
    iss.str(i);
    iss >> std::get_time(&tm,"%Y:%m:%d %H:%M:%S");

    std::time_t time = mktime(&tm);

    std::cout << i << " time = " << time << std::endl;
  }
}

clang++ -std=c++14 test.cpp

output:

2016:07:30 09:27:06 time = 1469867226
2016:07:30 09:27:07 time = 1469863627
2016:07:30 09:27:08 time = 1469863628

這顯然是錯誤的,它們應該是1分開的,這僅對最后兩個是正確的?

編輯

由於 clang 的std::get_time (和std::put_time )似乎有一個錯誤,這是我的版本信息:

 $ clang --version Apple LLVM version 8.1.0 (clang-802.0.42) Target: x86_64-apple-darwin16.7.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

這是另一個可以用來解決這個bug的庫。 這個庫是可移植的。 如果您有任何問題,我會在幾小時內解決。 它只是一個標題,沒有來源,所以非常容易“安裝”。 只需將標題放在include路徑中的某個位置:

#include "date.h"
#include <vector>
#include <string>
#include <iostream>
#include <sstream>

int main()
{
  std::vector<std::string> vec;

  vec.push_back("2016:07:30 09:27:06");
  vec.push_back("2016:07:30 09:27:07");
  vec.push_back("2016:07:30 09:27:08");

  for ( auto & i : vec )
  {
    date::sys_seconds tm;
    std::istringstream iss{i};
    iss >> date::parse("%Y:%m:%d %H:%M:%S", tm);

    std::cout << i << " time = " << tm.time_since_epoch().count() << std::endl;
    std::cout << date::format("%Y:%m:%d %H:%M:%S\n", tm);
  }
}

輸出:

2016:07:30 09:27:06 time = 1469870826
2016:07:30 09:27:06
2016:07:30 09:27:07 time = 1469870827
2016:07:30 09:27:07
2016:07:30 09:27:08 time = 1469870828
2016:07:30 09:27:08

正如我在評論中所說,你的所有輸出都是錯誤的。 這似乎是clang中的一個bug。 我搜索了LLVM項目的Bugzilla頁面,但一無所獲。 也許您想要使用示例代碼提交錯誤報告。

解決方法是使用std::sscanf()手動解析字符串並填充std::tm結構。 以下代碼為您的輸入字符串i std::get_time()查看整個示例,其中包含std::get_time()的輸出和std::sscanf()方法 如果字符串不同,那么根據您的需要調整該解決方案。

此解決方案使用%d:%d:%d %d:%d:%d作為格式字符串。 您還可以使用%4d:%2d:%2d %2d:%2d:%2d ,它只接受長度小於或等於%d之間的數字的數字。

輸出:

2016:07:30 09:27:06 | sscanf() time =        1469870826
2016:07:30 09:27:06 | std::get_time() time = 1469870826
2016:07:30 09:27:07 | sscanf() time =        1469870827
2016:07:30 09:27:07 | std::get_time() time = 1469870827
2016:07:30 09:27:08 | sscanf() time =        1469870828
2016:07:30 09:27:08 | std::get_time() time = 1469870828

碼:

#include <vector>
#include <string>
#include <iostream>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <cstring>

int main()
{
   std::vector<std::string> vec;

   vec.push_back("2016:07:30 09:27:06");
   vec.push_back("2016:07:30 09:27:07");
   vec.push_back("2016:07:30 09:27:08");

   for (auto & i : vec)
   {
      struct std::tm tm;

      /* std::sscanf() method: */
      std::memset(&tm, 0, sizeof(tm));
      if (6 != std::sscanf(i.c_str(), "%d:%d:%d %d:%d:%d",
                           &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
                           &tm.tm_hour, &tm.tm_min, &tm.tm_sec))
      {
         return -1;
      }

      /* correct the numbers according to:
       * see: http://en.cppreference.com/w/cpp/chrono/c/tm */
      --tm.tm_mon;
      tm.tm_year -= 1900;
      /* mktime determines if Daylight Saving Time was in effect
       * see: http://en.cppreference.com/w/cpp/chrono/c/mktime */
      tm.tm_isdst = -1;

      std::time_t time = std::mktime(&tm);

      std::cout << i << " | sscanf() time =        " << time << std::endl;

      /************************************************************/

      /* std::get_time() method: */
      std::istringstream iss;
      iss.str(i);
      iss >> std::get_time(&tm, "%Y:%m:%d %H:%M:%S");

      time = std::mktime(&tm);

      std::cout << i << " | std::get_time() time = " << time << std::endl;
   }
}

有必要減去其中一個tm_mon因為它從0開始。此外, tm_year成員是自1900年以來的年份。請參閱此處的std::tm說明

此外,需要將tm_isdst設置為-1,讓std::mktime()確定夏令時是否生效。


要將std::time_t Linux時間戳轉換回給定格式的std::string%Y:%m:%d %H:%M:%S您可以將std::strftime()std :: localtime() 查看ideone上的實例

輸出:

time = 1469870826 | 2016:07:30 09:27:06

碼:

#include <vector>
#include <string>
#include <iostream>
#include <ctime>

int main()
{
   char buff[20];
   time_t timestamp = 1469870826;
   std::strftime(buff, sizeof(buff), "%Y:%m:%d %H:%M:%S", std::localtime(&timestamp));
   std::string timeStr(buff);

   std::cout << "time = " << timestamp << " | " << timeStr;
}

發布的代碼中有一個錯誤 - 不在庫中。 這是一個微妙的。 代替:

struct std::tm tm;

和:

struct std::tm tm{};

這確保tm被零初始化。 應用該更改后,output 符合預期:

2016:07:30 09:27:06 time = 1469867226
2016:07:30 09:27:07 time = 1469867227
2016:07:30 09:27:08 time = 1469867228

第 2 次和第 3 次運行“正確”的原因是mktime通過指針更新其參數。 特別是它設置了 DST 和 GMT 偏移量,因此在循環第一次運行后,分配給tm的 memory 區域已經設置了這些。 下一個循環迭代抓取相同的 memory 區域,放置(未初始化)值並僅更新一些字段。 但是,由於 DST 和 GMT 偏移量已經“設置”, mktime的行為會發生變化。

因此,第 1 次運行與第 2 次和第 3 次不一致。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM