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 
32 
33 template <typename type> class duration_caster {
34 public:
35  typedef typename type::rep rep;
36  using period = typename type::period;
37 
38  using days = std::chrono::duration<uint_fast32_t, std::ratio<86400>>;
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  using type = std::chrono::time_point<std::chrono::system_clock, Duration>;
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 = time_point_cast<Duration>(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  // Get out microseconds, and make sure they are positive, to avoid bug in eastern hemisphere time zones
154  // (cfr. https://github.com/pybind/pybind11/issues/2417)
155  using us_t = duration<int, std::micro>;
156  auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1));
157  if (us.count() < 0)
158  us += seconds(1);
159 
160  // Subtract microseconds BEFORE `system_clock::to_time_t`, because:
161  // > If std::time_t has lower precision, it is implementation-defined whether the value is rounded or truncated.
162  // (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
163  std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
164  // this function uses static memory so it's best to copy it out asap just in case
165  // otherwise other code that is using localtime may break this (not just python code)
166  std::tm localtime = *std::localtime(&tt);
167 
168  return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
169  localtime.tm_mon + 1,
170  localtime.tm_mday,
171  localtime.tm_hour,
172  localtime.tm_min,
173  localtime.tm_sec,
174  us.count());
175  }
176  PYBIND11_TYPE_CASTER(type, _("datetime.datetime"));
177 };
178 
179 // Other clocks that are not the system clock are not measured as datetime.datetime objects
180 // since they are not measured on calendar time. So instead we just make them timedeltas
181 // Or if they have passed us a time as a float we convert that
182 template <typename Clock, typename Duration> class type_caster<std::chrono::time_point<Clock, Duration>>
183 : public duration_caster<std::chrono::time_point<Clock, Duration>> {
184 };
185 
186 template <typename Rep, typename Period> class type_caster<std::chrono::duration<Rep, Period>>
187 : public duration_caster<std::chrono::duration<Rep, Period>> {
188 };
189 
PyObject * ptr() const
Return the underlying PyObject * pointer.
Definition: pytypes.h:184
static handle cast(const type &src, return_value_policy, handle)
Definition: chrono.h:73
#define PyDateTime_DELTA_GET_SECONDS(o)
Definition: chrono.h:24
Definition: Half.h:150
#define PyDateTime_DELTA_GET_DAYS(o)
Definition: chrono.h:21
type::rep rep
Definition: chrono.h:35
typename std::chrono::duration< Rep, Period >::period period
Definition: chrono.h:36
static std::stringstream ss
Definition: testBTree.cpp:33
bool load(handle src, bool)
Definition: chrono.h:40
static const std::chrono::duration< rep, period > & get_duration(const std::chrono::duration< rep, period > &src)
Definition: chrono.h:64
#define PyDateTime_DELTA_GET_MICROSECONDS(o)
Definition: chrono.h:27
std::chrono::duration< uint_fast32_t, std::ratio< 86400 >> days
Definition: chrono.h:38
return_value_policy
Approach used to cast a previously unknown C++ instance into a Python object.
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:979
#define PYBIND11_NAMESPACE_END(name)
Definition: pytypes.h:897
static std::chrono::duration< rep, period > get_duration(const std::chrono::time_point< Clock, std::chrono::duration< rep, period >> &src)
Definition: chrono.h:69
#define PYBIND11_NAMESPACE_BEGIN(name)


gtsam
Author(s):
autogenerated on Sat May 8 2021 02:41:47