2017-09-04 6 views
6

私は時間順に写真を並べ替えようとしています。したがって、私はEXIFデータから文字列として時刻を抽出し、それをstd::time_tに変換します。しかし、時々間違った結果が出ます。私は問題をこの最小限の例にまで減らしました。std :: get_time:間違った結果を使用して、time-stringを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でコンパイル:それは3時間の文字列、1秒間隔でています。

出力:

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すべきですか?

編集

打ち鳴らすの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 
+2

、あなたの出力時間のすべてが間違っています。 https://www.epochconverter.com/によると、1469867226は土曜日、2016年7月30日8:27:06 AMにする必要があります。 [あなたのコードを実行すると、期待される結果が得られます](https://ideone.com/dUJqJ2)。 – user4581301

+1

はい、すべての出力が間違っています。 [(gcc 6.3)でideone.comで動作するコード](https://ideone.com/A0ysc2)を参照してください。それが動作します。 'std :: get_time()'のclang実装がバグのようですか? –

+0

@ user4581301ご意見ありがとうございます。確かに、正しい結果を得るためにg ++を使ってコンパイルして実行しました...私はclangのバグレポートを提出します。その間、この問題を回避する方法に関する提案はありますか? (私のQtアプリケーションでは、私は簡単に思いつくのを避けることはできません) –

答えて

2

私は私のコメントで述べたように、すべての出力が間違っています。それはclangのバグだと思われます。 the Bugzilla page of the LLVM projectを検索しましたが、何も見つかりませんでした。あなたのサンプルコードでバグ報告を提出したいかもしれません。

回避方法は、std::sscanf()で手動で文字列を解析し、std::tm構造体を入力することです。次のコードは、入力文字列iに対してこれを行います。 example with the output of std::get_time() and the std::sscanf() method at ideone全体を参照してください。文字列が異なる場合は、その解決策をニーズに合わせてください。

この解決方法は、フォーマット文字列として%d:%d:%d %d:%d:%dを使用しています。またを使用すると、%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; 
    } 
} 

それが0さらにtm_yearメンバーから始まるので、tm_monの1を減算する必要がある1900年以降の年です。 description of std::tm hereを参照してください。

さらに夏時間が有効であった場合std::mktime()を決定させるために-1にtm_isdstを設定するために必要とされます。


は、あなたの与えられたフォーマットの std::stringに戻す std::time_t Linux timestampを変換するには: %Y:%m:%d %H:%M:%Sあなたは std::localtime()std::strftime()を使用することができます。 See live example on 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; 
} 
+0

すばらしい、ありがとう!!同じバグを示す 'std :: put_time'も使用しているので、この関数の逆も書きます。私はバグレポートを電子メールで送ってきました(登録されておらず、バグがブロックされているため直接報告できません)。 –

+1

@TomdeGeus: '%Y:%m:%d%H:%M:%S'という形式で' std :: time_t'から 'std :: string'に戻すメソッドを追加しました。 –

3

Here is another library you could useバグを回避します。そして、このライブラリは移植可能です。何か問題があれば、数時間以内に修正させていただきます。それは単なるヘッダであり、ソースもなく、インストールも非常に簡単です。

#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 
+0

これは本当に私が潜在的に永遠に保つことができるきれいな回避策を持つ非常に良い解決策と思われます。あなたが作者であるように見えるので、あなたは 'std :: time_t' - >' '%Y:%m:%d%H:%M:%S" 'のように、自分自身を見つけますが、それは1秒しかないかもしれません) –

+2

'std :: string'を返す' format'を使って答えを更新しました。 –

+0

+1コンパイル時にできるだけ多く評価するうわー本当に素晴らしいライブラリです。 'std :: sscanf()'を使った私の答えより速くなければなりません。 –

関連する問題