时间偏移计算关闭一分钟

时间:2016-05-25 10:18:44

标签: c++ datetime visual-c++ timezone

我正在尝试使用单个一致的API替换许多不同的时间类。但是我最近遇到了一个问题,我无法正确地序列化时区偏移。请注意,我正在尝试复制已在系统中广泛使用的现有格式。

格式应为YYYY-mm-DD HH:MM:SS.xxxxxxx -HHMM,其中x表示亚秒级精度,最后-HHMM是UTC的TZ偏移量。

代码

using namespace My::Time;
namespace chrn = std::chrono;
time_point now = clock::now();
time_point lclNow = getDefaultCalendarProvider()->toLocal(now);
duration diff{ lclNow - now };
std::wstring sign = diff > duration::zero() ? L" +" : L" -";
duration ms{ now.time_since_epoch().count() % duration::period::den };
int diffHrs = popDurationPart<chrn::hours>(diff).count();
int diffMins{ abs(chrn::duration_cast<chrn::minutes>(diff).count()) };
std::cout << Format{ lclNow, TimeZone::UTC, L" %Y-%m-%d %H:%M:%S." } << ms.count()
    << sign << std::setfill(L'0') << std::setw(2) << diffHrs
    << std::setfill(L'0') << std::setw(2) << diffMins << std::endl;

问题

  

预期:&lt; 2016-05-25 09:45:18.1970000 + 0100&gt;实际:其中2016年5月25日   09:45:18.1964787 + 0059&gt;

当我使用旧类进行相同的操作时,您会得到预期的值。问题似乎是我尝试在lclNownow之间取得差异。

目前我在UTC +1(由于DST生效)。但是,diff值始终为35999995635。在Windows中使用Visual C ++时,滴答是100 ns,因此每秒有10000000个滴答,这意味着diff值为3599.9995秒,这比我需要花费一个小时所需的3600秒短。

当我使用相同的格式打印两个时间值时,我可以看到它们相距一个小时。所以看来时区翻译不是问题。

2 个答案:

答案 0 :(得分:2)

问题似乎来自于我正在尝试的时区转换(正如SamVarshavchik指出的那样)。不幸的是,我无法使用Howard Hinnant非常完整的date and tz libraries,因为他们需要一种机制来更新他们工作所需的IANA时区数据库,因此我采用了包装Windows本机调用时区转换;即TzSpecificLocalTimeToSystemTimeSystemTimeToTzSpecificLocalTime函数。

但是,这些仅适用于SYSTEMTIME而非time_point。这意味着我快速轻松地将time_point转换为FILETIME(只需修改&#34;纪元&#34;),将FILETIME转换为SYSTEMTIME在将它传递给上述两个函数之一之前。当它被推入SYSTEMTIME结构(仅保持毫秒分辨率)时,这导致截断时间值。结果是,虽然我对日期的准确性,但在将日期转换回原始值时,我并不完全准确。

新解决方案没有针对基本time_pointtime_point翻译的日历映射。它使用以下代码计算std::chrono::minutes中的偏移量(其中zoneInfoTIME_ZONE_INFORMATION):

time_point WindowsTzDateProvider::doToUtc(const time_point& inLocal) const {
    return inLocal + getBias(inLocal);
}

time_point WindowsTzDateProvider::doToLocal(const time_point& inUtc) const {
    return inUtc - getBias(inUtc);
}

std::chrono::minutes WindowsTzDateProvider::doGetBias(const time_point& input) const {
    bool isDst = CalendarDateProvider::isDstInEffect(input);
    minutes baseBias{ zoneInfo.Bias };
    minutes extraBias{ isDst ? zoneInfo.DaylightBias : zoneInfo.StandardBias };
    return baseBias + extraBias;
}

bool CalendarDateProvider::isDstInEffect(const time_point& t) {
    time_t epochTime = clock::to_time_t(t);
    tm out;
#ifdef WIN32
    localtime_s(&out, &epochTime);
#else
    localtime_r(&out, &epochTime);
#endif
    return out.tm_isdst > 0;
}

注意:我使用非虚拟接口习语作为类,因此&#34;做...&#34;方法的版本。

答案 1 :(得分:1)

考虑使用这个free, open source time zone library,它可以用非常简单的语法完成你想要的工作,并适用于VS-2013及更高版本:

#include "tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono;
    auto t = make_zoned(current_zone(), system_clock::now());
    std::cout << format("%F %T %z", t) << '\n';
}

这应该为你输出:

2016-05-25 09:45:18.1970000 +0100