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 
15 #include <chrono>
16 #include <cmath>
17 #include <ctime>
18 #include <datetime.h>
19 #include <mutex>
20 
23 
24 template <typename type>
26 public:
27  using rep = typename type::rep;
28  using period = typename type::period;
29 
30  // signed 25 bits required by the standard.
31  using days = std::chrono::duration<int_least32_t, std::ratio<86400>>;
32 
33  bool load(handle src, bool) {
34  using namespace std::chrono;
35 
36  // Lazy initialise the PyDateTime import
37  if (!PyDateTimeAPI) {
38  PyDateTime_IMPORT;
39  }
40 
41  if (!src) {
42  return false;
43  }
44  // If invoked with datetime.delta object
45  if (PyDelta_Check(src.ptr())) {
46  value = type(duration_cast<duration<rep, period>>(
47  days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
48  + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
49  + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
50  return true;
51  }
52  // If invoked with a float we assume it is seconds and convert
53  if (PyFloat_Check(src.ptr())) {
54  value = type(duration_cast<duration<rep, period>>(
55  duration<double>(PyFloat_AsDouble(src.ptr()))));
56  return true;
57  }
58  return false;
59  }
60 
61  // If this is a duration just return it back
62  static const std::chrono::duration<rep, period> &
63  get_duration(const std::chrono::duration<rep, period> &src) {
64  return src;
65  }
66 
67  // If this is a time_point get the time_since_epoch
68  template <typename Clock>
69  static std::chrono::duration<rep, period>
70  get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) {
71  return src.time_since_epoch();
72  }
73 
74  static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) {
75  using namespace std::chrono;
76 
77  // Use overloaded function to get our duration from our source
78  // Works out if it is a duration or time_point and get the duration
79  auto d = get_duration(src);
80 
81  // Lazy initialise the PyDateTime import
82  if (!PyDateTimeAPI) {
83  PyDateTime_IMPORT;
84  }
85 
86  // Declare these special duration types so the conversions happen with the correct
87  // primitive types (int)
88  using dd_t = duration<int, std::ratio<86400>>;
89  using ss_t = duration<int, std::ratio<1>>;
90  using us_t = duration<int, std::micro>;
91 
92  auto dd = duration_cast<dd_t>(d);
93  auto subd = d - dd;
94  auto ss = duration_cast<ss_t>(subd);
95  auto us = duration_cast<us_t>(subd - ss);
96  return PyDelta_FromDSU(dd.count(), ss.count(), us.count());
97  }
98 
99  PYBIND11_TYPE_CASTER(type, const_name("datetime.timedelta"));
100 };
101 
102 inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
103 #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || defined(_MSC_VER)
104  if (localtime_s(buf, time))
105  return nullptr;
106  return buf;
107 #else
108  static std::mutex mtx;
109  std::lock_guard<std::mutex> lock(mtx);
110  std::tm *tm_ptr = std::localtime(time);
111  if (tm_ptr != nullptr) {
112  *buf = *tm_ptr;
113  }
114  return tm_ptr;
115 #endif
116 }
117 
118 // This is for casting times on the system clock into datetime.datetime instances
119 template <typename Duration>
120 class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
121 public:
122  using type = std::chrono::time_point<std::chrono::system_clock, Duration>;
123  bool load(handle src, bool) {
124  using namespace std::chrono;
125 
126  // Lazy initialise the PyDateTime import
127  if (!PyDateTimeAPI) {
128  PyDateTime_IMPORT;
129  }
130 
131  if (!src) {
132  return false;
133  }
134 
135  std::tm cal;
136  microseconds msecs;
137 
138  if (PyDateTime_Check(src.ptr())) {
139  cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
140  cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
141  cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
142  cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
143  cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
144  cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
145  cal.tm_isdst = -1;
146  msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
147  } else if (PyDate_Check(src.ptr())) {
148  cal.tm_sec = 0;
149  cal.tm_min = 0;
150  cal.tm_hour = 0;
151  cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
152  cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
153  cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
154  cal.tm_isdst = -1;
155  msecs = microseconds(0);
156  } else if (PyTime_Check(src.ptr())) {
157  cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
158  cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
159  cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
160  cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
161  cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
162  cal.tm_year = 70; // earliest available date for Python's datetime
163  cal.tm_isdst = -1;
164  msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
165  } else {
166  return false;
167  }
168 
169  value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs);
170  return true;
171  }
172 
173  static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src,
174  return_value_policy /* policy */,
175  handle /* parent */) {
176  using namespace std::chrono;
177 
178  // Lazy initialise the PyDateTime import
179  if (!PyDateTimeAPI) {
180  PyDateTime_IMPORT;
181  }
182 
183  // Get out microseconds, and make sure they are positive, to avoid bug in eastern
184  // hemisphere time zones (cfr. https://github.com/pybind/pybind11/issues/2417)
185  using us_t = duration<int, std::micro>;
186  auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1));
187  if (us.count() < 0) {
188  us += seconds(1);
189  }
190 
191  // Subtract microseconds BEFORE `system_clock::to_time_t`, because:
192  // > If std::time_t has lower precision, it is implementation-defined whether the value is
193  // rounded or truncated. (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
194  std::time_t tt
195  = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
196 
197  std::tm localtime;
198  std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
199  if (!localtime_ptr) {
200  throw cast_error("Unable to represent system_clock in local time");
201  }
202  return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
203  localtime.tm_mon + 1,
204  localtime.tm_mday,
205  localtime.tm_hour,
206  localtime.tm_min,
207  localtime.tm_sec,
208  us.count());
209  }
210  PYBIND11_TYPE_CASTER(type, const_name("datetime.datetime"));
211 };
212 
213 // Other clocks that are not the system clock are not measured as datetime.datetime objects
214 // since they are not measured on calendar time. So instead we just make them timedeltas
215 // Or if they have passed us a time as a float we convert that
216 template <typename Clock, typename Duration>
217 class type_caster<std::chrono::time_point<Clock, Duration>>
218  : public duration_caster<std::chrono::time_point<Clock, Duration>> {};
219 
220 template <typename Rep, typename Period>
221 class type_caster<std::chrono::duration<Rep, Period>>
222  : public duration_caster<std::chrono::duration<Rep, Period>> {};
223 
std::tm * localtime_thread_safe(const std::time_t *time, std::tm *buf)
Definition: chrono.h:102
static handle cast(const type &src, return_value_policy, handle)
Definition: chrono.h:74
Definition: BFloat16.h:88
typename std::chrono::duration< Rep, Period > ::period period
Definition: chrono.h:28
PYBIND11_TYPE_CASTER(type, const_name("datetime.timedelta"))
#define time
static std::stringstream ss
Definition: testBTree.cpp:31
bool load(handle src, bool)
Definition: chrono.h:33
std::chrono::duration< int_least32_t, std::ratio< 86400 > > days
Definition: chrono.h:31
static const std::chrono::duration< rep, period > & get_duration(const std::chrono::duration< rep, period > &src)
Definition: chrono.h:63
typename std::chrono::duration< Rep, Period > ::rep rep
Definition: chrono.h:27
PyObject * ptr() const
Return the underlying PyObject * pointer.
Definition: pytypes.h:238
constexpr descr< N - 1 > const_name(char const (&text)[N])
Definition: descr.h:60
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:173
#define PYBIND11_NAMESPACE_END(name)
Definition: pytypes.h:1370
static std::chrono::duration< rep, period > get_duration(const std::chrono::time_point< Clock, std::chrono::duration< rep, period >> &src)
Definition: chrono.h:70
#define PYBIND11_NAMESPACE_BEGIN(name)


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:34:01