Repository: bokeh/bokeh

Issue:
DateRangeSlider returns datetime instead of date
DateRangeSlider has two options, returning a tuple of UNIX epochs or conveniently returning a tuple of datetimes, as explained in #6895.

I think it would be logical to have an option to return a tuple of dates, with the same time resolution as the actual DateRangeSlider. 

This could be fixed by adding a second convenience function very similar to the one defined in #6895.


Discussion:
@YMRTN would you like to submit a PR? In all likelihood will be able to do so faster than anyone else will be able to get to it. 

Resolving patch:
diff --git a/bokeh/models/widgets/sliders.py b/bokeh/models/widgets/sliders.py
--- a/bokeh/models/widgets/sliders.py
+++ b/bokeh/models/widgets/sliders.py
@@ -21,7 +21,7 @@
 #-----------------------------------------------------------------------------
 
 # Standard library imports
-from datetime import datetime
+from datetime import datetime, date
 import numbers
 
 # External imports
@@ -202,6 +202,26 @@ def value_as_datetime(self):
             d2 = datetime.utcfromtimestamp(v2 / 1000)
         else:
             d2 = v2
+        return d1, d2    \
+
+    @property
+    def value_as_date(self):
+        ''' Convenience property to retrieve the value tuple as a tuple of
+        date objects.
+
+        Added in version 1.1
+        '''
+        if self.value is None:
+            return None
+        v1, v2 = self.value
+        if isinstance(v1, numbers.Number):
+            d1 = date.fromtimestamp(v1 / 1000)
+        else:
+            d1 = v1
+        if isinstance(v2, numbers.Number):
+            d2 = date.fromtimestamp(v2 / 1000)
+        else:
+            d2 = v2
         return d1, d2
 
     value = Tuple(Date, Date, help="""


Test patch:
diff --git a/bokeh/models/widgets/tests/test_slider.py b/bokeh/models/widgets/tests/test_slider.py
--- a/bokeh/models/widgets/tests/test_slider.py
+++ b/bokeh/models/widgets/tests/test_slider.py
@@ -17,7 +17,7 @@
 #-----------------------------------------------------------------------------
 
 # Standard library imports
-from datetime import datetime
+from datetime import datetime, date
 import logging
 
 # External imports
@@ -67,6 +67,32 @@ def test_daterangeslider_value_as_datetime_when_set_mixed():
             value=(convert_datetime_type(start), end))
     assert s.value_as_datetime == (start, end)
 
+def test_daterangeslider_value_as_date_when_set_as_date():
+    start = date(2017, 8, 9)
+    end = date(2017, 8, 10)
+    s = mws.DateRangeSlider(start=start, end=end, value=(start, end))
+    assert s.value_as_date == (start, end)
+
+def test_daterangeslider_value_as_date_when_set_as_timestamp():
+    start = date(2017, 8, 9)
+    end = date(2017, 8, 10)
+    s = mws.DateRangeSlider(start=start, end=end,
+            # Bokeh serializes as ms since epoch, if they get set as numbers (e.g.)
+            # by client side update, this is the units they will be
+            value=(convert_datetime_type(start), convert_datetime_type(end)))
+    assert s.value_as_date == (start, end)
+
+def test_daterangeslider_value_as_date_when_set_mixed():
+    start = date(2017, 8, 9)
+    end = date(2017, 8, 10)
+    s = mws.DateRangeSlider(start=start, end=end,
+            value=(start, convert_datetime_type(end)))
+    assert s.value_as_date == (start, end)
+
+    s = mws.DateRangeSlider(start=start, end=end,
+            value=(convert_datetime_type(start), end))
+    assert s.value_as_date == (start, end)
+
 def test_rangeslider_equal_start_end_exception():
     start = 0
     end = 0