20

我有一个时间表示为自 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

我希望 13 年 3 月 12 日……的时间也是上午 8 点。


FigBug 提供的答案为我指明了正确的方向。但我不得不使用本地时间而不是 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

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

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

谢谢大家的帮助和建议......

4

5 回答 5

23

使用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;
}
于 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。它不应该在 DST 期间失败,因为 UTC 永远不会使用 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)。然而,我使用这个库来全面调查这一天整个星球的时间转换。在这一天只有一个时区发生了转换:太平洋/复活节。而那个过渡是向后移动一个小时,而不是向前移动。这是南半球智利使用的时区,其中一个时区倒退到 3 月的时间范围内

这可以通过在 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
于 2016-05-30T16:30:50.470 回答