Library of Assembled Shared Sources
export_traits_chrono.cpp
Go to the documentation of this file.
1/** @file
2* @author Bram de Greve (bram@cocamware.com)
3* @author Tom De Muer (tom@cocamware.com)
4*
5* *** BEGIN LICENSE INFORMATION ***
6*
7* The contents of this file are subject to the Common Public Attribution License
8* Version 1.0 (the "License"); you may not use this file except in compliance with
9* the License. You may obtain a copy of the License at
10* http://lass.sourceforge.net/cpal-license. The License is based on the
11* Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12* use of software over a computer network and provide for limited attribution for
13* the Original Developer. In addition, Exhibit A has been modified to be consistent
14* with Exhibit B.
15*
16* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18* language governing rights and limitations under the License.
19*
20* The Original Code is LASS - Library of Assembled Shared Sources.
21*
22* The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23* The Original Developer is the Initial Developer.
24*
25* All portions of the code written by the Initial Developer are:
26* Copyright (C) 2022-2024 the Initial Developer.
27* All Rights Reserved.
28*
29* Contributor(s):
30*
31* Alternatively, the contents of this file may be used under the terms of the
32* GNU General Public License Version 2 or later (the GPL), in which case the
33* provisions of GPL are applicable instead of those above. If you wish to allow use
34* of your version of this file only under the terms of the GPL and not to allow
35* others to use your version of this file under the CPAL, indicate your decision by
36* deleting the provisions above and replace them with the notice and other
37* provisions required by the GPL License. If you do not delete the provisions above,
38* a recipient may use your version of this file under either the CPAL or the GPL.
39*
40* *** END LICENSE INFORMATION ***
41*/
42
43#include "python_common.h"
45
46#include <datetime.h>
47
48
49namespace lass
50{
51namespace python
52{
53
54namespace impl
55{
56
57PyObject* buildTimedelta(int days, int secs, int usecs)
58{
59 if (!PyDateTimeAPI)
60 {
61 PyDateTime_IMPORT;
62 }
63 return PyDelta_FromDSU(days, secs, usecs);
64}
65
66int getTimedelta(PyObject* obj, int &days, int &secs, int &usecs)
67{
68 if (!PyDateTimeAPI)
69 {
70 PyDateTime_IMPORT;
71 }
72 if (!PyDelta_Check(obj))
73 {
74 PyErr_SetString(PyExc_TypeError, "not a datetime.timedelta");
75 return 1;
76 }
77 days = PyDateTime_DELTA_GET_DAYS(obj);
78 secs = PyDateTime_DELTA_GET_SECONDS(obj);
79 usecs = PyDateTime_DELTA_GET_MICROSECONDS(obj);
80 return 0;
81}
82
83}
84
85
86
88{
89 using namespace std::chrono_literals;
90
91 if (!PyDateTimeAPI)
92 {
93 PyDateTime_IMPORT;
94 }
95
96 using TMicroSeconds = std::chrono::duration<int, std::micro>;
97
98 const TMicroSeconds uSec = std::chrono::duration_cast<TMicroSeconds>(v.time_since_epoch() % 1s);
99 const std::time_t time = TClock::to_time_t(v - uSec);
100
101 std::tm local;
102#if LASS_PLATFORM_TYPE == LASS_PLATFORM_TYPE_WIN32
103 const errno_t err = localtime_s(&local, &time);
104#else
106 localtime_r(&time, &local);
107 const int err = util::impl::lass_errno();
108#endif
109 if (err)
110 {
111 PyErr_SetString(PyExc_ValueError, util::impl::lass_strerror(err).c_str());
112 return nullptr;
113 }
114
115 return PyDateTime_FromDateAndTime(
116 1900 + local.tm_year,
117 1 + local.tm_mon,
118 local.tm_mday,
119 local.tm_hour,
120 local.tm_min,
121 local.tm_sec,
122 uSec.count()
123 );
124}
125
126
127
128int PyExportTraits<std::chrono::time_point<std::chrono::system_clock>>::get(PyObject* obj, TTimePoint& v)
129{
130 using namespace std::chrono_literals;
131
132 if (!PyDateTimeAPI)
133 {
134 PyDateTime_IMPORT;
135 }
136 if (!PyDate_Check(obj))
137 {
138 PyErr_SetString(PyExc_TypeError, "not a datetime.date or datetime.datetime");
139 return 1;
140 }
141
142 std::tm time {
143 0, // tm_sec
144 0, // tm_min
145 0, // tm_hour
146 PyDateTime_GET_DAY(obj), // tm_mday
147 PyDateTime_GET_MONTH(obj) - 1, // tm_mon
148 PyDateTime_GET_YEAR(obj) - 1900, // tm_year
149 0, // tm_wday
150 0, // tm_yday
151 -1, // tm_isdst
152#if LASS_PLATFORM_TYPE != LASS_PLATFORM_TYPE_WIN32
153 0, // tm_gmtoff
154 nullptr, // tm_zone
155#endif
156 };
157 std::chrono::microseconds uSec{};
158 PyObject* tz = Py_None;
159
160 if (PyDateTime_Check(obj))
161 {
162 time.tm_hour = PyDateTime_DATE_GET_HOUR(obj);
163 time.tm_min = PyDateTime_DATE_GET_MINUTE(obj);
164 time.tm_sec = PyDateTime_DATE_GET_SECOND(obj);
165 uSec = std::chrono::microseconds(PyDateTime_DATE_GET_MICROSECOND(obj));
166
167#if PY_VERSION_HEX >= 0x030a0000 // >= 3.10
168 tz = PyDateTime_DATE_GET_TZINFO(obj);
169#else
170 TPyObjPtr tz_(PyObject_GetAttrString(obj, "tzinfo"));
171 if (!tz_)
172 {
173 return 1;
174 }
175 tz = tz_.get();
176#endif
177 }
178
179 if (tz == Py_None)
180 {
181 // naive datetime is assumed to be local time. dates are always naive too.
182 const std::time_t t = mktime(&time);
183 if (t < 0)
184 {
185 PyErr_SetString(PyExc_ValueError, "out of range");
186 return 1;
187 }
188 v = TClock::from_time_t(t) + uSec;
189 }
190 else
191 {
192 TPyObjPtr timedelta{ PyObject_CallMethod(tz, "utcoffset", "O", obj) };
193 if (!timedelta)
194 {
195 return 1;
196 }
197 std::chrono::system_clock::duration utcoffset{};
198 if (PyExportTraits<std::chrono::system_clock::duration>::get(timedelta.get(), utcoffset) != 0)
199 {
200 return 1;
201 }
202#if LASS_PLATFORM_TYPE == LASS_PLATFORM_TYPE_WIN32
203 const std::time_t t = _mkgmtime(&time);
204#else
205 const std::time_t t = timegm(&time);
206#endif
207 if (t < 0)
208 {
209 PyErr_SetString(PyExc_ValueError, "out of range");
210 return 1;
211 }
212 v = TClock::from_time_t(t) + uSec - utcoffset;
213 }
214
215 return 0;
216}
217
218
219
220#ifdef LASS_HAVE_STD_CHRONO_CPP20
221
222PyObject* PyExportTraits<std::chrono::utc_clock::time_point>::build(const std::chrono::utc_clock::time_point& v)
223{
224 using namespace std::chrono_literals;
225
226 if (!PyDateTimeAPI)
227 {
228 PyDateTime_IMPORT;
229 }
230
231 using TMicroSeconds = std::chrono::duration<int, std::micro>;
232
233 const auto sysTime = std::chrono::utc_clock::to_sys(v);
234 const TMicroSeconds uSec = std::chrono::duration_cast<TMicroSeconds>(sysTime.time_since_epoch() % 1s);
235 const std::time_t time = std::chrono::system_clock::to_time_t(sysTime - uSec);
236
237 std::tm local;
238#if LASS_PLATFORM_TYPE == LASS_PLATFORM_TYPE_WIN32
239 const errno_t err = gmtime_s(&local, &time);
240#else
242 gmtime_r(&time, &local);
243 const int err = util::impl::lass_errno();
244#endif
245 if (err)
246 {
247 PyErr_SetString(PyExc_ValueError, util::impl::lass_strerror(err).c_str());
248 return nullptr;
249 }
250
251 PyObject* tz = PyDateTime_TimeZone_UTC;
252
253 return PyDateTimeAPI->DateTime_FromDateAndTime(
254 1900 + local.tm_year,
255 1 + local.tm_mon,
256 local.tm_mday,
257 local.tm_hour,
258 local.tm_min,
259 local.tm_sec,
260 uSec.count(),
261 tz,
262 PyDateTimeAPI->DateTimeType
263 );
264}
265
266
267
268int PyExportTraits<std::chrono::utc_clock::time_point>::get(PyObject* obj, std::chrono::utc_clock::time_point& v)
269{
270 std::chrono::system_clock::time_point sysTime;
271 if (PyExportTraits<std::chrono::system_clock::time_point>::get(obj, sysTime) != 0)
272 {
273 return 1;
274 }
275 v = std::chrono::clock_cast<std::chrono::utc_clock>(sysTime);
276 return 0;
277}
278
279
280
281PyObject* PyExportTraits<std::chrono::gps_clock::time_point>::build(const std::chrono::gps_clock::time_point& v)
282{
283 auto utcTime = std::chrono::clock_cast<std::chrono::utc_clock>(v);
284 return PyExportTraits<std::chrono::utc_clock::time_point>::build(utcTime);
285}
286
287
288
289int PyExportTraits<std::chrono::gps_clock::time_point>::get(PyObject* obj, std::chrono::gps_clock::time_point& v)
290{
291 std::chrono::system_clock::time_point sysTime;
292 if (PyExportTraits<std::chrono::system_clock::time_point>::get(obj, sysTime) != 0)
293 {
294 return 1;
295 }
296 v = std::chrono::clock_cast<std::chrono::gps_clock>(sysTime);
297 return 0;
298}
299
300
301
302PyObject* PyExportTraits<std::chrono::tai_clock::time_point>::build(const std::chrono::tai_clock::time_point& v)
303{
304 auto utcTime = std::chrono::clock_cast<std::chrono::utc_clock>(v);
305 return PyExportTraits<std::chrono::utc_clock::time_point>::build(utcTime);
306}
307
308
309
310int PyExportTraits<std::chrono::tai_clock::time_point>::get(PyObject* obj, std::chrono::tai_clock::time_point& v)
311{
312 std::chrono::system_clock::time_point sysTime;
313 if (PyExportTraits<std::chrono::system_clock::time_point>::get(obj, sysTime) != 0)
314 {
315 return 1;
316 }
317 v = std::chrono::clock_cast<std::chrono::tai_clock>(sysTime);
318 return 0;
319}
320
321
322
323PyObject* PyExportTraits<std::chrono::file_clock::time_point>::build(const std::chrono::file_clock::time_point& v)
324{
325 auto utcTime = std::chrono::clock_cast<std::chrono::utc_clock>(v);
326 return PyExportTraits<std::chrono::utc_clock::time_point>::build(utcTime);
327}
328
329
330
331int PyExportTraits<std::chrono::file_clock::time_point>::get(PyObject* obj, std::chrono::file_clock::time_point& v)
332{
333 std::chrono::system_clock::time_point sysTime;
334 if (PyExportTraits<std::chrono::system_clock::time_point>::get(obj, sysTime) != 0)
335 {
336 return 1;
337 }
338 v = std::chrono::clock_cast<std::chrono::file_clock>(sysTime);
339 return 0;
340}
341
342
343
344PyObject* PyExportTraits<std::chrono::year_month_day>::build(const std::chrono::year_month_day& v)
345{
346 if (!PyDateTimeAPI)
347 {
348 PyDateTime_IMPORT;
349 }
350 return PyDate_FromDate(
351 static_cast<int>(v.year()),
352 static_cast<unsigned>(v.month()),
353 static_cast<unsigned>(v.day())
354 );
355}
356
357
358
359int PyExportTraits<std::chrono::year_month_day>::get(PyObject* obj, std::chrono::year_month_day& v)
360{
361 if (!PyDateTimeAPI)
362 {
363 PyDateTime_IMPORT;
364 }
365 if (!PyDate_Check(obj))
366 {
367 PyErr_SetString(PyExc_TypeError, "not a datetime.date");
368 return 1;
369 }
370 v = std::chrono::year_month_day
371 {
372 std::chrono::year{ PyDateTime_GET_YEAR(obj) },
373 std::chrono::month{ PyDateTime_GET_MONTH(obj) },
374 std::chrono::day{ PyDateTime_GET_DAY(obj) }
375 };
376 return 0;
377}
378
379
380#endif
381
382}
383}
const std::string lass_strerror(int errnum)
returns message associated to an CLIB error code
int lass_errno()
returns CLIB errno
void lass_reset_errno()
sets CLIB errno to zero.
PyObjectPtr< PyObject >::Type TPyObjPtr
PyObjectPtr to a PyObject.
Comprehensive C++ to Python binding library.
Library for Assembled Shared Sources.
Definition config.h:53
by copy, general case assumes shadow type or PyObjectPlus based type.