Skip to content

Commit 6280cd9

Browse files
authored
Fix OSError when displaying Polars DataFrames with old timestamps on Windows (#7477)
**Fixes #7469** ### Problem When displaying a Polars DataFrame with timestamps before the Unix epoch (1970) or very old dates (e.g., 1902-01-01), marimo would crash on Windows with: ``` OSError: [Errno 22] Invalid argument ``` This occurred in the `_get_bin_values_temporal()` function when calling `datetime.fromtimestamp()` to convert millisecond timestamps back to datetime/date objects for histogram bin visualization. ### Root Cause `datetime.fromtimestamp()` has platform-specific limitations on Windows: - Cannot handle negative timestamps (dates before 1970-01-01) - Cannot handle very old dates that are out of the platform's supported range - This is a known Windows limitation with timestamp conversions ### Solution Modified `_get_bin_values_temporal()` in `narwhals_table.py` to use a more robust approach: - Wrapped `fromtimestamp()` calls in try-except blocks to catch `OSError`, `OverflowError`, and `ValueError` - Added fallback logic using `datetime.timedelta`: `epoch + timedelta(seconds=timestamp)` - This approach works for any date within Python's datetime range (years 1-9999) regardless of platform
1 parent fd5c750 commit 6280cd9

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

marimo/_plugins/ui/_impl/tables/narwhals_table.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -616,9 +616,36 @@ def _get_bin_values_temporal(
616616
int(hours), int(minutes), int(seconds), int(microseconds)
617617
)
618618
elif dtype == nw.Date:
619-
bin_end = datetime.date.fromtimestamp(bin_end / ms_time)
619+
# Use timedelta to handle dates before Unix epoch (1970)
620+
# which cause OSError on Windows with fromtimestamp
621+
try:
622+
bin_end = datetime.date.fromtimestamp(bin_end / ms_time)
623+
except (OSError, OverflowError, ValueError):
624+
# Fall back to timedelta calculation for old dates
625+
epoch = datetime.datetime(
626+
1970, 1, 1, tzinfo=datetime.timezone.utc
627+
)
628+
bin_end_dt = epoch + datetime.timedelta(
629+
seconds=bin_end / ms_time
630+
)
631+
bin_end = bin_end_dt.date()
620632
else:
621-
bin_end = datetime.datetime.fromtimestamp(bin_end / ms_time)
633+
# Use timedelta to handle datetimes before Unix epoch (1970)
634+
# which cause OSError on Windows with fromtimestamp
635+
try:
636+
bin_end = datetime.datetime.fromtimestamp(
637+
bin_end / ms_time
638+
)
639+
except (OSError, OverflowError, ValueError):
640+
# Fall back to timedelta calculation for old dates
641+
epoch = datetime.datetime(
642+
1970, 1, 1, tzinfo=datetime.timezone.utc
643+
)
644+
bin_end = epoch + datetime.timedelta(
645+
seconds=bin_end / ms_time
646+
)
647+
# Remove timezone to match fromtimestamp behavior
648+
bin_end = bin_end.replace(tzinfo=None)
622649

623650
# Only append if the count is greater than 0
624651
if count > 0:

tests/_plugins/ui/_impl/tables/test_narwhals.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,57 @@ def test_dates_multiple(self, df: Any) -> None:
13151315
assert bin_values[0].count == 5
13161316

13171317

1318+
@pytest.mark.skipif(not HAS_DEPS, reason="optional dependencies not installed")
1319+
@pytest.mark.parametrize(
1320+
"df",
1321+
create_dataframes(
1322+
{
1323+
"old_dates": [
1324+
datetime.datetime.fromisoformat("1902-01-01 00:00:00"),
1325+
datetime.datetime.fromisoformat("2020-01-01 00:00:00"),
1326+
datetime.datetime.fromisoformat("2020-01-02 00:00:00"),
1327+
datetime.datetime.fromisoformat("2020-01-03 00:00:00"),
1328+
datetime.datetime.fromisoformat("2020-01-04 00:00:00"),
1329+
datetime.datetime.fromisoformat("2020-01-05 00:00:00"),
1330+
datetime.datetime.fromisoformat("2020-01-06 00:00:00"),
1331+
datetime.datetime.fromisoformat("2020-01-07 00:00:00"),
1332+
datetime.datetime.fromisoformat("2020-01-08 00:00:00"),
1333+
datetime.datetime.fromisoformat("2020-01-09 00:00:00"),
1334+
datetime.datetime.fromisoformat("2020-01-10 00:00:00"),
1335+
]
1336+
},
1337+
exclude=["ibis"],
1338+
),
1339+
)
1340+
class TestOldDates:
1341+
"""Test handling of old dates (pre-1970) that cause OSError on Windows."""
1342+
1343+
def test_get_bin_values_with_old_dates(self, df: Any) -> None:
1344+
"""
1345+
Test that get_bin_values works with timestamps before Unix epoch.
1346+
1347+
This reproduces issue #7469 where dates like 1902-01-01 cause
1348+
OSError: [Errno 22] Invalid argument on Windows when using
1349+
datetime.fromtimestamp().
1350+
"""
1351+
manager = NarwhalsTableManager.from_dataframe(df)
1352+
# Should not raise OSError even with old dates
1353+
bin_values = manager.get_bin_values("old_dates", 3)
1354+
1355+
# Verify we get valid bin values
1356+
assert len(bin_values) > 0
1357+
assert all(
1358+
isinstance(bv.bin_start, (datetime.datetime, type(None)))
1359+
for bv in bin_values
1360+
)
1361+
assert all(
1362+
isinstance(bv.bin_end, (datetime.datetime, type(None)))
1363+
for bv in bin_values
1364+
)
1365+
# Total count should be 11 (all rows)
1366+
assert sum(bin_value.count for bin_value in bin_values) == 11
1367+
1368+
13181369
@pytest.mark.skipif(not HAS_DEPS, reason="optional dependencies not installed")
13191370
@pytest.mark.parametrize(
13201371
"df",

0 commit comments

Comments
 (0)