mirror of
https://github.com/apache/superset.git
synced 2026-08-12 11:11:01 +00:00
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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:
|
||||
|
||||
+24
-1
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user