Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:06:14

0001 /*
0002     pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime
0003 
0004     Copyright (c) 2016 Trent Houliston <trent@houliston.me> and
0005                        Wenzel Jakob <wenzel.jakob@epfl.ch>
0006 
0007     All rights reserved. Use of this source code is governed by a
0008     BSD-style license that can be found in the LICENSE file.
0009 */
0010 
0011 #pragma once
0012 
0013 #include "pybind11.h"
0014 
0015 #include <chrono>
0016 #include <cmath>
0017 #include <ctime>
0018 #include <datetime.h>
0019 #include <mutex>
0020 
0021 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0022 PYBIND11_NAMESPACE_BEGIN(detail)
0023 
0024 template <typename type>
0025 class duration_caster {
0026 public:
0027     using rep = typename type::rep;
0028     using period = typename type::period;
0029 
0030     // signed 25 bits required by the standard.
0031     using days = std::chrono::duration<int_least32_t, std::ratio<86400>>;
0032 
0033     bool load(handle src, bool) {
0034         using namespace std::chrono;
0035 
0036         // Lazy initialise the PyDateTime import
0037         if (!PyDateTimeAPI) {
0038             PyDateTime_IMPORT;
0039         }
0040 
0041         if (!src) {
0042             return false;
0043         }
0044         // If invoked with datetime.delta object
0045         if (PyDelta_Check(src.ptr())) {
0046             value = type(duration_cast<duration<rep, period>>(
0047                 days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
0048                 + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
0049                 + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
0050             return true;
0051         }
0052         // If invoked with a float we assume it is seconds and convert
0053         if (PyFloat_Check(src.ptr())) {
0054             value = type(duration_cast<duration<rep, period>>(
0055                 duration<double>(PyFloat_AsDouble(src.ptr()))));
0056             return true;
0057         }
0058         return false;
0059     }
0060 
0061     // If this is a duration just return it back
0062     static const std::chrono::duration<rep, period> &
0063     get_duration(const std::chrono::duration<rep, period> &src) {
0064         return src;
0065     }
0066 
0067     // If this is a time_point get the time_since_epoch
0068     template <typename Clock>
0069     static std::chrono::duration<rep, period>
0070     get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) {
0071         return src.time_since_epoch();
0072     }
0073 
0074     static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) {
0075         using namespace std::chrono;
0076 
0077         // Use overloaded function to get our duration from our source
0078         // Works out if it is a duration or time_point and get the duration
0079         auto d = get_duration(src);
0080 
0081         // Lazy initialise the PyDateTime import
0082         if (!PyDateTimeAPI) {
0083             PyDateTime_IMPORT;
0084         }
0085 
0086         // Declare these special duration types so the conversions happen with the correct
0087         // primitive types (int)
0088         using dd_t = duration<int, std::ratio<86400>>;
0089         using ss_t = duration<int, std::ratio<1>>;
0090         using us_t = duration<int, std::micro>;
0091 
0092         auto dd = duration_cast<dd_t>(d);
0093         auto subd = d - dd;
0094         auto ss = duration_cast<ss_t>(subd);
0095         auto us = duration_cast<us_t>(subd - ss);
0096         return PyDelta_FromDSU(dd.count(), ss.count(), us.count());
0097     }
0098 
0099     PYBIND11_TYPE_CASTER(type, const_name("datetime.timedelta"));
0100 };
0101 
0102 inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
0103 #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || defined(_MSC_VER)
0104     if (localtime_s(buf, time))
0105         return nullptr;
0106     return buf;
0107 #else
0108     static std::mutex mtx;
0109     std::lock_guard<std::mutex> lock(mtx);
0110     std::tm *tm_ptr = std::localtime(time);
0111     if (tm_ptr != nullptr) {
0112         *buf = *tm_ptr;
0113     }
0114     return tm_ptr;
0115 #endif
0116 }
0117 
0118 // This is for casting times on the system clock into datetime.datetime instances
0119 template <typename Duration>
0120 class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
0121 public:
0122     using type = std::chrono::time_point<std::chrono::system_clock, Duration>;
0123     bool load(handle src, bool) {
0124         using namespace std::chrono;
0125 
0126         // Lazy initialise the PyDateTime import
0127         if (!PyDateTimeAPI) {
0128             PyDateTime_IMPORT;
0129         }
0130 
0131         if (!src) {
0132             return false;
0133         }
0134 
0135         std::tm cal;
0136         microseconds msecs;
0137 
0138         if (PyDateTime_Check(src.ptr())) {
0139             cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
0140             cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
0141             cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
0142             cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
0143             cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
0144             cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
0145             cal.tm_isdst = -1;
0146             msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
0147         } else if (PyDate_Check(src.ptr())) {
0148             cal.tm_sec = 0;
0149             cal.tm_min = 0;
0150             cal.tm_hour = 0;
0151             cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
0152             cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
0153             cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
0154             cal.tm_isdst = -1;
0155             msecs = microseconds(0);
0156         } else if (PyTime_Check(src.ptr())) {
0157             cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
0158             cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
0159             cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
0160             cal.tm_mday = 1;  // This date (day, month, year) = (1, 0, 70)
0161             cal.tm_mon = 0;   // represents 1-Jan-1970, which is the first
0162             cal.tm_year = 70; // earliest available date for Python's datetime
0163             cal.tm_isdst = -1;
0164             msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
0165         } else {
0166             return false;
0167         }
0168 
0169         value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs);
0170         return true;
0171     }
0172 
0173     static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src,
0174                        return_value_policy /* policy */,
0175                        handle /* parent */) {
0176         using namespace std::chrono;
0177 
0178         // Lazy initialise the PyDateTime import
0179         if (!PyDateTimeAPI) {
0180             PyDateTime_IMPORT;
0181         }
0182 
0183         // Get out microseconds, and make sure they are positive, to avoid bug in eastern
0184         // hemisphere time zones (cfr. https://github.com/pybind/pybind11/issues/2417)
0185         using us_t = duration<int, std::micro>;
0186         auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1));
0187         if (us.count() < 0) {
0188             us += seconds(1);
0189         }
0190 
0191         // Subtract microseconds BEFORE `system_clock::to_time_t`, because:
0192         // > If std::time_t has lower precision, it is implementation-defined whether the value is
0193         // rounded or truncated. (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
0194         std::time_t tt
0195             = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
0196 
0197         std::tm localtime;
0198         std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
0199         if (!localtime_ptr) {
0200             throw cast_error("Unable to represent system_clock in local time");
0201         }
0202         return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
0203                                           localtime.tm_mon + 1,
0204                                           localtime.tm_mday,
0205                                           localtime.tm_hour,
0206                                           localtime.tm_min,
0207                                           localtime.tm_sec,
0208                                           us.count());
0209     }
0210     PYBIND11_TYPE_CASTER(type, const_name("datetime.datetime"));
0211 };
0212 
0213 // Other clocks that are not the system clock are not measured as datetime.datetime objects
0214 // since they are not measured on calendar time. So instead we just make them timedeltas
0215 // Or if they have passed us a time as a float we convert that
0216 template <typename Clock, typename Duration>
0217 class type_caster<std::chrono::time_point<Clock, Duration>>
0218     : public duration_caster<std::chrono::time_point<Clock, Duration>> {};
0219 
0220 template <typename Rep, typename Period>
0221 class type_caster<std::chrono::duration<Rep, Period>>
0222     : public duration_caster<std::chrono::duration<Rep, Period>> {};
0223 
0224 PYBIND11_NAMESPACE_END(detail)
0225 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)