Я хочу преобразовать TIME_DURATION к ДАТА формат, который число дней с 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.
Проверка ДАТЫ осуществляется с помощью этого метода, украденного из Вот:
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;
}
Любые идеи, как исправить эту проблему округления эффективно?
Это зависит от ваших обстоятельств.
Общая проблема заключается в том, что 1 / (24. * 60. * 60.)
не является точно представимым в виде двоичного числа с плавающей точкой (потому что 86400 не является степенью двойки). DATE
Вы получите почти точный результат, но будет ошибка округления. Иногда это означает, что это очень немного больше, иногда очень немного меньше, но на самом деле вы не можете многое сделать, чтобы сделать его более точным; это так хорошо, как вы можете получить. То, что вы видите несоответствие секунды, возможно, является проблемой с вашей проверкой, поскольку вы перестаете смотреть на секунды — если вы проверяете milliseconds
, вы, скорее всего, получите 999, что делает ошибку округления намного менее экстремальной. Это будет продолжаться в течение microseconds
и, возможно, nanoseconds
в зависимости от разрешения time_duration
,
Так что вполне возможно, что делать нечего, потому что с данными все в порядке. Однако, если вас не интересуют миллисекунды и более и вы хотите, чтобы значение секунд оставалось стабильным при конверсиях назад и вперед, самый простой способ добиться этого — добавить значение эпсилона:
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()
уровень.
Я мог бы пропустить некоторые сложности, но мне кажется, что это действительно просто:
#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
Видеть это Жить на Колиру