使用霍华德·欣南特的日期库将双倍转换为zoned_time
Convert double to zoned_time using Howard Hinnant's date library
我有一个双精度表示自午夜(本地时区)1970 年 1 月 1 日以来的时间(以天为单位),还有一个字符串表示时区。我想将这些转换为 日期::zoned_time 使用霍华德·欣南特的日期和时间区库。
背景是我需要将日期时间与双精度值相互转换,以便在分析库中使用。我还将从本地或用户指定的时区的 excel 收到日期时间的双精度。
这是我做的一次尝试
using namespace date;
using namespace std::chrono;
typedef date::zoned_time<std::chrono::seconds> datetime;
const double UNIX_MINUS_EXCEL_EPOCH = 25569.0;
const double SECONDS_PER_DAY = 24.0 * 60.0 * 60.0;
datetime timePointFromDouble(double x)
{
double seconds = (x - UNIX_MINUS_EXCEL_EPOCH) * SECONDS_PER_DAY;
system_clock::duration d = duration_cast<system_clock::duration>(duration<double>(seconds));
system_clock::time_point t = system_clock::time_point(d);
auto xx = make_zoned("America/Chicago", t);
return xx;
}
它无法编译,因为make_zoned的结果具有错误的类型。此外,我不相信它正确地将输入时间(以天为单位)映射到输出日期时间,因为闰秒和夏令时发生变化的天数。
规格:
x
是自 1899-12-30 00:00:00 以来在美国/芝加哥的天数度量。
溶液:
using datetime = date::zoned_seconds;
datetime
timePointFromDouble(double x)
{
using namespace date;
using namespace std::chrono;
using ddays = duration<double, days::period>;
constexpr auto excel_epoch = local_days{1_d/January/1970} -
local_days{30_d/December/1899};
return datetime{"America/Chicago",
local_seconds{round<seconds>(ddays{x} - excel_epoch)}};
}
解释:
您的版本无法编译的原因是因为转换为system_clock::time_point
,实际上精度为微秒或更精细。 但是您的结果类型的精度为秒,因此库拒绝将高精度t
隐式截断为低精度xx
。
解决此问题的最简单方法是time_point_cast<seconds>(t)
. 但是还有更多的乐趣...
<chrono>
通过为您处理转换来生死存亡。 每当您自己进行转换时,您都应该选择删除这些转换,而不是让<chrono>
进行转换。 这通常会简化您的代码,并且可能只是捕获转换错误。
<chrono>
知道如何在不同的持续时间之间进行转换,但不知道如何在 Excel 纪元之间进行转换,因此这是我们无法避免的一种转换。 但是我们可以用比神秘常数25569.0更高层次的语言来表达这个时代。
因此,从顶部开始:
date::zoned_seconds
是一种更简单的编写date::zoned_time<std::chrono::seconds>
的方法。 它只是一个方便的类型定义。ddays
是一个自定义duration
单位,表示 1 天,double
。 这便于将标量输入x
直接转换为<chrono>
持续时间。 最好尽快进入<chrono>
型系统。纪元差异是 1970-01-01 和 1899-12-30 之间的时间量。 这些单位将是我编码的天数,但这是一个不重要的细节。
<chrono>
为您照顾这些单位。我使用
local_days
而不是sys_days
来计算纪元差异。 这在很大程度上是一种象征性的姿态,用于传达纪元是本地时间,而不是 UTC。 它不会对计算的常量的实际值产生差异。由于您措辞问题的方式,我认为您更喜欢代码中的日-月-年排序。 这是一个纯粹的风格选择。
如果你在 C++11 中写这篇文章,
excel_epoch
将不得不const
而不是constexpr
. 不同之处在于,C++11 必须在运行时计算此常量,而 C++14 及更高版本可以在编译时计算它。
从基于双的单位转换为基于积分的单位时,我喜欢使用
round
而不是duration_cast
。 不同之处在于,round
选择最接近的可表示值,而duration_cast
截断为零到最接近的可表示值。round
策略更有可能导致双精度表示和整数表示之间的稳定往返转换,而截断更有可能由于双精度表示中的舍入误差而暴露一次性差异。最后一行必须明确地将我们从基于双精度的单位转换为基于积分的单位,并且必须指定与返回类型匹配的
seconds
,但不必担心将天数转换为秒。最后一行使用
local_seconds
将duration
转换为time_point
,因为此duration
表示美国/芝加哥当地时间的度量,而不是 UTC 的度量。 这会将纪元固定为 1899-12-30 00:00:00 在美国/芝加哥,而不是 1899-12-30 00:00:00 UTC。结果不考虑闰秒。 这是正确的做法,因为 Excel 和
system_clock
都没有。 几乎每个基于计算机的计时协议都不计算闰秒。 这是对Unix时间的一个很好的描述。 如果您想转换为计算闰秒的系统,此库也可以做到这一点。 它被称为utc_clock
/utc_time
.结果确实考虑了芝加哥的夏令时,包括多年来夏令时的变化,这是 IANA 数据库所能做到的(据我所知,这是确切的)。
- 防止主数据类型C++的隐式转换
- 模板参数替换失败,并且未完成隐式转换
- 努力将整数转换为链表。不知道我在这里做错了什么
- HEX值到wchar_t字符(UTF-8)的转换
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将 Qvector<uint8_t> 转换为 QString
- 如何在cuSparse中使用cusparseXcoo2csr从coo转换为csc
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 在c++中使用nlohmann从类到json的转换
- 从"int*"强制转换为"unsigned int"会丢失精度错误
- 将Integer转换为4字节的unsined字符矢量(按大端字节顺序)
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 如何使用OpenCV将RBG图像转换为HSV,并将H、S和V值保存为C++中的3个独立图像
- 复制列表初始化的隐式转换的等级是多少
- 正在将指针转换为范围
- 如何将 time-uuid(存储在 boost uuid 中)转换为时间戳/自纪元以来的时间?
- c/在Unix时间和"Gregorian time"之间转换
- 在 boost::p osix_time::p time 和 mongo::D ate_t 之间转换
- srand(time(null))导致编译器警告:隐式转换会丢失整数精度
- 使用自定义时区将 boost::p osix_time::p time 转换为字符串