hageking
@hageking

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

c++の時間差を取得するプログラムを連続で実行すると値がおかしくなる

Q&A

Closed

解決したいこと

C++で1時間後の時間を取得し今の時間と差を取得するプログラムを作りました。
連続で実行するとたまに3600(1時間の秒数)ではなくなります。
解決方法を教えてください。

発生している問題・エラー

aaaaa@MacBook-Air test % ./a.out 
2022/08/15-04:36:57
時間 : 3600
aaaaa@MacBook-Air test % ./a.out                                   
2022/08/15-04:37:01
時間 : 7200

該当するソースコード

#include <time.h>
#include <iostream>
using namespace std;

int main()
{
    time_t time1;
    time_t time2 = time(NULL) - 3600;
    struct tm result;
    char date[64];
    strftime(date, sizeof(date), "%Y/%m/%d-%H:%M:%S", localtime(&time2));

    if (strptime(date, "%Y/%m/%d-%H:%M:%S", &result) == NULL)
    {
        printf("error");
    }

    cout << date << endl;

    time(&time1);
    cout << "時間 : " << difftime(time1, mktime(&result)) << endl;

    return 0;
}
0

2Answer

If the struct tm object was obtained from POSIX strptime or equivalent function, the value of tm_isdst is indeterminate, and needs to be set explicitly before calling mktime.

strptime() で取得した tm 構造体は、時刻が夏時間かどうかを表す tm_isdst フィールドの値が不定になるようです1。これを mktime() に渡すということは、ランダムに夏時間だったりそうでなかったりする time_t 値が返るということです。夏時間は1時間ずれるので、結果として今の時間との差が3600秒になったり7200秒になったりします。

mktime() を呼ぶ前に、以下のように tm_isdst フィールドを0にセットすれば解決すると思います。

result.tm_isdst = 0;
  1. 正確には strptime()tm_isdst フィールドをセットしないだけで、事前に tm 構造体を初期化しておけば問題ないはずです。

2Like

Comments

  1. @hageking

    Questioner

    非常に詳しい回答ありがとうございます。
    問題が解決しました。
    調べてもわからなかったので助かりました。
    これからは自己解決できるように、どのように検索したか教えてくれると嬉しいです。
  2. C++ でランダムな挙動が起きるのは初期化されていないメモリ領域から値を読み取るバグが原因なことが多いので、まずコードをよく読んでそういった箇所がないか調べました。
    今回だと time_t time1; に初期値が入っていないので、もし time(&time1); する前に time1 の値を読み取ると変な値が返ります。struct tm result; や char date[64]; も同様です。

    一見バグはなさそうだったので、時刻関係の関数が予想外の値を返していそうだとあたりをつけて、それぞれの関数名を https://en.cppreference.com/w/ で検索してドキュメントを読んだら説明が見つかりました。

    もしそれで分からなかったら「C++ difftime 時間がずれる」とか「C++ mktime return time randomly」などでググるつもりでした。
  3. 初期化についていうと、ローカル変数は宣言と同時に初期値を入れておくといいです。

    // 数値型変数なら適当な値をセットする
    // 無効値やエラーを表す値が決まっていればそれを使うのがベター
    time_t time1 = 0;

    // 構造体や配列は {0} を代入すると0埋めされる
    // それがまずければ適切な値をセットする
    struct tm result = {0};
    char date[64] = {0};

    // ポインタなら NULL をセットする
    // NULL は C++11 以降なら nullptr とも書け、こちらが推奨される
    int *x = NULL;

    グローバル変数やスタティック変数は自動的に0で初期化されます。
  4. @hageking

    Questioner

    回答ありがとうございます。
    経験を積まないと分からないことですねー
    これから苦戦したらこのやり方で検索してみます。
    長い間本当にありがとうございました。

Comments

  1. @hageking

    Questioner

    そうですかー
    環境の問題ですかねー
    もう少し調べてみます。

Your answer might help someone💌