3434#include < pybind11/pybind11.h>
3535#include < pybind11/stl.h>
3636
37+ // Must NOT appear before at least one pybind11 include.
38+ #include < datetime.h> // Python datetime builtin.
39+
3740#include < cmath>
3841#include < cstdint>
3942#include < tuple>
@@ -107,6 +110,14 @@ struct type_caster<absl::TimeZone> {
107110 }
108111};
109112
113+ namespace internal {
114+ inline void EnsurePyDateTime_IMPORT () {
115+ if (PyDateTimeAPI == nullptr ) {
116+ PyDateTime_IMPORT;
117+ }
118+ }
119+ } // namespace internal
120+
110121// Convert between absl::Duration and python datetime.timedelta.
111122template <>
112123struct type_caster <absl::Duration> {
@@ -117,33 +128,41 @@ struct type_caster<absl::Duration> {
117128
118129 // Conversion part 1 (Python->C++)
119130 bool load (handle src, bool convert) {
131+ // As early as possible to avoid mid-process surprises.
132+ internal::EnsurePyDateTime_IMPORT ();
120133 if (!convert) {
121134 return false ;
122135 }
123136 if (PyFloat_Check (src.ptr ())) {
124137 value = absl::Seconds (src.cast <double >());
125138 return true ;
126- } else if (PyLong_Check (src.ptr ())) {
139+ }
140+ if (PyLong_Check (src.ptr ())) {
127141 value = absl::Seconds (src.cast <int64_t >());
128142 return true ;
129- } else {
130- // Ensure that absl::Duration is converted from a Python
131- // datetime.timedelta.
132- if (!hasattr (src, " days" ) || !hasattr (src, " seconds" ) ||
133- !hasattr (src, " microseconds" )) {
134- return false ;
135- }
136- auto py_duration_t = module::import (" datetime" ).attr (" timedelta" );
137- if (src == object (py_duration_t .attr (" max" ))) {
138- value = absl::InfiniteDuration ();
139- } else {
140- value = absl::Hours (24 * GetInt64Attr (src, " days" )) +
141- absl::Seconds (GetInt64Attr (src, " seconds" )) +
142- absl::Microseconds (GetInt64Attr (src, " microseconds" ));
143- }
143+ }
144+ if (PyTime_Check (src.ptr ())) {
145+ value = absl::Hours (PyDateTime_TIME_GET_HOUR (src.ptr ())) +
146+ absl::Minutes (PyDateTime_TIME_GET_MINUTE (src.ptr ())) +
147+ absl::Seconds (PyDateTime_TIME_GET_SECOND (src.ptr ())) +
148+ absl::Microseconds (PyDateTime_TIME_GET_MICROSECOND (src.ptr ()));
144149 return true ;
145150 }
146- return false ;
151+ // Ensure that absl::Duration is converted from a Python
152+ // datetime.timedelta.
153+ if (!hasattr (src, " days" ) || !hasattr (src, " seconds" ) ||
154+ !hasattr (src, " microseconds" )) {
155+ return false ;
156+ }
157+ auto py_duration_t = module::import (" datetime" ).attr (" timedelta" );
158+ if (src == object (py_duration_t .attr (" max" ))) {
159+ value = absl::InfiniteDuration ();
160+ } else {
161+ value = absl::Hours (24 * GetInt64Attr (src, " days" )) +
162+ absl::Seconds (GetInt64Attr (src, " seconds" )) +
163+ absl::Microseconds (GetInt64Attr (src, " microseconds" ));
164+ }
165+ return true ;
147166 }
148167
149168 // Conversion part 2 (C++ -> Python)
@@ -172,15 +191,32 @@ struct type_caster<absl::Time> {
172191
173192 // Conversion part 1 (Python->C++)
174193 bool load (handle src, bool convert) {
194+ // As early as possible to avoid mid-process surprises.
195+ internal::EnsurePyDateTime_IMPORT ();
175196 if (convert) {
176197 if (PyLong_Check (src.ptr ())) {
177198 value = absl::FromUnixSeconds (src.cast <int64_t >());
178199 return true ;
179- } else if (PyFloat_Check (src.ptr ())) {
200+ }
201+ if (PyFloat_Check (src.ptr ())) {
180202 value = absl::time_internal::FromUnixDuration (absl::Seconds (
181203 src.cast <double >()));
182204 return true ;
183205 }
206+ if (PyTime_Check (src.ptr ())) {
207+ // Adapted from absl/python/time.cc
208+ // Copyright 2018 The Abseil Authors.
209+ timeval tv{PyDateTime_TIME_GET_HOUR (src.ptr ()) * 3600 +
210+ PyDateTime_TIME_GET_MINUTE (src.ptr ()) * 60 +
211+ PyDateTime_TIME_GET_SECOND (src.ptr ()),
212+ PyDateTime_TIME_GET_MICROSECOND (src.ptr ())};
213+ value = absl::TimeFromTimeval (tv);
214+ int utcoffset;
215+ if (PyTzOffset (src.ptr (), &utcoffset)) {
216+ value += absl::Seconds (utcoffset);
217+ }
218+ return true ;
219+ }
184220 }
185221 if (!hasattr (src, " year" ) || !hasattr (src, " month" ) ||
186222 !hasattr (src, " day" )) {
@@ -233,6 +269,25 @@ struct type_caster<absl::Time> {
233269 auto py_datetime = py_from_timestamp (as_seconds, " tz" _a = py_timezone);
234270 return py_datetime.release ();
235271 }
272+
273+ private:
274+ // Adapted from absl/python/time.cc
275+ // Copyright 2018 The Abseil Authors.
276+ bool PyTzOffset (PyObject* datetime, int * utcoffset) {
277+ PyObject* offset = PyObject_CallMethod (datetime, " utcoffset" , nullptr );
278+
279+ if (!offset || !PyDelta_Check (offset)) {
280+ return false ;
281+ }
282+
283+ if (utcoffset) {
284+ *utcoffset = PyDateTime_DELTA_GET_SECONDS (offset) +
285+ PyDateTime_DELTA_GET_DAYS (offset) * 24 * 3600 ;
286+ }
287+
288+ Py_DECREF (offset);
289+ return true ;
290+ }
236291};
237292
238293template <typename CivilTimeUnitType>
0 commit comments