Scarab  v2.9.1
Project 8 C++ Utility Library
chrono.h
Go to the documentation of this file.
1 /*
2  pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime
3 
4  Copyright (c) 2016 Trent Houliston <trent@houliston.me> and
5  Wenzel Jakob <wenzel.jakob@epfl.ch>
6 
7  All rights reserved. Use of this source code is governed by a
8  BSD-style license that can be found in the LICENSE file.
9 */
10 
11 #pragma once
12 
13 #include "pybind11.h"
14 #include <cmath>
15 #include <ctime>
16 #include <chrono>
17 #include <datetime.h>
18 
19 // Backport the PyDateTime_DELTA functions from Python3.3 if required
20 #ifndef PyDateTime_DELTA_GET_DAYS
21 #define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days)
22 #endif
23 #ifndef PyDateTime_DELTA_GET_SECONDS
24 #define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds)
25 #endif
26 #ifndef PyDateTime_DELTA_GET_MICROSECONDS
27 #define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds)
28 #endif
29 
31 NAMESPACE_BEGIN(detail)
32 
33 template <typename type> class duration_caster {
34 public:
35  typedef typename type::rep rep;
36  typedef typename type::period period;
37 
38  typedef std::chrono::duration<uint_fast32_t, std::ratio<86400>> days;
39 
40  bool load(handle src, bool) {
41  using namespace std::chrono;
42 
43  // Lazy initialise the PyDateTime import
44  if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
45 
46  if (!src) return false;
47  // If invoked with datetime.delta object
48  if (PyDelta_Check(src.ptr())) {
49  value = type(duration_cast<duration<rep, period>>(
51  + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
52  + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
53  return true;
54  }
55  // If invoked with a float we assume it is seconds and convert
56  else if (PyFloat_Check(src.ptr())) {
57  value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr()))));
58  return true;
59  }
60  else return false;
61  }
62 
63  // If this is a duration just return it back
64  static const std::chrono::duration<rep, period>& get_duration(const std::chrono::duration<rep, period> &src) {
65  return src;
66  }
67 
68  // If this is a time_point get the time_since_epoch
69  template <typename Clock> static std::chrono::duration<rep, period> get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) {
70  return src.time_since_epoch();
71  }
72 
73  static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) {
74  using namespace std::chrono;
75 
76  // Use overloaded function to get our duration from our source
77  // Works out if it is a duration or time_point and get the duration
78  auto d = get_duration(src);
79 
80  // Lazy initialise the PyDateTime import
81  if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
82 
83  // Declare these special duration types so the conversions happen with the correct primitive types (int)
84  using dd_t = duration<int, std::ratio<86400>>;
85  using ss_t = duration<int, std::ratio<1>>;
86  using us_t = duration<int, std::micro>;
87 
88  auto dd = duration_cast<dd_t>(d);
89  auto subd = d - dd;
90  auto ss = duration_cast<ss_t>(subd);
91  auto us = duration_cast<us_t>(subd - ss);
92  return PyDelta_FromDSU(dd.count(), ss.count(), us.count());
93  }
94 
95  PYBIND11_TYPE_CASTER(type, _("datetime.timedelta"));
96 };
97 
98 // This is for casting times on the system clock into datetime.datetime instances
99 template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
100 public:
101  typedef std::chrono::time_point<std::chrono::system_clock, Duration> type;
102  bool load(handle src, bool) {
103  using namespace std::chrono;
104 
105  // Lazy initialise the PyDateTime import
106  if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
107 
108  if (!src) return false;
109 
110  std::tm cal;
111  microseconds msecs;
112 
113  if (PyDateTime_Check(src.ptr())) {
114  cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
115  cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
116  cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
117  cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
118  cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
119  cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
120  cal.tm_isdst = -1;
121  msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
122  } else if (PyDate_Check(src.ptr())) {
123  cal.tm_sec = 0;
124  cal.tm_min = 0;
125  cal.tm_hour = 0;
126  cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
127  cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
128  cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
129  cal.tm_isdst = -1;
130  msecs = microseconds(0);
131  } else if (PyTime_Check(src.ptr())) {
132  cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
133  cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
134  cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
135  cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
136  cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
137  cal.tm_year = 70; // earliest available date for Python's datetime
138  cal.tm_isdst = -1;
139  msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
140  }
141  else return false;
142 
143  value = system_clock::from_time_t(std::mktime(&cal)) + msecs;
144  return true;
145  }
146 
147  static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, return_value_policy /* policy */, handle /* parent */) {
148  using namespace std::chrono;
149 
150  // Lazy initialise the PyDateTime import
151  if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
152 
153  std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src));
154  // this function uses static memory so it's best to copy it out asap just in case
155  // otherwise other code that is using localtime may break this (not just python code)
156  std::tm localtime = *std::localtime(&tt);
157 
158  // Declare these special duration types so the conversions happen with the correct primitive types (int)
159  using us_t = duration<int, std::micro>;
160 
161  return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
162  localtime.tm_mon + 1,
163  localtime.tm_mday,
164  localtime.tm_hour,
165  localtime.tm_min,
166  localtime.tm_sec,
167  (duration_cast<us_t>(src.time_since_epoch() % seconds(1))).count());
168  }
169  PYBIND11_TYPE_CASTER(type, _("datetime.datetime"));
170 };
171 
172 // Other clocks that are not the system clock are not measured as datetime.datetime objects
173 // since they are not measured on calendar time. So instead we just make them timedeltas
174 // Or if they have passed us a time as a float we convert that
175 template <typename Clock, typename Duration> class type_caster<std::chrono::time_point<Clock, Duration>>
176 : public duration_caster<std::chrono::time_point<Clock, Duration>> {
177 };
178 
179 template <typename Rep, typename Period> class type_caster<std::chrono::duration<Rep, Period>>
180 : public duration_caster<std::chrono::duration<Rep, Period>> {
181 };
182 
183 NAMESPACE_END(detail)
#define PYBIND11_NAMESPACE
Definition: detail/common.h:26
PyObject * ptr() const
Return the underlying PyObject * pointer.
Definition: pytypes.h:182
#define PyDateTime_DELTA_GET_SECONDS(o)
Definition: chrono.h:24
STL namespace.
constexpr descr< N - 1 > _(char const(&text)[N])
Definition: descr.h:54
#define PyDateTime_DELTA_GET_DAYS(o)
Definition: chrono.h:21
std::chrono::time_point< std::chrono::system_clock, Duration > type
Definition: chrono.h:101
std::chrono::duration< uint_fast32_t, std::ratio< 86400 > > days
Definition: chrono.h:38
std::chrono::duration< int, std::ratio_multiply< std::ratio< 24 >, std::chrono::hours::period > > days
Definition: date.h:147
#define NAMESPACE_END(name)
Definition: detail/common.h:16
return_value_policy
Approach used to cast a previously unknown C++ instance into a Python object.
static std::chrono::duration< rep, period > get_duration(const std::chrono::time_point< Clock, std::chrono::duration< rep, period >> &src)
Definition: chrono.h:69
def d(s)
Definition: mkdoc.py:69
static handle cast(const type &src, return_value_policy, handle)
Definition: chrono.h:73
bool load(handle src, bool)
Definition: chrono.h:40
const detail::type_info * type
Definition: cast.h:453
test_initializer chrono("chrono", test_submodule_chrono)
#define PyDateTime_DELTA_GET_MICROSECONDS(o)
Definition: chrono.h:27
static const std::chrono::duration< rep, period > & get_duration(const std::chrono::duration< rep, period > &src)
Definition: chrono.h:64
#define NAMESPACE_BEGIN(name)
Definition: detail/common.h:13
static handle cast(const std::chrono::time_point< std::chrono::system_clock, Duration > &src, return_value_policy, handle)
Definition: chrono.h:147
#define PYBIND11_TYPE_CASTER(type, py_name)
Definition: cast.h:942