Repository: bokeh/bokeh

Issue:
bokeh.core.properties.Date#transform fails on Windows
Bokeh 0.12.14dev4

`bokeh.core.properties.Date#transform` catches `ValueError`, but it doesn't work on Windows.
On windows, the exception for invalid values is `OSError`.



Discussion:
Three hours later I can finally run (a subset of) tests on Windows. Will make a PR with a test for windows so that in the future when we run windows unit tests continuously it will be there. 
@p-himik what usage triggers this error?
@mattpap it looks as though you added this logic in https://github.com/bokeh/bokeh/commit/4b6fe7f5217d67203073b5e4feb484ce2123929b:

```
 +    def transform(self, value):
 +        value = super(Date, self).transform(value)
 +
 +        if isinstance(value, (float,) + integer_types):
 +            try:
 +                value = datetime.date.fromtimestamp(value)
 +            except ValueError:
 +                value = datetime.date.fromtimestamp(value/1000)
```
Can you explain this more clearly?  The `Data.fromtimestamp` docs report:
> Return the local date corresponding to the POSIX timestamp, such as is returned by time.time(). This may raise OverflowError, if the timestamp is out of the range of values supported by the platform C localtime() function, and OSError on localtime() failure. It’s common for this to be restricted to years from 1970 through 2038. Note that on non-POSIX systems that include leap seconds in their notion of a timestamp, leap seconds are ignored by fromtimestamp().

> Changed in version 3.3: Raise OverflowError instead of ValueError if the timestamp is out of the range of values supported by the platform C localtime() function. Raise OSError instead of ValueError on localtime() failure.

Is this an attempt to work around that? Are there not date values that potentially should be decoded in the second way, but that would pass through the `try` by pure concidence?

Resolving patch:
diff --git a/bokeh/core/properties.py b/bokeh/core/properties.py
--- a/bokeh/core/properties.py
+++ b/bokeh/core/properties.py
@@ -1071,7 +1071,7 @@ def transform(self, value):
         if isinstance(value, (float,) + bokeh_integer_types):
             try:
                 value = datetime.date.fromtimestamp(value)
-            except ValueError:
+            except (ValueError, OSError):
                 value = datetime.date.fromtimestamp(value/1000)
         elif isinstance(value, string_types):
             value = dateutil.parser.parse(value).date()


Test patch:
diff --git a/bokeh/core/tests/test_properties.py b/bokeh/core/tests/test_properties.py
--- a/bokeh/core/tests/test_properties.py
+++ b/bokeh/core/tests/test_properties.py
@@ -1,6 +1,7 @@
 from __future__ import absolute_import
 
 import datetime
+import time
 import unittest
 import numpy as np
 import pandas as pd
@@ -9,7 +10,7 @@
 import pytest
 
 from bokeh.core.properties import (field, value,
-    NumberSpec, ColorSpec, Bool, Int, Float, Complex, String,
+    NumberSpec, ColorSpec, Bool, Int, Float, Complex, Date, String,
     Regex, Seq, List, Dict, Tuple, Instance, Any, Interval, Either,
     Enum, Color, DashPattern, Size, Percent, Angle, AngleSpec, StringSpec,
     DistanceSpec, FontSize, FontSizeSpec, Override, Include, MinMaxBounds,
@@ -21,6 +22,17 @@
 
 from bokeh.models import Plot
 
+class Test_Date(object):
+    def test_validate_seconds(self):
+        t = time.time()
+        d = Date()
+        assert d.transform(t) == datetime.date.today()
+
+    def test_validate_milliseconds(self):
+        t = time.time() * 1000
+        d = Date()
+        assert d.transform(t) == datetime.date.today()
+
 class Basictest(unittest.TestCase):
 
     def test_simple_class(self):