This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch fix/mssql-datetime-range-charts
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 4026ac9134459a2137c0397a904f2a63b61aa3dd
Author: Evan Rusackas <[email protected]>
AuthorDate: Fri Aug 1 15:14:22 2025 -0700

    fix(charts): handle MSSQL datetime values outside JavaScript's safe range
    
    MSSQL datetime values outside JavaScript's Date range (1938-2286) were 
causing charts to display "0NaN-NaN-NaN". This fix returns ISO strings for 
dates outside the safe range instead of epoch milliseconds that overflow.
    
    🤖 Generated with [Claude Code](https://claude.ai/code)
    
    Co-Authored-By: Claude <[email protected]>
---
 superset/utils/dates.py              |  5 +++++
 superset/utils/json.py               | 25 ++++++++++++++++++++++++-
 tests/unit_tests/utils/json_tests.py | 17 +++++++++++++++++
 3 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/superset/utils/dates.py b/superset/utils/dates.py
index b2b422a1f0..5b79e27060 100644
--- a/superset/utils/dates.py
+++ b/superset/utils/dates.py
@@ -19,6 +19,11 @@ from datetime import datetime
 import pytz
 
 EPOCH = datetime(1970, 1, 1)
+# JavaScript's Date object can safely represent dates within this range
+# These are the min/max values that can be represented as milliseconds since 
epoch
+# without overflowing JavaScript's Number type (±2^53)
+JS_DATE_RANGE_MIN = datetime(1938, 4, 24, 22, 13, 20, 0)
+JS_DATE_RANGE_MAX = datetime(2286, 11, 20, 17, 46, 39, 999000)
 
 
 def datetime_to_epoch(dttm: datetime) -> float:
diff --git a/superset/utils/json.py b/superset/utils/json.py
index 689b0af6de..e1da205673 100644
--- a/superset/utils/json.py
+++ b/superset/utils/json.py
@@ -29,7 +29,12 @@ from jsonpath_ng import parse
 from simplejson import JSONDecodeError
 
 from superset.constants import PASSWORD_MASK
-from superset.utils.dates import datetime_to_epoch, EPOCH
+from superset.utils.dates import (
+    datetime_to_epoch,
+    EPOCH,
+    JS_DATE_RANGE_MAX,
+    JS_DATE_RANGE_MIN,
+)
 
 logging.getLogger("MARKDOWN").setLevel(logging.INFO)
 logger = logging.getLogger(__name__)
@@ -155,9 +160,27 @@ def json_int_dttm_ser(obj: Any) -> Any:
     """
 
     if isinstance(obj, (datetime, pd.Timestamp)):
+        # Check if datetime is within JavaScript's safe date range
+        # If not, return ISO string instead of epoch milliseconds
+        if isinstance(obj, pd.Timestamp):
+            dttm = obj.to_pydatetime()
+        else:
+            dttm = obj
+
+        # Remove timezone info for comparison
+        dttm_no_tz = dttm.replace(tzinfo=None) if dttm.tzinfo else dttm
+
+        if dttm_no_tz < JS_DATE_RANGE_MIN or dttm_no_tz > JS_DATE_RANGE_MAX:
+            # Return ISO string for dates outside JavaScript's safe range
+            return obj.isoformat()
+
         return datetime_to_epoch(obj)
 
     if isinstance(obj, date):
+        # Check if date is within JavaScript's safe date range
+        date_as_datetime = datetime.combine(obj, datetime.min.time())
+        if date_as_datetime < JS_DATE_RANGE_MIN or date_as_datetime > 
JS_DATE_RANGE_MAX:
+            return obj.isoformat()
         return (obj - EPOCH.date()).total_seconds() * 1000
 
     return base_json_conv(obj)
diff --git a/tests/unit_tests/utils/json_tests.py 
b/tests/unit_tests/utils/json_tests.py
index 3356577407..9e124b0dc1 100644
--- a/tests/unit_tests/utils/json_tests.py
+++ b/tests/unit_tests/utils/json_tests.py
@@ -254,6 +254,23 @@ def test_json_int_dttm_ser():
     assert json.json_int_dttm_ser(dttm + timedelta(milliseconds=1)) == (ts + 1)
     assert json.json_int_dttm_ser(np.int64(1)) == 1
 
+    # Test edge cases for JavaScript Date range
+    # Dates outside JavaScript's safe range should return ISO strings
+    assert (
+        json.json_int_dttm_ser(datetime(1938, 4, 24, 22, 13, 19))
+        == "1938-04-24T22:13:19"
+    )
+    assert (
+        json.json_int_dttm_ser(datetime(2286, 11, 20, 17, 46, 40))
+        == "2286-11-20T17:46:40"
+    )
+    assert json.json_int_dttm_ser(date(1938, 4, 23)) == "1938-04-23"
+    assert json.json_int_dttm_ser(date(2286, 11, 21)) == "2286-11-21"
+
+    # Dates within JavaScript's safe range should return epoch milliseconds
+    assert isinstance(json.json_int_dttm_ser(datetime(2000, 1, 1)), (int, 
float))
+    assert isinstance(json.json_int_dttm_ser(date(2000, 1, 1)), (int, float))
+
     with pytest.raises(TypeError):
         json.json_int_dttm_ser(np.datetime64())
 

Reply via email to