This recipe allows to transparently convert python's datetime.datetime objects to and from boost's boost::posix_time::ptime objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | #include <boost/python.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <datetime.h> // compile with -I/path/to/python/include
/**
* Convert boost::posix_ptime objects (ptime and time_duration)
* to/from python datetime objects (datetime and timedelta).
*
* Credits:
* http://libtorrent.svn.sourceforge.net/viewvc/libtorrent/trunk/bindings/python/src/datetime.cpp
* http://www.nabble.com/boost::posix_time::ptime-conversion-td16857866.html
*/
static long get_usecs(boost::posix_time::time_duration const& d)
{
static long resolution
= boost::posix_time::time_duration::ticks_per_second();
long fracsecs = d.fractional_seconds();
if (resolution > 1000000)
return fracsecs / (resolution / 1000000);
else
return fracsecs * (1000000 / resolution);
}
/* Convert ptime to/from python */
struct ptime_to_python_datetime
{
static PyObject* convert(boost::posix_time::ptime const& pt)
{
boost::gregorian::date date = pt.date();
boost::posix_time::time_duration td = pt.time_of_day();
return PyDateTime_FromDateAndTime((int)date.year(),
(int)date.month(),
(int)date.day(),
td.hours(),
td.minutes(),
td.seconds(),
get_usecs(td));
}
};
struct ptime_from_python_datetime
{
ptime_from_python_datetime()
{
boost::python::converter::registry::push_back(
&convertible,
&construct,
boost::python::type_id<boost::posix_time::ptime > ());
}
static void* convertible(PyObject * obj_ptr)
{
if ( ! PyDateTime_Check(obj_ptr))
return 0;
return obj_ptr;
}
static void construct(
PyObject* obj_ptr,
boost::python::converter::rvalue_from_python_stage1_data * data)
{
PyDateTime_DateTime const* pydate
= reinterpret_cast<PyDateTime_DateTime*>(obj_ptr);
// Create date object
boost::gregorian::date _date(PyDateTime_GET_YEAR(pydate),
PyDateTime_GET_MONTH(pydate),
PyDateTime_GET_DAY(pydate));
// Create time duration object
boost::posix_time::time_duration
_duration(PyDateTime_DATE_GET_HOUR(pydate),
PyDateTime_DATE_GET_MINUTE(pydate),
PyDateTime_DATE_GET_SECOND(pydate),
0);
// Set the usecs value
_duration += boost::posix_time::microseconds(PyDateTime_DATE_GET_MICROSECOND(pydate));
// Create posix time object
void* storage = (
(boost::python::converter::rvalue_from_python_storage<boost::posix_time::ptime>*)
data)->storage.bytes;
new (storage)
boost::posix_time::ptime(_date, _duration);
data->convertible = storage;
}
};
/* Convert time_duration to/from python */
struct tduration_to_python_delta
{
static PyObject* convert(boost::posix_time::time_duration d)
{
long days = d.hours() / 24;
if (days < 0)
days --;
long seconds = d.total_seconds() - days*(24*3600);
long usecs = get_usecs(d);
if (days < 0)
usecs = 1000000-1 - usecs;
return PyDelta_FromDSU(days, seconds, usecs);
}
};
/* Should support the negative values, but not the special boost time
durations */
struct tduration_from_python_delta
{
tduration_from_python_delta()
{
boost::python::converter::registry::push_back(
&convertible,
&construct,
boost::python::type_id<boost::posix_time::time_duration>());
}
static void* convertible(PyObject * obj_ptr)
{
if ( ! PyDelta_Check(obj_ptr))
return 0;
return obj_ptr;
}
static void construct(
PyObject* obj_ptr,
boost::python::converter::rvalue_from_python_stage1_data * data)
{
PyDateTime_Delta const* pydelta
= reinterpret_cast<PyDateTime_Delta*>(obj_ptr);
long days = pydelta->days;
bool is_negative = (days < 0);
if (is_negative)
days = -days;
// Create time duration object
boost::posix_time::time_duration
duration = boost::posix_time::hours(24)*days
+ boost::posix_time::seconds(pydelta->seconds)
+ boost::posix_time::microseconds(pydelta->microseconds);
if (is_negative)
duration = duration.invert_sign();
void* storage = (
(boost::python::converter::rvalue_from_python_storage<boost::posix_time::time_duration>*)
data)->storage.bytes;
new (storage)
boost::posix_time::time_duration(duration);
data->convertible = storage;
}
};
void bind_datetime()
{
PyDateTime_IMPORT;
ptime_from_python_datetime();
to_python_converter<
const boost::posix_time::ptime
, ptime_to_python_datetime
>();
tduration_from_python_delta();
to_python_converter<
const boost::posix_time::time_duration
, tduration_to_python_delta
>();
}
|
This recipe would work for both datetime/ptime and timedelta/time_duration objects. The discussion below illustrates the datetime/ptime case.
Imagine you want to export this C++ function to the python world:
void take_time(boost::posix_time::ptime const& t);
And this one:
boost::posix_time::ptime const& give_time();
With the code above, you just call bind_datetime() from inside a BOOST_PYTHON_MODULE(MyModule), and define your bindings as usual. The result is that you can do this from python:
MyModule.take_time(datetime.datetime.utcnow())
And if you do this:
d = MyModule.give_time()
print d, repr(d), type(d)
then python tells you:
2008-07-29 22:04:32.202591 datetime.datetime(2008, 7, 29, 22, 4, 32, 202591) <type 'datetime.datetime'>
which means that it sees a genuine datetime.datetime object.
This recipe is based on the libtorrent and HÃ¥kon Groven's implementations. It mainly adds the support for the "from python" conversion without the need to transit trhough the string representation, and it should make the "to python" implementation more low-level (faster ?) and with correct support for microseconds.
The code above is C++, but I think it is more a python recipe. To compile, don't forget to add both the Boost.Python and python compilation flags (-I...../include/python).
Meta: For some reason, I cannot update the "discussion" section... Is it a bug in the web app ?
Just updated (rev 10) with the timedelta/time_duration conversion.