将time_duration转换为DATE

时间:2014-12-14 01:00:09

标签: c++ date datetime boost rounding-error

我想将time_duration格式转换为DATE格式,即自1899年12月30日以来的天数。

DATE date_from_duration(time_duration td)
{
   double days = td.hours()/24.+td.minutes()/(24.*60.)+td.seconds()/(24.*60.*60.);
   return days;
}

此代码几乎可以正常运行,但有时会出现舍入错误,time_duration(1007645, 15, 0)应该会在2014-12-12 00:15:00生成,但实际上是2014-12-12 00:14:59。

使用此方法检查DATE,从here

中窃取
ptime pTime_from_DATE(double date)
{
    using boost::math::modf;

    static const ptime::date_type base_date(1899, Dec, 30);
    static const ptime base_time(base_date, ptime::time_duration_type(0,0,0));

    int dayOffset, hourOffset, minuteOffset, secondOffset;
    double fraction = fabs(modf(date, &dayOffset)) * 24; // fraction = hours
    fraction = modf(fraction, &hourOffset) * 60; // fraction = minutes
    fraction = modf(fraction, &minuteOffset) * 60; // fraction = seconds
    modf(fraction, &secondOffset);
    ptime t(base_time);
    t += ptime::time_duration_type(hourOffset, minuteOffset, secondOffset);
    t += ptime::date_duration_type(dayOffset);
    return t;
}

如何有效地纠正这种舍入问题?

2 个答案:

答案 0 :(得分:1)

我可能会错过一些复杂性,但对我来说似乎很简单:

Live On Coliru

#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
using DATE = double;

boost::posix_time::ptime pTime_from_DATE(double date)
{
    static const boost::posix_time::ptime::date_type base_date(1899, boost::gregorian::Dec, 30);
    return boost::posix_time::ptime(
            base_date, 
            boost::posix_time::milliseconds(date * 1000 * 60 * 60 * 24));
}

int main() {
    boost::posix_time::time_duration duration(1007645, 15, 0);

    DATE date = duration.total_milliseconds() / 1000.0 / 60 / 60 / 24;

    std::cout << date << ": " << pTime_from_DATE(date);
}

打印

41985.2: 2014-Dec-12 05:15:00

查看 Live On Coliru

答案 1 :(得分:0)

这取决于你的情况。

一般问题是1 / (24. * 60. * 60.)不能完全表示为二进制浮点数(因为86400不是2的幂)。你得到的DATE非常精确,但会出现舍入错误。有时候这意味着它会更多,有时甚至更少,但实际上并没有太多可以让它更精确;它就像你能得到的一样完美。您看到一秒钟的差异可以说是您的支票存在问题,因为您停止查看秒数 - 如果您检查milliseconds,您可能会得到999,这使得舍入错误看起来很多不太极端。这将继续microseconds,可能会nanoseconds,具体取决于time_duration的解决方案。

所以很可能无事可做,因为数据没问题。但是,如果你不关心毫秒甚至更长时间并且只希望秒值在来回转换中保持稳定,那么最简单的方法是添加epsilon值:

DATE date_from_duration(time_duration td)
{
   double days =
       td.hours  () /  24.
     + td.minutes() / (24. * 60.)
     + td.seconds() / (24. * 60. * 60.)
     + 1e-8; // add roughly a millisecond
   return days;
}

这增加了整体舍入误差,但确保错误处于“安全”方向,即将其转换回time_duration将得到相同的seconds()值,并且可见的更改将是在milliseconds()级别。