20

1970 年 1 月 1 日の午前 0 時 (UTC) (以前の time() の呼び出しの結果) から経過した秒数として表された時刻があります。この時間に 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 を使用しています。ファイルを Solaris 5.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 を使用する Red Hut Linux でも、(両方の出力関数で) 正しい結果が得られます。

したがって、Cygwin のバグに遭遇したと思います。

皆様のご協力、ご助言をよろしくお願いいたします。...

4

5 に答える 5

23

gmtime()を使用してtime_tstruct tmに変換します

その日に 1 を追加 ( tm_mday )

mktime()を使用してstruct tmtime_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;
}
于 2008-11-21T22:07:46.880 に答える
8

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

tm_isdst に正または 0 の値を指定すると、mktime() は、指定された時間にそれぞれ夏時間が有効である、または無効であると最初に推定します。tm_isdst に負の値を指定すると、mktime() は、指定された時間にサマータイムが有効かどうかを判断しようとします。

( mktime仕様より引用)

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
于 2009-03-13T15:31:45.230 に答える
4

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

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

于 2008-11-21T22:13:38.023 に答える
3

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

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

于 2008-11-21T22:06:28.177 に答える
2

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

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

新しい回答には、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};
    }
}

zoned_timeは、目的のタイム ゾーンと Unix タイム スタンプを組み合わせてを作成することから始めます。

これは、任意の形式でフォーマットされます。

また、タイム ゾーンのローカル タイム システムで 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 月の時間枠に戻ります。

これは、現地時間ではなく 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
于 2016-05-30T16:30:50.470 に答える