2008-11-21 146 views
16

我有一个时间表示为自1970年1月1日午夜以来UTC(早期调用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日......至上午八时。


由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

但是,请注意,所有的日子都是周六。此外,它关系到三月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输出:在Red必胜客的Linux

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。

所以我想我遇到了Cygwin的错误。

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

+0

我想你想在最终版本中使用ctime而不是asctime,因为您希望在当地时区使用该时间。 time()返回UTC,localtime()将其转换为您的时区。在您的时区添加一天,然后转换回UTC。用ctime()在你的时区打印出来。很混乱。 – 2008-11-22 01:49:33

+0

再多一行:tm-> tm_wday =(tm-> tm_wday + i)%7; – 2008-11-22 02:40:35

+0

您不必担心:从mktime文档: timeptr的成员tm_wday和tm_yday的原始值将被忽略,并且其余成员的值的范围不限于其正常值(像tm_mday在1和31之间)。 – 2008-11-22 04:08:43

回答

18

使用gmtime的() time_t的转换为结构TM

添加一个新的一天( tm_mday

使用mktime()转换结构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; 
} 
3

我总是有最好的结果与保持时间戳UTC并将其转换为指定的时区(包括夏令时)时要显示的值。

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

5

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

如果失败,那么你是不是在你的代码中使用UTC地方。删除时区依赖。

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日1点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 
2

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

新答案的基本原理:现在有更好的工具可以解决这个问题,通过最小化串行转换,使结果更容易出错,更易于阅读,而且更加高效。

新答案需要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每天)。不过,我使用这个库来全面调查整个星球在这一天的时间转换。在这个日期只有一个时区有过渡:太平洋/复活节。并且该转换是将移回一小时,而不是转发。这是智利在南半球使用的时区,其中一个在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