Library of Assembled Shared Sources
 
Loading...
Searching...
No Matches
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-2026 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 tz = PyDateTime_DATE_GET_TZINFO(obj);
168 }
169
170 if (tz == Py_None)
171 {
172 // naive datetime is assumed to be local time. dates are always naive too.
173 const std::time_t t = mktime(&time);
174 if (t < 0)
175 {
176 PyErr_SetString(PyExc_ValueError, "out of range");
177 return 1;
178 }
179 v = TClock::from_time_t(t) + uSec;
180 }
181 else
182 {
183 TPyObjPtr timedelta{ PyObject_CallMethod(tz, "utcoffset", "O", obj) };
184 if (!timedelta)
185 {
186 return 1;
187 }
188 std::chrono::system_clock::duration utcoffset{};
189 if (PyExportTraits<std::chrono::system_clock::duration>::get(timedelta.get(), utcoffset) != 0)
190 {
191 return 1;
192 }
193#if LASS_PLATFORM_TYPE == LASS_PLATFORM_TYPE_WIN32
194 const std::time_t t = _mkgmtime(&time);
195#else
196 const std::time_t t = timegm(&time);
197#endif
198 if (t < 0)
199 {
200 PyErr_SetString(PyExc_ValueError, "out of range");
201 return 1;
202 }
203 v = TClock::from_time_t(t) + uSec - utcoffset;
204 }
205
206 return 0;
207}
208
209
210
211#ifdef LASS_HAVE_STD_CHRONO_CPP20
212
213PyObject* PyExportTraits<std::chrono::utc_clock::time_point>::build(const std::chrono::utc_clock::time_point& v)
214{
215 using namespace std::chrono_literals;
216
217 if (!PyDateTimeAPI)
218 {
219 PyDateTime_IMPORT;
220 }
221
222 using TMicroSeconds = std::chrono::duration<int, std::micro>;
223
224 const auto sysTime = std::chrono::utc_clock::to_sys(v);
225 const TMicroSeconds uSec = std::chrono::duration_cast<TMicroSeconds>(sysTime.time_since_epoch() % 1s);
226 const std::time_t time = std::chrono::system_clock::to_time_t(sysTime - uSec);
227
228 std::tm local;
229#if LASS_PLATFORM_TYPE == LASS_PLATFORM_TYPE_WIN32
230 const errno_t err = gmtime_s(&local, &time);
231#else
233 gmtime_r(&time, &local);
234 const int err = util::impl::lass_errno();
235#endif
236 if (err)
237 {
238 PyErr_SetString(PyExc_ValueError, util::impl::lass_strerror(err).c_str());
239 return nullptr;
240 }
241
242 PyObject* tz = PyDateTime_TimeZone_UTC;
243
244 return PyDateTimeAPI->DateTime_FromDateAndTime(
245 1900 + local.tm_year,
246 1 + local.tm_mon,
247 local.tm_mday,
248 local.tm_hour,
249 local.tm_min,
250 local.tm_sec,
251 uSec.count(),
252 tz,
253 PyDateTimeAPI->DateTimeType
254 );
255}
256
257
258
259int PyExportTraits<std::chrono::utc_clock::time_point>::get(PyObject* obj, std::chrono::utc_clock::time_point& v)
260{
261 std::chrono::system_clock::time_point sysTime;
262 if (PyExportTraits<std::chrono::system_clock::time_point>::get(obj, sysTime) != 0)
263 {
264 return 1;
265 }
266 v = std::chrono::clock_cast<std::chrono::utc_clock>(sysTime);
267 return 0;
268}
269
270
271
272PyObject* PyExportTraits<std::chrono::gps_clock::time_point>::build(const std::chrono::gps_clock::time_point& v)
273{
274 auto utcTime = std::chrono::clock_cast<std::chrono::utc_clock>(v);
275 return PyExportTraits<std::chrono::utc_clock::time_point>::build(utcTime);
276}
277
278
279
280int PyExportTraits<std::chrono::gps_clock::time_point>::get(PyObject* obj, std::chrono::gps_clock::time_point& v)
281{
282 std::chrono::system_clock::time_point sysTime;
283 if (PyExportTraits<std::chrono::system_clock::time_point>::get(obj, sysTime) != 0)
284 {
285 return 1;
286 }
287 v = std::chrono::clock_cast<std::chrono::gps_clock>(sysTime);
288 return 0;
289}
290
291
292
293PyObject* PyExportTraits<std::chrono::tai_clock::time_point>::build(const std::chrono::tai_clock::time_point& v)
294{
295 auto utcTime = std::chrono::clock_cast<std::chrono::utc_clock>(v);
296 return PyExportTraits<std::chrono::utc_clock::time_point>::build(utcTime);
297}
298
299
300
301int PyExportTraits<std::chrono::tai_clock::time_point>::get(PyObject* obj, std::chrono::tai_clock::time_point& v)
302{
303 std::chrono::system_clock::time_point sysTime;
304 if (PyExportTraits<std::chrono::system_clock::time_point>::get(obj, sysTime) != 0)
305 {
306 return 1;
307 }
308 v = std::chrono::clock_cast<std::chrono::tai_clock>(sysTime);
309 return 0;
310}
311
312
313
314PyObject* PyExportTraits<std::chrono::file_clock::time_point>::build(const std::chrono::file_clock::time_point& v)
315{
316 auto utcTime = std::chrono::clock_cast<std::chrono::utc_clock>(v);
317 return PyExportTraits<std::chrono::utc_clock::time_point>::build(utcTime);
318}
319
320
321
322int PyExportTraits<std::chrono::file_clock::time_point>::get(PyObject* obj, std::chrono::file_clock::time_point& v)
323{
324 std::chrono::system_clock::time_point sysTime;
325 if (PyExportTraits<std::chrono::system_clock::time_point>::get(obj, sysTime) != 0)
326 {
327 return 1;
328 }
329 v = std::chrono::clock_cast<std::chrono::file_clock>(sysTime);
330 return 0;
331}
332
333
334
335PyObject* PyExportTraits<std::chrono::year_month_day>::build(const std::chrono::year_month_day& v)
336{
337 if (!PyDateTimeAPI)
338 {
339 PyDateTime_IMPORT;
340 }
341 return PyDate_FromDate(
342 static_cast<int>(v.year()),
343 static_cast<unsigned>(v.month()),
344 static_cast<unsigned>(v.day())
345 );
346}
347
348
349
350int PyExportTraits<std::chrono::year_month_day>::get(PyObject* obj, std::chrono::year_month_day& v)
351{
352 if (!PyDateTimeAPI)
353 {
354 PyDateTime_IMPORT;
355 }
356 if (!PyDate_Check(obj))
357 {
358 PyErr_SetString(PyExc_TypeError, "not a datetime.date");
359 return 1;
360 }
361 v = std::chrono::year_month_day
362 {
363 std::chrono::year{ PyDateTime_GET_YEAR(obj) },
364 std::chrono::month{ PyDateTime_GET_MONTH(obj) },
365 std::chrono::day{ PyDateTime_GET_DAY(obj) }
366 };
367 return 0;
368}
369
370
371#endif
372
373}
374}
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.