我正在尝试使用单个一致的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;
当我使用旧类进行相同的操作时,您会得到预期的值。问题似乎是我尝试在lclNow
和now
之间取得差异。
目前我在UTC +1(由于DST生效)。但是,diff
值始终为35999995635
。在Windows中使用Visual C ++时,滴答是100 ns,因此每秒有10000000个滴答,这意味着diff
值为3599.9995秒,这比我需要花费一个小时所需的3600秒短。
当我使用相同的格式打印两个时间值时,我可以看到它们相距一个小时。所以看来时区翻译不是问题。
答案 0 :(得分:2)
问题似乎来自于我正在尝试的时区转换(正如SamVarshavchik指出的那样)。不幸的是,我无法使用Howard Hinnant非常完整的date and tz libraries,因为他们需要一种机制来更新他们工作所需的IANA时区数据库,因此我采用了包装Windows本机调用时区转换;即TzSpecificLocalTimeToSystemTime和SystemTimeToTzSpecificLocalTime函数。
但是,这些仅适用于SYSTEMTIME而非time_point
。这意味着我快速轻松地将time_point
转换为FILETIME
(只需修改&#34;纪元&#34;),将FILETIME
转换为SYSTEMTIME
在将它传递给上述两个函数之一之前。当它被推入SYSTEMTIME
结构(仅保持毫秒分辨率)时,这导致截断时间值。结果是,虽然我对日期的准确性,但在将日期转换回原始值时,我并不完全准确。
新解决方案没有针对基本time_point
到time_point
翻译的日历映射。它使用以下代码计算std::chrono::minutes
中的偏移量(其中zoneInfo
为TIME_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