Skip to content

Commit 95026c9

Browse files
committed
Change date_range inclusive
1 parent 95a3b07 commit 95026c9

File tree

3 files changed

+112
-8
lines changed

3 files changed

+112
-8
lines changed

pandas/core/arrays/datetimelike.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
Callable,
1212
Literal,
1313
Sequence,
14+
Tuple,
1415
TypeVar,
1516
Union,
1617
cast,
@@ -1823,6 +1824,71 @@ def validate_periods(periods):
18231824
return periods
18241825

18251826

1827+
def validate_inclusiveness(inclusive):
1828+
"""
1829+
Check that the `inclusive` argument is among {"both", "neither", "left", "right"}.
1830+
1831+
Parameters
1832+
----------
1833+
inclusive : {"both", "neither", "left", "right"}
1834+
1835+
Returns
1836+
-------
1837+
left_inclusive : bool
1838+
right_inclusive : bool
1839+
1840+
Raises
1841+
------
1842+
ValueError : if argument is not among valid values
1843+
"""
1844+
left_right_inclusive: Tuple[bool, bool] | None = {
1845+
"both": (True, True),
1846+
"left": (True, False),
1847+
"right": (False, True),
1848+
"neither": (False, False),
1849+
}.get(inclusive)
1850+
1851+
if left_right_inclusive is None:
1852+
raise ValueError(
1853+
"Inclusive has to be either 'both', 'neither', 'left', 'right'"
1854+
)
1855+
left_inclusive, right_inclusive = left_right_inclusive
1856+
return left_inclusive, right_inclusive
1857+
1858+
1859+
def validate_endpoints(closed):
1860+
"""
1861+
Check that the `closed` argument is among [None, "left", "right"]
1862+
1863+
Parameters
1864+
----------
1865+
closed : {None, "left", "right"}
1866+
1867+
Returns
1868+
-------
1869+
left_closed : bool
1870+
right_closed : bool
1871+
1872+
Raises
1873+
------
1874+
ValueError : if argument is not among valid values
1875+
"""
1876+
left_closed = False
1877+
right_closed = False
1878+
1879+
if closed is None:
1880+
left_closed = True
1881+
right_closed = True
1882+
elif closed == "left":
1883+
left_closed = True
1884+
elif closed == "right":
1885+
right_closed = True
1886+
else:
1887+
raise ValueError("Closed has to be either 'left', 'right' or None")
1888+
1889+
return left_closed, right_closed
1890+
1891+
18261892
def validate_inferred_freq(freq, inferred_freq, freq_infer):
18271893
"""
18281894
If the user passes a freq and another freq is inferred from passed data,

pandas/core/arrays/datetimes.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ def _generate_range(
394394
normalize=False,
395395
ambiguous="raise",
396396
nonexistent="raise",
397-
closed=None,
397+
inclusive="both",
398398
):
399399

400400
periods = dtl.validate_periods(periods)
@@ -417,7 +417,7 @@ def _generate_range(
417417
if start is NaT or end is NaT:
418418
raise ValueError("Neither `start` nor `end` can be NaT")
419419

420-
left_closed, right_closed = validate_endpoints(closed)
420+
left_inclusive, right_inclusive = dtl.validate_inclusiveness(inclusive)
421421
start, end, _normalized = _maybe_normalize_endpoints(start, end, normalize)
422422
tz = _infer_tz_from_endpoints(start, end, tz)
423423

@@ -477,10 +477,20 @@ def _generate_range(
477477
arr = arr.astype("M8[ns]", copy=False)
478478
index = cls._simple_new(arr, freq=None, dtype=dtype)
479479

480-
if not left_closed and len(index) and index[0] == start:
481-
index = index[1:]
482-
if not right_closed and len(index) and index[-1] == end:
483-
index = index[:-1]
480+
# do not remove when one side is inclusive
481+
# and removing would leave index empty
482+
to_remove_any = not (
483+
(left_inclusive or right_inclusive)
484+
and len(index) == 1
485+
and start == index[0]
486+
and start == end
487+
)
488+
489+
if to_remove_any:
490+
if (not left_inclusive) and len(index) and index[0] == start:
491+
index = index[1:]
492+
if (not right_inclusive) and len(index) and index[-1] == end:
493+
index = index[:-1]
484494

485495
dtype = tz_to_dtype(tz)
486496
return cls._simple_new(index._ndarray, freq=freq, dtype=dtype)

pandas/core/indexes/datetimes.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,8 @@ def date_range(
881881
tz=None,
882882
normalize: bool = False,
883883
name: Hashable = None,
884-
closed=None,
884+
closed: bool | lib.NoDefault = lib.no_default,
885+
inclusive: str | None = None,
885886
**kwargs,
886887
) -> DatetimeIndex:
887888
"""
@@ -919,6 +920,12 @@ def date_range(
919920
closed : {None, 'left', 'right'}, optional
920921
Make the interval closed with respect to the given frequency to
921922
the 'left', 'right', or both sides (None, the default).
923+
.. deprecated:: 1.4.0
924+
Argument `closed` have been deprecated
925+
to standardize boundary inputs. Use `inclusive` instead, to set
926+
each bound as closed or open.
927+
inclusive : {"both", "neither", "left", "right"}, default "both"
928+
Include boundaries; Whether to set each bound as closed or open.
922929
**kwargs
923930
For compatibility. Has no effect on the result.
924931
@@ -1029,6 +1036,27 @@ def date_range(
10291036
DatetimeIndex(['2017-01-02', '2017-01-03', '2017-01-04'],
10301037
dtype='datetime64[ns]', freq='D')
10311038
"""
1039+
if inclusive is not None and closed is not lib.no_default:
1040+
raise ValueError(
1041+
"Deprecated Argument `closed` cannot be passed if Argument `inclusive` is not None"
1042+
)
1043+
elif closed is not lib.no_default:
1044+
warnings.warn(
1045+
"Argument `closed` is deprectated in favor of `inclusive`.",
1046+
FutureWarning,
1047+
stacklevel=2,
1048+
)
1049+
if closed is None:
1050+
inclusive = "both"
1051+
elif closed in ("left", "right"):
1052+
inclusive = closed
1053+
else:
1054+
raise ValueError(
1055+
"Argument `closed` has to be either 'left', 'right' or None"
1056+
)
1057+
elif inclusive is None:
1058+
inclusive = "both"
1059+
10321060
if freq is None and com.any_none(periods, start, end):
10331061
freq = "D"
10341062

@@ -1039,7 +1067,7 @@ def date_range(
10391067
freq=freq,
10401068
tz=tz,
10411069
normalize=normalize,
1042-
closed=closed,
1070+
inclusive=inclusive,
10431071
**kwargs,
10441072
)
10451073
return DatetimeIndex._simple_new(dtarr, name=name)

0 commit comments

Comments
 (0)