Added ability to convert from datetime.date to system_clock::time_point (#1848)

* Added ability to convert from Python datetime.date and datetime.time to C++ system_clock::time_point
diff --git a/docs/advanced/cast/chrono.rst b/docs/advanced/cast/chrono.rst
index 8c6b3d7..fbd4605 100644
--- a/docs/advanced/cast/chrono.rst
+++ b/docs/advanced/cast/chrono.rst
@@ -59,7 +59,7 @@
 
 .. rubric:: Python to C++
 
-- ``datetime.datetime`` → ``std::chrono::system_clock::time_point``
+- ``datetime.datetime`` or ``datetime.date`` or ``datetime.time`` → ``std::chrono::system_clock::time_point``
     Date/time objects are converted into system clock timepoints. Any
     timezone information is ignored and the type is treated as a naive
     object.
diff --git a/include/pybind11/chrono.h b/include/pybind11/chrono.h
index 2ace232..ea777e6 100644
--- a/include/pybind11/chrono.h
+++ b/include/pybind11/chrono.h
@@ -106,8 +106,11 @@
         if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
 
         if (!src) return false;
+
+        std::tm cal;
+        microseconds msecs;
+
         if (PyDateTime_Check(src.ptr())) {
-            std::tm cal;
             cal.tm_sec   = PyDateTime_DATE_GET_SECOND(src.ptr());
             cal.tm_min   = PyDateTime_DATE_GET_MINUTE(src.ptr());
             cal.tm_hour  = PyDateTime_DATE_GET_HOUR(src.ptr());
@@ -115,11 +118,30 @@
             cal.tm_mon   = PyDateTime_GET_MONTH(src.ptr()) - 1;
             cal.tm_year  = PyDateTime_GET_YEAR(src.ptr()) - 1900;
             cal.tm_isdst = -1;
-
-            value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
-            return true;
+            msecs        = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
+        } else if (PyDate_Check(src.ptr())) {
+            cal.tm_sec   = 0;
+            cal.tm_min   = 0;
+            cal.tm_hour  = 0;
+            cal.tm_mday  = PyDateTime_GET_DAY(src.ptr());
+            cal.tm_mon   = PyDateTime_GET_MONTH(src.ptr()) - 1;
+            cal.tm_year  = PyDateTime_GET_YEAR(src.ptr()) - 1900;
+            cal.tm_isdst = -1;
+            msecs        = microseconds(0);
+        } else if (PyTime_Check(src.ptr())) {
+            cal.tm_sec   = PyDateTime_TIME_GET_SECOND(src.ptr());
+            cal.tm_min   = PyDateTime_TIME_GET_MINUTE(src.ptr());
+            cal.tm_hour  = PyDateTime_TIME_GET_HOUR(src.ptr());
+            cal.tm_mday  = 1;   // This date (day, month, year) = (1, 0, 70)
+            cal.tm_mon   = 0;   // represents 1-Jan-1970, which is the first
+            cal.tm_year  = 70;  // earliest available date for Python's datetime
+            cal.tm_isdst = -1;
+            msecs        = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
         }
         else return false;
+
+        value = system_clock::from_time_t(std::mktime(&cal)) + msecs;
+        return true;
     }
 
     static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, return_value_policy /* policy */, handle /* parent */) {
diff --git a/tests/test_chrono.py b/tests/test_chrono.py
index f308de9..55c9544 100644
--- a/tests/test_chrono.py
+++ b/tests/test_chrono.py
@@ -40,6 +40,62 @@
     assert diff.microseconds == 0
 
 
+def test_chrono_system_clock_roundtrip_date():
+    date1 = datetime.date.today()
+
+    # Roundtrip the time
+    datetime2 = m.test_chrono2(date1)
+    date2 = datetime2.date()
+    time2 = datetime2.time()
+
+    # The returned value should be a datetime
+    assert isinstance(datetime2, datetime.datetime)
+    assert isinstance(date2, datetime.date)
+    assert isinstance(time2, datetime.time)
+
+    # They should be identical (no information lost on roundtrip)
+    diff = abs(date1 - date2)
+    assert diff.days == 0
+    assert diff.seconds == 0
+    assert diff.microseconds == 0
+
+    # Year, Month & Day should be the same after the round trip
+    assert date1.year == date2.year
+    assert date1.month == date2.month
+    assert date1.day == date2.day
+
+    # There should be no time information
+    assert time2.hour == 0
+    assert time2.minute == 0
+    assert time2.second == 0
+    assert time2.microsecond == 0
+
+
+def test_chrono_system_clock_roundtrip_time():
+    time1 = datetime.datetime.today().time()
+
+    # Roundtrip the time
+    datetime2 = m.test_chrono2(time1)
+    date2 = datetime2.date()
+    time2 = datetime2.time()
+
+    # The returned value should be a datetime
+    assert isinstance(datetime2, datetime.datetime)
+    assert isinstance(date2, datetime.date)
+    assert isinstance(time2, datetime.time)
+
+    # Hour, Minute, Second & Microsecond should be the same after the round trip
+    assert time1.hour == time2.hour
+    assert time1.minute == time2.minute
+    assert time1.second == time2.second
+    assert time1.microsecond == time2.microsecond
+
+    # There should be no date information (i.e. date = python base date)
+    assert date2.year == 1970
+    assert date2.month == 1
+    assert date2.day == 1
+
+
 def test_chrono_duration_roundtrip():
 
     # Get the difference between two times (a timedelta)
@@ -70,6 +126,19 @@
     assert cpp_diff.microseconds == diff.microseconds
 
 
+def test_chrono_duration_subtraction_equivalence_date():
+
+    date1 = datetime.date.today()
+    date2 = datetime.date.today()
+
+    diff = date2 - date1
+    cpp_diff = m.test_chrono4(date2, date1)
+
+    assert cpp_diff.days == diff.days
+    assert cpp_diff.seconds == diff.seconds
+    assert cpp_diff.microseconds == diff.microseconds
+
+
 def test_chrono_steady_clock():
     time1 = m.test_chrono5()
     assert isinstance(time1, datetime.timedelta)