如何将一天添加到从 time() 获得的时间
Posted
技术标签:
【中文标题】如何将一天添加到从 time() 获得的时间【英文标题】:How to add one day to a time obtained from time() 【发布时间】:2010-09-23 13:07:39 【问题描述】:我有一个时间表示为自 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 提供的答案为我指明了正确的方向。但是我不得不使用 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
我还在使用 g++ 3.4.6 的 Red Hut Linux 上获得了正确的结果(使用两个输出函数)。
所以我想我遇到了一个 Cygwin 错误。
感谢您的所有帮助和建议......
【问题讨论】:
我认为您想在最终版本中使用 ctime 而不是 asctime,因为您想要本地时区的时间。 time() 返回 UTC,localtime() 将其转换为您的时区。在您的时区中添加一天,转换回 UTC。使用 ctime() 在您的时区打印出来。太混乱了。 多一行:tm->tm_wday = (tm->tm_wday + i) % 7; 您不必担心:来自mktime docs:timeptr的成员tm_wday和tm_yday的原始值被忽略,其余成员的取值范围不受限制到它们的正常值(比如 tm_mday 在 1 到 31 之间)。 在 Solaris 10 上,TZ=US/Pacific,程序产生:2006 年 3 月 11 日星期六 06:00:00,2006 年 3 月 12 日星期日 06:00:00,3 月 13 日星期一 06:00: 2006 年 00 月,2006 年 3 月 14 日星期二 06:00:00 -- 这就是您想要的答案...您使用的是哪个时区和平台? 【参考方案1】:使用 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;
【讨论】:
带下划线的单词不能用斜体吗? 使用 some_word_with_underscores 而不是 word 或者在下划线前面加一个反斜杠。 不使用 gmtime() 而不是 localtime() 是否意味着您忽略了问题所问的问题?实际上,您总是要添加 86400,而不是 90000 或 82800? 推论:使用 localtime() 代替 gmtime() 的算法应该可以工作 - 如果 localtime() 是健全的。【参考方案2】: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】:只需添加 24*60*60。它不应该在 DST 期间失败,因为 UTC 永远不会使用 DST。
如果失败,那么您没有在代码中的某处使用 UTC。移除时区依赖。
【讨论】:
重点是添加一天来表示当地时间 2008 年 3 月 31 日上午 8:00:00 应该给我当地时间 2008 年 4 月 1 日上午 8:00:00 当地时间,即使 DST 发生变化3/31 在当地过夜。 我不明白,你是否在UTC中使用unix时间戳? Unix 时间戳与 DST 无关。 从 UTC 到本地时间的转换是 DST 小时应该增加或减少的地方 - 正如 Pyrolistical 所说,UTC 不应该受到影响。 我添加了一个例子来说明我的意思(对我来说本地是美国中部时区), 然后按照 FigBug 下面的建议进行操作,但使用 localtime() 代替【参考方案4】:当您想要显示值时,保持时间戳 UTC 并将它们转换为指定的时区(包括夏令时),我总是得到最好的结果。
这样可以省去很多麻烦(并使您的程序独立于时区。
【讨论】:
【参考方案5】:一个非常老的问题的新答案。
新答案的理由:现在有更好的工具来解决这个问题,通过最小化串行 字段转换,使结果更不容易出错、更易于阅读并且实际上更有效。
新答案需要 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_seconds1142085600s);
for (int i = 0; i < 4; ++i)
std::cout << format("%a %b %d %T %Y %Z", base) << '\n';
base = base.get_local_time() + days1;
首先创建一个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)。然而,我使用这个库来全面调查这一天整个星球的时间转换。在这一天只有一个时区发生了转换:太平洋/复活节。而这种过渡是后退一个小时,而不是前进。这是智利使用的时区,位于南半球,其中一个回落在三月的时间范围内。
这可以通过在 UTC 而不是本地时间进行算术来证明。这是对上述程序在一行中的微小调整:
base = base.get_sys_time() + days1;
使用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
【讨论】:
以上是关于如何将一天添加到从 time() 获得的时间的主要内容,如果未能解决你的问题,请参考以下文章
熊猫方式将一天中的时间(有效的 datetime.time)转换为浮点变量