Boost logo

Boost :

Subject: [boost] OLE Automation date to local_date_time conversion
From: Matthew Chambers (matthew.chambers_at_[hidden])
Date: 2009-06-24 12:36:10


Hello fellow boosters,

Under MSVC boost::date_time supports conversion from FILETIME, so it
makes sense to also support conversion from the OLE automation date
format, helpfully defined at:
http://msdn.microsoft.com/en-us/library/system.datetime.fromoadate.aspx
> The d parameter is a double-precision floating-point number that
> represents a date as the number of days before or after the base date,
> midnight, 30 December 1899. The sign and integral part of d encode the
> date as a positive or negative day displacement from 30 December 1899,
> and the absolute value of the fractional part of d encodes the time of
> day as a fraction of a day displacement from midnight. d must be a
> value between negative 657435.0 through positive 2958466.0.

In fact, I don't see a good reason this function can't be included in a
non-MSVC build too since it doesn't depend on the Win32 API in any way.

The code to handle this conversion is quite ugly. Here is my code to do it:

namespace boost {
namespace date_time {

//! Create a time object from an OLE automation date value.
/*! Create a time object from an OLE automation date value.
 * The oa_date parameter is a double-precision floating-point number that
 * represents a date as the number of days before or after the base
date, midnight,
 * 30 December 1899. The sign and integral part of oa_date encode the
date as a
 * positive or negative day displacement from 30 December 1899, and the
absolute
 * value of the fractional part of oa_date encodes the time of day as a
fraction of a
 * day displacement from midnight. oa_date must be a value between
 * negative 657435.0 through positive 2958466.0.
 */
template<class time_type>
inline
time_type time_from_OADATE(double oa_date)
{
    typedef typename time_type::date_type date_type;
    typedef typename time_type::date_duration_type date_duration_type;
    typedef typename time_type::time_duration_type time_duration_type;
    using boost::math::modf;

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

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

}
}

Here are some basic unit tests:

template<typename time_type>
void test_time_from_OADATE(std::ostream* os_ = NULL)
{
    typedef typename time_type::date_type date_type;
    typedef typename time_type::date_duration_type date_duration_type;
    typedef typename time_type::time_duration_type time_duration_type;
    using namespace boost::date_time;

    if (os_) *os_ << "OADATE: 0.0 -> " <<
time_from_OADATE<time_type>(0.0) << endl;
    unit_assert(time_from_OADATE<time_type>(0.0) ==
time_type(date_type(1899, Dec, 30), time_duration_type(0,0,0)));

    if (os_) *os_ << "OADATE: 1.0 -> " <<
time_from_OADATE<time_type>(1.0) << endl;
    unit_assert(time_from_OADATE<time_type>(1.0) ==
time_type(date_type(1899, Dec, 31), time_duration_type(0,0,0)));

    if (os_) *os_ << "OADATE: -1.0 -> " <<
time_from_OADATE<time_type>(-1.0) << endl;
    unit_assert(time_from_OADATE<time_type>(-1.0) ==
time_type(date_type(1899, Dec, 29), time_duration_type(0,0,0)));

    if (os_) *os_ << "OADATE: 2.0 -> " <<
time_from_OADATE<time_type>(2.0) << endl;
    unit_assert(time_from_OADATE<time_type>(2.0) ==
time_type(date_type(1900, Jan, 1), time_duration_type(0,0,0)));

    if (os_) *os_ << "OADATE: 2.25 -> " <<
time_from_OADATE<time_type>(2.25) << endl;
    unit_assert(time_from_OADATE<time_type>(2.25) ==
time_type(date_type(1900, Jan, 1), time_duration_type(6,0,0)));

    if (os_) *os_ << "OADATE: -1.25 -> " <<
time_from_OADATE<time_type>(-1.25) << endl;
    unit_assert(time_from_OADATE<time_type>(-1.25) ==
time_type(date_type(1899, Dec, 29), time_duration_type(6,0,0)));
}

I made it templated because its sibling function in
boost\date_time\filetime_functions.hpp is templated, but it seems that
ptime is the only stock type that will work. Local_time doesn't have the
right constructors, at least.

Thoughts? What's the procedure to add this officially to date_time?

Thanks,
-Matt Chambers


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk