Path: blob/main/py-polars/tests/unit/functions/range/test_time_range.py
6939 views
from __future__ import annotations12from datetime import time, timedelta3from typing import TYPE_CHECKING45import pytest67import polars as pl8from polars.exceptions import ComputeError, ShapeError9from polars.testing import assert_frame_equal, assert_series_equal1011if TYPE_CHECKING:12from polars._typing import ClosedInterval131415def test_time_range_schema() -> None:16df = pl.DataFrame({"start": [time(1)], "end": [time(1, 30)]}).lazy()17result = df.with_columns(time_range=pl.time_ranges(pl.col("start"), pl.col("end")))18expected_schema = {"start": pl.Time, "end": pl.Time, "time_range": pl.List(pl.Time)}19assert result.collect_schema() == expected_schema20assert result.collect().schema == expected_schema212223def test_time_ranges_eager() -> None:24start = pl.Series("start", [time(9, 0), time(10, 0)])25end = pl.Series("end", [time(12, 0), time(11, 0)])2627result = pl.time_ranges(start, end, eager=True)2829expected = pl.Series(30"start",31[32[time(9, 0), time(10, 0), time(11, 0), time(12, 0)],33[time(10, 0), time(11, 0)],34],35)36assert_series_equal(result, expected)373839def test_time_range_eager_explode() -> None:40result = pl.time_range(time(9, 0), time(11, 0), eager=True)41expected = pl.Series("literal", [time(9, 0), time(10, 0), time(11, 0)])42assert_series_equal(result, expected)434445def test_time_range_input_shape_empty() -> None:46empty = pl.Series(dtype=pl.Time)47single = pl.Series([time(12, 0)])4849with pytest.raises(ShapeError):50pl.time_range(empty, single, eager=True)51with pytest.raises(ShapeError):52pl.time_range(single, empty, eager=True)53with pytest.raises(ShapeError):54pl.time_range(empty, empty, eager=True)555657def test_time_range_input_shape_multiple_values() -> None:58single = pl.Series([time(12, 0)])59multiple = pl.Series([time(11, 0), time(12, 0)])6061with pytest.raises(ShapeError):62pl.time_range(multiple, single, eager=True)63with pytest.raises(ShapeError):64pl.time_range(single, multiple, eager=True)65with pytest.raises(ShapeError):66pl.time_range(multiple, multiple, eager=True)676869def test_time_range_start_equals_end() -> None:70t = time(12, 0)7172result = pl.time_range(t, t, closed="both", eager=True)7374expected = pl.Series("literal", [t])75assert_series_equal(result, expected)767778@pytest.mark.parametrize("closed", ["left", "right", "none"])79def test_time_range_start_equals_end_open(closed: ClosedInterval) -> None:80t = time(12, 0)8182result = pl.time_range(t, t, closed=closed, eager=True)8384expected = pl.Series("literal", dtype=pl.Time)85assert_series_equal(result, expected)868788def test_time_range_start_later_than_end() -> None:89result = pl.time_range(time(12), time(11), eager=True)90expected = pl.Series("literal", dtype=pl.Time)91assert_series_equal(result, expected)929394@pytest.mark.parametrize("interval", [timedelta(0), timedelta(minutes=-10)])95def test_time_range_invalid_step(interval: timedelta) -> None:96with pytest.raises(ComputeError, match="`interval` must be positive"):97pl.time_range(time(11), time(12), interval=interval, eager=True)9899100def test_time_range_lit_lazy() -> None:101tm = pl.select(102pl.time_range(103start=time(1, 2, 3),104end=time(23, 59, 59),105interval="5h45m10s333ms",106closed="right",107).alias("tm")108)109110assert tm["tm"].to_list() == [111time(6, 47, 13, 333000),112time(12, 32, 23, 666000),113time(18, 17, 33, 999000),114]115116# validate unset start/end117tm = pl.select(pl.time_range(interval="5h45m10s333ms").alias("tm"))118assert tm["tm"].to_list() == [119time(0, 0),120time(5, 45, 10, 333000),121time(11, 30, 20, 666000),122time(17, 15, 30, 999000),123time(23, 0, 41, 332000),124]125126tm = pl.select(127pl.time_range(start=pl.lit(time(23, 59, 59, 999980)), interval="10000ns").alias(128"tm"129)130)131assert tm["tm"].to_list() == [132time(23, 59, 59, 999980),133time(23, 59, 59, 999990),134]135136137def test_time_range_lit_eager() -> None:138eager = True139tm = pl.select(140pl.time_range(141start=time(1, 2, 3),142end=time(23, 59, 59),143interval="5h45m10s333ms",144closed="right",145eager=eager,146).alias("tm")147)148if not eager:149tm = tm.select(pl.col("tm").explode())150assert tm["tm"].to_list() == [151time(6, 47, 13, 333000),152time(12, 32, 23, 666000),153time(18, 17, 33, 999000),154]155156# validate unset start/end157tm = pl.select(158pl.time_range(159interval="5h45m10s333ms",160eager=eager,161).alias("tm")162)163if not eager:164tm = tm.select(pl.col("tm").explode())165assert tm["tm"].to_list() == [166time(0, 0),167time(5, 45, 10, 333000),168time(11, 30, 20, 666000),169time(17, 15, 30, 999000),170time(23, 0, 41, 332000),171]172173tm = pl.select(174pl.time_range(175start=pl.lit(time(23, 59, 59, 999980)),176interval="10000ns",177eager=eager,178).alias("tm")179)180tm = tm.select(pl.col("tm").explode())181assert tm["tm"].to_list() == [182time(23, 59, 59, 999980),183time(23, 59, 59, 999990),184]185186187def test_time_range_expr() -> None:188df = pl.DataFrame(189{190"start": pl.time_range(interval="6h", eager=True),191"stop": pl.time_range(start=time(2, 59), interval="5h59m", eager=True),192}193).with_columns(intervals=pl.time_ranges("start", pl.col("stop"), interval="1h29m"))194# shape: (4, 3)195# ┌──────────┬──────────┬────────────────────────────────┐196# │ start ┆ stop ┆ intervals │197# │ --- ┆ --- ┆ --- │198# │ time ┆ time ┆ list[time] │199# ╞══════════╪══════════╪════════════════════════════════╡200# │ 00:00:00 ┆ 02:59:00 ┆ [00:00:00, 01:29:00, 02:58:00] │201# │ 06:00:00 ┆ 08:58:00 ┆ [06:00:00, 07:29:00, 08:58:00] │202# │ 12:00:00 ┆ 14:57:00 ┆ [12:00:00, 13:29:00] │203# │ 18:00:00 ┆ 20:56:00 ┆ [18:00:00, 19:29:00] │204# └──────────┴──────────┴────────────────────────────────┘205assert df.rows() == [206(time(0, 0), time(2, 59), [time(0, 0), time(1, 29), time(2, 58)]),207(time(6, 0), time(8, 58), [time(6, 0), time(7, 29), time(8, 58)]),208(time(12, 0), time(14, 57), [time(12, 0), time(13, 29)]),209(time(18, 0), time(20, 56), [time(18, 0), time(19, 29)]),210]211212213def test_time_range_name() -> None:214expected_name = "literal"215result_eager = pl.time_range(time(10), time(12), eager=True)216assert result_eager.name == expected_name217218expected_name = "s1"219result_lazy = pl.select(220pl.time_range(221pl.lit(time(10)).alias("s1"), pl.lit(time(12)).alias("s2"), eager=False222)223).to_series()224assert result_lazy.name == expected_name225226227def test_time_ranges_broadcasting() -> None:228df = pl.DataFrame({"time": [time(10, 0), time(11, 0), time(12, 0)]})229result = df.select(230pl.time_ranges(start="time", end=time(12, 0)).alias("end"),231pl.time_ranges(start=time(10, 0), end="time").alias("start"),232)233expected = pl.DataFrame(234{235"end": [236[time(10, 0), time(11, 0), time(12, 0)],237[time(11, 0), time(12, 0)],238[time(12, 0)],239],240"start": [241[time(10, 0)],242[time(10, 0), time(11, 0)],243[time(10, 0), time(11, 0), time(12, 0)],244],245}246)247assert_frame_equal(result, expected)248249250def test_time_ranges_mismatched_chunks() -> None:251s1 = pl.Series("s1", [time(10), time(11)])252s1.append(pl.Series([time(12)]))253254s2 = pl.Series("s2", [time(12)])255s2.append(pl.Series([time(12), time(12)]))256257result = pl.time_ranges(s1, s2, eager=True)258expected = pl.Series(259"s1",260[261[time(10, 0), time(11, 0), time(12, 0)],262[time(11, 0), time(12, 0)],263[time(12, 0)],264],265)266assert_series_equal(result, expected)267268269