2014-12-14 3 views
1

Я хочу, чтобы преобразовать time_duration в формат DATE, что число дней с 1899 года, 12, 30.Преобразование TIME_DURATION в DATE

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; 
} 

Любые идеи, как эффективно решить эту проблему округления?

+0

Почему вы делаете все эти расчеты, когда именно для этого была создана библиотека? Я не понимаю. Я опубликовал гораздо более простой ответ. – sehe

+0

Вы можете обойтись без плавающих точек, и у вас не будет проблем округления и округления :) –

ответ

0

Это зависит от ваших обстоятельств.

Общая проблема заключается в том, что 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().

+0

Меня не волнует миллисекунды, поэтому добавление 1е-8 - это решение для меня. Благодаря! – Juergen

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

Смежные вопросы