web-dev-qa-db-ja.com

time()から取得した時間に1日を加算する方法

UTC(time()への以前の呼び出しの結果)である1970年1月1日の午前0時から経過した秒数として表される時間があります。この時間に1日を追加するにはどうすればよいですか?

ほとんどの場合、24 * 60 * 60を追加しても機能しますが、夏時間がオンまたはオフになると失敗します。言い換えれば、私は主に24時間を追加したいのですが、23時間または25時間を追加することもあります。

説明するために-プログラム:

#include <time.h>
#include <iostream>

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    time_t time = base + i * 24 * 60 * 60;
    std::cout << ctime(&time);
  }
  return 0;

}

生産:

Sat Mar 11 08:00:00 2006
Sun Mar 12 09:00:00 2006
Mon Mar 13 09:00:00 2006
Tue Mar 14 09:00:00 2006

3月12、13、...の時間も午前8時にしたいと思います。


FigBugによって提供された答えは、私を正しい方向に向けました。しかし、gmtimeの代わりにlocaltimeを使用する必要がありました。

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    struct tm* tm = localtime(&base);
    tm->tm_mday += i;
    std::cout << asctime(tm);
 }
 return 0;
}

ください:

Sat Mar 11 08:00:00 2006
Sat Mar 12 08:00:00 2006
Sat Mar 13 08:00:00 2006
Sat Mar 14 08:00:00 2006

それが私が欲しいものです。 gmtimeを使用すると、14:00:00の時刻が表示されます

ただし、すべての日が土曜日であることに注意してください。また、3月32日、33日などに移動します。mktime関数をスローすると、開始した場所に戻ります。

#include <time.h>
#include <iostream>

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    struct tm* tm = localtime(&base);
    tm->tm_mday += i;
    time_t time = mktime(tm);
    std::cout << asctime(tm);
 }
 return 0;
}

私に与える:

Sat Mar 11 08:00:00 2006
Sun Mar 12 09:00:00 2006
Mon Mar 13 09:00:00 2006
Tue Mar 14 09:00:00 2006

私は何が欠けていますか?


OK、私は使用することであるFigBugの最新の提案を試しました:

 std::cout << ctime(&time);

asctimeの代わりに、同じ結果が得られます。ですから、私のライブラリやコンパイラがめちゃくちゃになっていると思います。 cygwinでg ++ 3.4.4を使用しています。ファイルをSolaris5.8にコピーし、そこでg ++ 3.3を使用してコンパイルしました。そこで正しい結果が得られます!実際、出力にctimeとasctimeのどちらを使用しても、正しい結果が得られます。

Sat Mar 11 08:00:00 2006
Sun Mar 12 08:00:00 2006
Mon Mar 13 08:00:00 2006
Tue Mar 14 08:00:00 2006

また、g ++ 3.4.6を使用するRedHut Linuxでも正しい結果(両方の出力関数を使用)が得られます。

だから私はCygwinのバグに出くわしたと思います。

すべてのあなたの助けとアドバイスをありがとう....

18
Andrew Stein

gmtime()を使用して time_tstruct tm

その日に1つ追加します(tm_mday

mktime()を使用して、struct tmを time_t

詳細については、 time.h を参照してください

編集:

私はそれを試しました、これはうまくいきます:

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    struct tm* tm = localtime(&base);
    tm->tm_mday += i;
    time_t next = mktime(tm);
    std::cout << ctime(&next);
 }
 return 0;
}
20
Roland Rabien

24 * 60 * 60を追加するだけです。 UTCはDSTを使用しないため、DST中に失敗することはありません。

失敗している場合は、コードのどこかでUTCを使用していません。タイムゾーンの依存関係を削除します。

5
Pyrolistical

FigBugのソリューションは毎回ほぼ動作しますが、DST修正が必要です:tm-> tm_isdst = -1

Tm_isdstの値が正または0の場合、mktime()は最初に、夏時間がそれぞれ指定された時間有効であるか無効であるかを推定します。 tm_isdstの値が負の場合、mktime()は、指定された時間に夏時間が有効であるかどうかを判別しようとします。

mktime spec から引用)

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    struct tm* tm = localtime(&base);
    tm->tm_mday += i;
    tm->tm_isdst = -1;        // don't know if DST is in effect, please determine
                              // this for me
    time_t next = mktime(tm);
    std::cout << ctime(&next);
 }
 return 0;
}

そうしないと、バグが発生します(2009年3月29日01:59:59から始まるモスクワ夏時間の例):

int main()
{
    // 28 March 2009 05:00:00 GMT ( local - 08:00 (MSK) )
    time_t base = 1238216400;

    std::time_t start_date_t = base;
    std::time_t end_date_t = base;

    std::tm start_date = *std::localtime(&start_date_t);
    std::tm end_date = *std::localtime(&end_date_t);

    end_date.tm_mday += 1;
//    end_date.tm_isdst = -1;

    std::time_t b = mktime(&start_date);
    std::time_t e = mktime(&end_date);

    std::string start_date_str(ctime(&b));
    std::string stop_date_str(ctime(&e));

    cout << " begin (MSK) (DST is not active): " << start_date_str;
    cout << " end   (MSD) (DST is active):     " << stop_date_str;
}

出力:

begin (MSK) (DST is not active): Sat Mar 28 08:00:00 2009
end   (MSD) (DST is active):     Sun Mar 29 09:00:00 2009
5

タイムスタンプをUTCに保ち、値を表示するときに指定されたタイムゾーン(夏時間を含む)に変換すると、常に最良の結果が得られました。

これにより、このような面倒な作業が大幅に軽減されます(また、プログラムがタイムゾーンに依存しなくなります。

3
Toon Krijthe

非常に古い質問に対する新しい答え。

新しい答えの理論的根拠:この問題を解決するためのより優れたツールがあり、シリアル<->フィールド変換を最小限に抑えることで、エラーが発生しにくく、読みやすく、実際には効率が向上しています。

新しい答えには、C++ 11/14、_<chrono>_、およびこれ 無料、オープンソース、タイムゾーンライブラリ が必要です。

コードは次のとおりです。

_#include "tz.h"
#include <iostream>

int
main()
{
    using namespace std::chrono;
    using namespace date;
    auto base = make_zoned("Pacific/Easter", sys_seconds{1142085600s});
    for (int i = 0; i < 4; ++i)
    {
        std::cout << format("%a %b %d %T %Y %Z", base) << '\n';
        base = base.get_local_time() + days{1};
    }
}
_

まず、目的のタイムゾーンとUnixタイムスタンプを組み合わせて_zoned_time_を作成します。

これは、必要な形式でフォーマットされています。

また、1日の追加は、タイムゾーンの現地時間システムで行われ、夏時間が考慮されます。出力は次のとおりです。

_Sat Mar 11 09:00:00 2006 -05
Sun Mar 12 09:00:00 2006 -06
Mon Mar 13 09:00:00 2006 -06
Tue Mar 14 09:00:00 2006 -06
_

結局のところ、この出力はOPが望んでいたものとは正確には一致しません(要求された出力は毎日08:00:00です)。しかし、私はこのライブラリを使用して、この日付の惑星全体の時間遷移を完全に調査しました。そして、この日付に移行したタイムゾーンは、太平洋/東の1つだけです。そして、その移行は、前方ではなく、後方を1時間移動することでした。これは、南半球のチリで使用されるタイムゾーンであり、3月の時間枠で1つフォールバックします。

これは、現地時間ではなくUTCで算術演算を実行することで実証できます。これは、上記のプログラムを1行で少し調整したものです。

_        base = base.get_sys_time() + days{1};
_

base.get_sys_time()ではなくbase.get_local_time()を使用すると、うるう秒を無視するUTCである「システム時間」で算術演算が実行されます。そして今、出力は次のように変わります。

_Sat Mar 11 09:00:00 2006 -05
Sun Mar 12 08:00:00 2006 -06
Mon Mar 13 08:00:00 2006 -06
Tue Mar 14 08:00:00 2006 -06
_
1
Howard Hinnant