打印/读取时间时出现奇怪的行为

4

我正在尝试将std::time_point保存到std::stream并读取回来。一个问题是使用标准函数会在某个地方“损失”1小时的时间。也就是说,我读取的时间比我写入的时间提前了1小时。我怀疑需要在某个地方设置夏令时。我编写了一个小程序,将时间打印到std::stringstream中并读取回来。

#include <iomanip>
#include <iostream>
#include <sstream>

#include <chrono>
#include <ctime>

using std::chrono::system_clock;
namespace chrono = std::chrono;

void test();

int main(int argc, char** argv)
{
    std::stringstream ss;

    auto start = system_clock::now();

    std::time_t ts = system_clock::to_time_t(start);

    std::tm time_out = *std::localtime(&ts);
    ss << std::put_time(&time_out, "%Y-%m-%d %H:%M:%S %Z") << '\n';

    std::cout << ss.str() << std::endl;

    std::tm time_in;
    ss >> std::get_time(&time_in, "%Y-%m-%d %H:%M:%S %Z");

    std::cout << "Are time dsts equal? : " <<
    (time_out.tm_isdst == time_in.tm_isdst) << '\n';

    std::time_t rawTime = std::mktime(&time_in);
    auto end = std::chrono::system_clock::from_time_t(rawTime);

    std::cout << "Are time points equal? : " << (start == end) << '\n';

    // print the trouble makers
    std::time_t start_time = system_clock::to_time_t(start);
    std::time_t end_time = system_clock::to_time_t(end);

    std::cout << "times: \n"
    << '\t' << std::put_time(std::localtime(&start_time), "%c %z") << '\n'
    << '\t' << std::put_time(std::localtime(&end_time), "%c %z") << '\n';

    // this is a source of strange behaviour...
//    std::cout << "Difference: "
//    << chrono::duration_cast<chrono::seconds>(start - end).count()
//    << std::endl;

    return 0;
}

最奇怪的是,程序输出了以下内容:
Are time dsts equal? : 1
Are time points equal? : 0
times: 
Tue Dec 11 19:26:24 2012 +0000
Tue Dec 11 19:26:24 2012 +0000

当我取消程序末尾的3行注释(打印时间点之间的差异)时,结果是:
Are time dsts equal? : 0
Are time points equal? : 0
times: 
Tue Dec 11 19:29:40 2012 +0000
Tue Dec 11 18:29:40 2012 +0000
Difference: 3600

请注意dst(夏令时)突然不等,时间也不等。

我在Mac OS X 10.8.2上使用libc++和XCode46-DP2。我使用的clang++版本是Apple clang version 4.1clang version 3.2 (trunk 167239)

我的问题是: A)关于1小时的差异,这是我的库中的错误还是我的标准函数使用不正确?(后者不会让我感到惊讶...) B)当我取消注释程序末尾的三行时,代码到底发生了什么?这看起来像一个bug。有人愿意在他们的平台上试试吗?


对于chrono/io,您可能希望使用boost chrono_io http://www.boost.org/doc/libs/1_52_0/doc/html/chrono/reference.html#chrono.reference.io - Johan Lundberg
@JohanLundberg:这两个都是我的代码,所以切换不太可能解决问题。正在调查中… - Howard Hinnant
1个回答

1

我认为我们正在看一个与%Z说明符有关的错误,以及可能%z说明符有关,但还不确定。

我将进一步研究这些错误的原因。然而,我想先发布一篇文章,以便为您提供解决方法。我相信,如果您对输入的tm进行零初始化,并始终假定它是针对您本地时区的,则可以消除错误:

std::tm time_in{0};

谢谢,很好用。我之前使用了 time_in.is_dst = 0,也有效。 - Roman Kutlak
在相关的问题上,你会推荐哪种“时间类型”作为时间戳?我要将其用于缓存条目,并希望能够丢弃旧条目。我正在使用time_t timestamp = system_clock::to_time_t(system_clock::now()) 创建时间戳,但我不确定是否是最好的选择。 - Roman Kutlak
这取决于您的精度需求。但在Mac OS X 10.8.2上,如果您以64位模式编译(默认情况下),time_t具有秒级精度,并占用64位。 system_clock :: time_point也是一个合适的时间戳,同样占用64位,并具有微秒级精度。如果您需要纳秒级精度的时间戳,则在此平台上,您可以选择high_resolution_clock :: time_point,它也是64位。 - Howard Hinnant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接