Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion altair/utils/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,12 @@ def sanitize_narwhals_dataframe(
elif dtype == nw.Date:
columns.append(nw.col(name).dt.to_string(local_iso_fmt_string))
elif dtype == nw.Datetime:
columns.append(nw.col(name).dt.to_string(f"{local_iso_fmt_string}%.f"))
# Preserve timezone information when present so Vega-Lite can disambiguate
# repeated local times during DST transitions.
fmt = f"{local_iso_fmt_string}%.f"
if getattr(dtype, "time_zone", None) is not None:
fmt = f"{fmt}%z"
columns.append(nw.col(name).dt.to_string(fmt))
elif dtype == nw.Duration:
msg = (
f'Field "{name}" has type "{dtype}" which is '
Expand Down
12 changes: 12 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ def windows_has_tzdata() -> bool:
https://duckdb.org/
"""

skip_requires_polars: pytest.MarkDecorator = pytest.mark.skipif(
find_spec("polars") is None, reason="`polars` not installed."
)
"""
``pytest.mark.skipif`` decorator.

Applies when `polars`_ import would fail.

.. _polars:
https://pola.rs/
"""


@overload
def skip_requires_pyarrow(
Expand Down
26 changes: 25 additions & 1 deletion tests/utils/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
sanitize_narwhals_dataframe,
sanitize_pandas_dataframe,
)
from tests import skip_requires_pyarrow
from tests import skip_requires_polars, skip_requires_pyarrow


def test_infer_vegalite_type():
Expand Down Expand Up @@ -168,6 +168,30 @@ def test_sanitize_pyarrow_table_columns() -> None:
json.dumps(values)


@skip_requires_polars
def test_sanitize_polars_datetime_timezone_preserved() -> None:
import polars as pl

start = pl.datetime(2023, 11, 5, time_zone="US/Mountain")
df = pl.DataFrame(
{
"datetime": pl.datetime_range(
start, start.dt.offset_by("3h"), "1h", closed="both", eager=True
),
"value": [10, 20, 30, 40],
}
)

sanitized = sanitize_narwhals_dataframe(nw.from_native(df, eager_only=True))

assert sanitized.rows(named=True) == [
{"datetime": "2023-11-05T00:00:00-0600", "value": 10},
{"datetime": "2023-11-05T01:00:00-0600", "value": 20},
{"datetime": "2023-11-05T01:00:00-0700", "value": 30},
{"datetime": "2023-11-05T02:00:00-0700", "value": 40},
]


def test_sanitize_dataframe_colnames():
df = pd.DataFrame(np.arange(12).reshape(4, 3))

Expand Down