$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
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