111986Sandreas.sandberg@arm.com/* 211986Sandreas.sandberg@arm.com pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime 311986Sandreas.sandberg@arm.com 411986Sandreas.sandberg@arm.com Copyright (c) 2016 Trent Houliston <trent@houliston.me> and 511986Sandreas.sandberg@arm.com Wenzel Jakob <wenzel.jakob@epfl.ch> 611986Sandreas.sandberg@arm.com 711986Sandreas.sandberg@arm.com All rights reserved. Use of this source code is governed by a 811986Sandreas.sandberg@arm.com BSD-style license that can be found in the LICENSE file. 911986Sandreas.sandberg@arm.com*/ 1011986Sandreas.sandberg@arm.com 1111986Sandreas.sandberg@arm.com#pragma once 1211986Sandreas.sandberg@arm.com 1311986Sandreas.sandberg@arm.com#include "pybind11.h" 1411986Sandreas.sandberg@arm.com#include <cmath> 1511986Sandreas.sandberg@arm.com#include <ctime> 1611986Sandreas.sandberg@arm.com#include <chrono> 1711986Sandreas.sandberg@arm.com#include <datetime.h> 1811986Sandreas.sandberg@arm.com 1911986Sandreas.sandberg@arm.com// Backport the PyDateTime_DELTA functions from Python3.3 if required 2011986Sandreas.sandberg@arm.com#ifndef PyDateTime_DELTA_GET_DAYS 2111986Sandreas.sandberg@arm.com#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) 2211986Sandreas.sandberg@arm.com#endif 2311986Sandreas.sandberg@arm.com#ifndef PyDateTime_DELTA_GET_SECONDS 2411986Sandreas.sandberg@arm.com#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) 2511986Sandreas.sandberg@arm.com#endif 2611986Sandreas.sandberg@arm.com#ifndef PyDateTime_DELTA_GET_MICROSECONDS 2711986Sandreas.sandberg@arm.com#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) 2811986Sandreas.sandberg@arm.com#endif 2911986Sandreas.sandberg@arm.com 3012391Sjason@lowepower.comNAMESPACE_BEGIN(PYBIND11_NAMESPACE) 3111986Sandreas.sandberg@arm.comNAMESPACE_BEGIN(detail) 3211986Sandreas.sandberg@arm.com 3311986Sandreas.sandberg@arm.comtemplate <typename type> class duration_caster { 3411986Sandreas.sandberg@arm.compublic: 3511986Sandreas.sandberg@arm.com typedef typename type::rep rep; 3611986Sandreas.sandberg@arm.com typedef typename type::period period; 3711986Sandreas.sandberg@arm.com 3811986Sandreas.sandberg@arm.com typedef std::chrono::duration<uint_fast32_t, std::ratio<86400>> days; 3911986Sandreas.sandberg@arm.com 4011986Sandreas.sandberg@arm.com bool load(handle src, bool) { 4111986Sandreas.sandberg@arm.com using namespace std::chrono; 4211986Sandreas.sandberg@arm.com 4311986Sandreas.sandberg@arm.com // Lazy initialise the PyDateTime import 4411986Sandreas.sandberg@arm.com if (!PyDateTimeAPI) { PyDateTime_IMPORT; } 4511986Sandreas.sandberg@arm.com 4611986Sandreas.sandberg@arm.com if (!src) return false; 4711986Sandreas.sandberg@arm.com // If invoked with datetime.delta object 4811986Sandreas.sandberg@arm.com if (PyDelta_Check(src.ptr())) { 4911986Sandreas.sandberg@arm.com value = type(duration_cast<duration<rep, period>>( 5011986Sandreas.sandberg@arm.com days(PyDateTime_DELTA_GET_DAYS(src.ptr())) 5111986Sandreas.sandberg@arm.com + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) 5211986Sandreas.sandberg@arm.com + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); 5311986Sandreas.sandberg@arm.com return true; 5411986Sandreas.sandberg@arm.com } 5511986Sandreas.sandberg@arm.com // If invoked with a float we assume it is seconds and convert 5611986Sandreas.sandberg@arm.com else if (PyFloat_Check(src.ptr())) { 5711986Sandreas.sandberg@arm.com value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr())))); 5811986Sandreas.sandberg@arm.com return true; 5911986Sandreas.sandberg@arm.com } 6011986Sandreas.sandberg@arm.com else return false; 6111986Sandreas.sandberg@arm.com } 6211986Sandreas.sandberg@arm.com 6311986Sandreas.sandberg@arm.com // If this is a duration just return it back 6411986Sandreas.sandberg@arm.com static const std::chrono::duration<rep, period>& get_duration(const std::chrono::duration<rep, period> &src) { 6511986Sandreas.sandberg@arm.com return src; 6611986Sandreas.sandberg@arm.com } 6711986Sandreas.sandberg@arm.com 6811986Sandreas.sandberg@arm.com // If this is a time_point get the time_since_epoch 6911986Sandreas.sandberg@arm.com template <typename Clock> static std::chrono::duration<rep, period> get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) { 7011986Sandreas.sandberg@arm.com return src.time_since_epoch(); 7111986Sandreas.sandberg@arm.com } 7211986Sandreas.sandberg@arm.com 7311986Sandreas.sandberg@arm.com static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { 7411986Sandreas.sandberg@arm.com using namespace std::chrono; 7511986Sandreas.sandberg@arm.com 7611986Sandreas.sandberg@arm.com // Use overloaded function to get our duration from our source 7711986Sandreas.sandberg@arm.com // Works out if it is a duration or time_point and get the duration 7811986Sandreas.sandberg@arm.com auto d = get_duration(src); 7911986Sandreas.sandberg@arm.com 8011986Sandreas.sandberg@arm.com // Lazy initialise the PyDateTime import 8111986Sandreas.sandberg@arm.com if (!PyDateTimeAPI) { PyDateTime_IMPORT; } 8211986Sandreas.sandberg@arm.com 8311986Sandreas.sandberg@arm.com // Declare these special duration types so the conversions happen with the correct primitive types (int) 8411986Sandreas.sandberg@arm.com using dd_t = duration<int, std::ratio<86400>>; 8511986Sandreas.sandberg@arm.com using ss_t = duration<int, std::ratio<1>>; 8611986Sandreas.sandberg@arm.com using us_t = duration<int, std::micro>; 8711986Sandreas.sandberg@arm.com 8812037Sandreas.sandberg@arm.com auto dd = duration_cast<dd_t>(d); 8912037Sandreas.sandberg@arm.com auto subd = d - dd; 9012037Sandreas.sandberg@arm.com auto ss = duration_cast<ss_t>(subd); 9112037Sandreas.sandberg@arm.com auto us = duration_cast<us_t>(subd - ss); 9212037Sandreas.sandberg@arm.com return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); 9311986Sandreas.sandberg@arm.com } 9411986Sandreas.sandberg@arm.com 9511986Sandreas.sandberg@arm.com PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); 9611986Sandreas.sandberg@arm.com}; 9711986Sandreas.sandberg@arm.com 9811986Sandreas.sandberg@arm.com// This is for casting times on the system clock into datetime.datetime instances 9911986Sandreas.sandberg@arm.comtemplate <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> { 10011986Sandreas.sandberg@arm.compublic: 10111986Sandreas.sandberg@arm.com typedef std::chrono::time_point<std::chrono::system_clock, Duration> type; 10211986Sandreas.sandberg@arm.com bool load(handle src, bool) { 10311986Sandreas.sandberg@arm.com using namespace std::chrono; 10411986Sandreas.sandberg@arm.com 10511986Sandreas.sandberg@arm.com // Lazy initialise the PyDateTime import 10611986Sandreas.sandberg@arm.com if (!PyDateTimeAPI) { PyDateTime_IMPORT; } 10711986Sandreas.sandberg@arm.com 10811986Sandreas.sandberg@arm.com if (!src) return false; 10914299Sbbruce@ucdavis.edu 11014299Sbbruce@ucdavis.edu std::tm cal; 11114299Sbbruce@ucdavis.edu microseconds msecs; 11214299Sbbruce@ucdavis.edu 11311986Sandreas.sandberg@arm.com if (PyDateTime_Check(src.ptr())) { 11411986Sandreas.sandberg@arm.com cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); 11511986Sandreas.sandberg@arm.com cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); 11611986Sandreas.sandberg@arm.com cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); 11711986Sandreas.sandberg@arm.com cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); 11811986Sandreas.sandberg@arm.com cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; 11911986Sandreas.sandberg@arm.com cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; 12011986Sandreas.sandberg@arm.com cal.tm_isdst = -1; 12114299Sbbruce@ucdavis.edu msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); 12214299Sbbruce@ucdavis.edu } else if (PyDate_Check(src.ptr())) { 12314299Sbbruce@ucdavis.edu cal.tm_sec = 0; 12414299Sbbruce@ucdavis.edu cal.tm_min = 0; 12514299Sbbruce@ucdavis.edu cal.tm_hour = 0; 12614299Sbbruce@ucdavis.edu cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); 12714299Sbbruce@ucdavis.edu cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; 12814299Sbbruce@ucdavis.edu cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; 12914299Sbbruce@ucdavis.edu cal.tm_isdst = -1; 13014299Sbbruce@ucdavis.edu msecs = microseconds(0); 13114299Sbbruce@ucdavis.edu } else if (PyTime_Check(src.ptr())) { 13214299Sbbruce@ucdavis.edu cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr()); 13314299Sbbruce@ucdavis.edu cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr()); 13414299Sbbruce@ucdavis.edu cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr()); 13514299Sbbruce@ucdavis.edu cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70) 13614299Sbbruce@ucdavis.edu cal.tm_mon = 0; // represents 1-Jan-1970, which is the first 13714299Sbbruce@ucdavis.edu cal.tm_year = 70; // earliest available date for Python's datetime 13814299Sbbruce@ucdavis.edu cal.tm_isdst = -1; 13914299Sbbruce@ucdavis.edu msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr())); 14011986Sandreas.sandberg@arm.com } 14111986Sandreas.sandberg@arm.com else return false; 14214299Sbbruce@ucdavis.edu 14314299Sbbruce@ucdavis.edu value = system_clock::from_time_t(std::mktime(&cal)) + msecs; 14414299Sbbruce@ucdavis.edu return true; 14511986Sandreas.sandberg@arm.com } 14611986Sandreas.sandberg@arm.com 14711986Sandreas.sandberg@arm.com static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, return_value_policy /* policy */, handle /* parent */) { 14811986Sandreas.sandberg@arm.com using namespace std::chrono; 14911986Sandreas.sandberg@arm.com 15011986Sandreas.sandberg@arm.com // Lazy initialise the PyDateTime import 15111986Sandreas.sandberg@arm.com if (!PyDateTimeAPI) { PyDateTime_IMPORT; } 15211986Sandreas.sandberg@arm.com 15314299Sbbruce@ucdavis.edu std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src)); 15411986Sandreas.sandberg@arm.com // this function uses static memory so it's best to copy it out asap just in case 15511986Sandreas.sandberg@arm.com // otherwise other code that is using localtime may break this (not just python code) 15611986Sandreas.sandberg@arm.com std::tm localtime = *std::localtime(&tt); 15711986Sandreas.sandberg@arm.com 15811986Sandreas.sandberg@arm.com // Declare these special duration types so the conversions happen with the correct primitive types (int) 15911986Sandreas.sandberg@arm.com using us_t = duration<int, std::micro>; 16011986Sandreas.sandberg@arm.com 16111986Sandreas.sandberg@arm.com return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, 16211986Sandreas.sandberg@arm.com localtime.tm_mon + 1, 16311986Sandreas.sandberg@arm.com localtime.tm_mday, 16411986Sandreas.sandberg@arm.com localtime.tm_hour, 16511986Sandreas.sandberg@arm.com localtime.tm_min, 16611986Sandreas.sandberg@arm.com localtime.tm_sec, 16711986Sandreas.sandberg@arm.com (duration_cast<us_t>(src.time_since_epoch() % seconds(1))).count()); 16811986Sandreas.sandberg@arm.com } 16911986Sandreas.sandberg@arm.com PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); 17011986Sandreas.sandberg@arm.com}; 17111986Sandreas.sandberg@arm.com 17211986Sandreas.sandberg@arm.com// Other clocks that are not the system clock are not measured as datetime.datetime objects 17311986Sandreas.sandberg@arm.com// since they are not measured on calendar time. So instead we just make them timedeltas 17411986Sandreas.sandberg@arm.com// Or if they have passed us a time as a float we convert that 17511986Sandreas.sandberg@arm.comtemplate <typename Clock, typename Duration> class type_caster<std::chrono::time_point<Clock, Duration>> 17611986Sandreas.sandberg@arm.com: public duration_caster<std::chrono::time_point<Clock, Duration>> { 17711986Sandreas.sandberg@arm.com}; 17811986Sandreas.sandberg@arm.com 17911986Sandreas.sandberg@arm.comtemplate <typename Rep, typename Period> class type_caster<std::chrono::duration<Rep, Period>> 18011986Sandreas.sandberg@arm.com: public duration_caster<std::chrono::duration<Rep, Period>> { 18111986Sandreas.sandberg@arm.com}; 18211986Sandreas.sandberg@arm.com 18311986Sandreas.sandberg@arm.comNAMESPACE_END(detail) 18412391Sjason@lowepower.comNAMESPACE_END(PYBIND11_NAMESPACE) 185