ぼやきごと/2015-02-21/VC++2012の system_clock クラス to_time_t 関数は未来の時刻を返す場合がある の変更点


#blog2navi()
*VC++2012の system_clock クラス to_time_t 関数は未来の時刻を返す場合がある [#od2d096c]

てっきり切り捨てだと思っていて、それを想定した作りにしていたらハマったという話。

次のコードを見てください。

#code(c){{
#include <chrono>
#include <iostream>
#include <ctime>

int main(int, char**)
{
    // 現在時刻を time_t 値で取得
    const std::time_t t1 = std::time(nullptr);

    // システムクロックの time_point 値に変換
    auto tp = std::chrono::system_clock::from_time_t(t1);

    // 500ミリ秒を加算
    tp += std::chrono::milliseconds(500);

    // time_t 値に変換
    const std::time_t t2 = std::chrono::system_clock::to_time_t(tp);

    // t1, t2 を出力
    std::cout << t1 << '\n' << t2 << std::endl;

    return 0;
}
}}

何をやっているかはコメントで記している通りです。~
上記のコードを実行すると、 Visual C++ 2013 、 gcc 4.9.1 、 clang 3.5.0 では例えば次のように出力されます。

#pre{{
1424458932
1424458932
}}

- [[gcc 4.9.1 での実行結果 - Wandbox>http://melpon.org/wandbox/permlink/Xct55tej5H7OibS6]]
- [[clang 3.5.0 での実行結果 - Wandbox>http://melpon.org/wandbox/permlink/sZ2XscoNs97Mqq1P]]

しかし Visual C++ 2012 では実行結果が異なり、例えば次のように出力されます。

#pre{{
1424459283
1424459284
}}

500ミリ秒加算後の @code{time_t}; 値が 1 増えています。

@code{std::time_t}; 型の数値が何を表すかはコンパイラ依存ですが、VC++、gcc、clangといった主要コンパイラはEpoch((1970年1月1日0時0分0秒))からの経過秒数を表しています。~
よって @code{std::chrono::system_clock::to_time_t}; 関数では秒未満を何らかの方法で捨てることになります。

一番単純な実装としては秒未満切り捨てであり、VC++2013等の主要コンパイラはそうなっています。~
前述のコードの 500 を 999 に変えても @code{time_t}; 値は増加しません。

しかしVC++2012は最も近い秒数値に丸める実装になっています。~
つまり秒未満が500ミリ秒以上であれば、''変換元の @code{time_point}; 値よりも未来の時刻を返してきます。''~
VC++2012でも、前述のコードの 500 を 499 に変えれば @code{time_t}; 値は増加しなくなります。

関数の仕様としては「引数に渡した @code{time_point}; 値と同等の @code{time_t}; 値を返す」としか定義されていないはずなのでVC++2012の実装でも誤りではないと思いますが、注意する必要はあるでしょう。

RIGHT:Category: &#x5b;[[C++>ぼやきごと/カテゴリ/C++]]&#x5d;&#x5b;[[Visual Studio>ぼやきごと/カテゴリ/Visual Studio]]&#x5d;&#x5b;[[プログラミング>ぼやきごと/カテゴリ/プログラミング]]&#x5d; - 2015-02-21 04:35:12
----
RIGHT:&blog2trackback();
#comment(above)
#blog2navi()