如何将一天添加到从时间()获得的时间

时间:2008-11-21 22:03:32

标签: c++ c date dst

我有一个时间表示自UTC,1970年1月1日午夜以来经过的秒数(之前调用time()的结果)。如何在此时添加一天?

在大多数情况下,添加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提供的答案指出了我正确的方向。但我不得不使用localtime而不是gmtime。

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

我缺少什么?


好的,我已经尝试过使用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

我也在Red Hut Linux上用g ++ 3.4.6获得了正确的结果(带有两个输出函数)。

所以我猜我遇到了一个Cygwin错误。

感谢您的所有帮助和建议......

5 个答案:

答案 0 :(得分:19)

使用 gmtime() time_t 转换为 struct tm

在当天添加一个( 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;
}

答案 1 :(得分:5)

只需添加24 * 60 * 60即可。它在DST期间不应该失败,因为UTC不会使用DST。

如果失败,那么您不会在代码中的某处使用UTC。消除时区依赖。

答案 2 :(得分:5)

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

答案 3 :(得分:3)

当您想要显示值时,我始终保持时间戳UTC并将其转换为指定的时区(包括夏令时),从而获得最佳结果。

这样可以省去很多麻烦(并使你的程序独立于时区。

答案 4 :(得分:1)

一个非常古老的问题的新答案。

新答案的基本原理:现在有更好的工具可以解决这个问题,通过最小化串行&lt; - &gt;来减少错误,更容易阅读,并且实际上更有效。现场转换。

新答案需要C ++ 11/14,<chrono>和此free, open source, timezone library

以下是代码:

#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)。然而,我使用这个库来全面调查这个日期整个星球的时间转换。在这个日期只有一个时区有过渡:太平洋/复活节。而这种转变是将移回一小时,而不是前进。这是南半球智利使用的时区,其中一个在三月时间内落后

这可以通过以UTC而不是本地时间进行算术来证明。这是对上述程序的一个小调整:

        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