Skip to content

REF: move generate_range from offsets to the one place it is used #34146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
80 changes: 79 additions & 1 deletion pandas/core/arrays/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
import pandas.core.common as com

from pandas.tseries.frequencies import get_period_alias, to_offset
from pandas.tseries.offsets import Day, Tick, generate_range
from pandas.tseries.offsets import BDay, Day, Tick

_midnight = time(0, 0)

Expand Down Expand Up @@ -2336,3 +2336,81 @@ def _maybe_localize_point(ts, is_none, is_not_none, freq, tz, ambiguous, nonexis
localize_args["tz"] = tz
ts = ts.tz_localize(**localize_args)
return ts


def generate_range(start=None, end=None, periods=None, offset=BDay()):
"""
Generates a sequence of dates corresponding to the specified time
offset. Similar to dateutil.rrule except uses pandas DateOffset
objects to represent time increments.

Parameters
----------
start : datetime, (default None)
end : datetime, (default None)
periods : int, (default None)
offset : DateOffset, (default BDay())

Notes
-----
* This method is faster for generating weekdays than dateutil.rrule
* At least two of (start, end, periods) must be specified.
* If both start and end are specified, the returned dates will
satisfy start <= date <= end.

Returns
-------
dates : generator object
"""
offset = to_offset(offset)

start = Timestamp(start)
start = start if start is not NaT else None
end = Timestamp(end)
end = end if end is not NaT else None

if start and not offset.is_on_offset(start):
start = offset.rollforward(start)

elif end and not offset.is_on_offset(end):
end = offset.rollback(end)

if periods is None and end < start and offset.n >= 0:
end = None
periods = 0

if end is None:
end = start + (periods - 1) * offset

if start is None:
start = end - (periods - 1) * offset

cur = start
if offset.n >= 0:
while cur <= end:
yield cur

if cur == end:
# GH#24252 avoid overflows by not performing the addition
# in offset.apply unless we have to
break

# faster than cur + offset
next_date = offset.apply(cur)
if next_date <= cur:
raise ValueError(f"Offset {offset} did not increment date")
cur = next_date
else:
while cur >= end:
yield cur

if cur == end:
# GH#24252 avoid overflows by not performing the addition
# in offset.apply unless we have to
break

# faster than cur + offset
next_date = offset.apply(cur)
if next_date >= cur:
raise ValueError(f"Offset {offset} did not decrement date")
cur = next_date
10 changes: 2 additions & 8 deletions pandas/tests/indexes/datetimes/test_date_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,9 @@
import pandas as pd
from pandas import DatetimeIndex, Timestamp, bdate_range, date_range, offsets
import pandas._testing as tm
from pandas.core.arrays.datetimes import generate_range

from pandas.tseries.offsets import (
BDay,
CDay,
DateOffset,
MonthEnd,
generate_range,
prefix_mapping,
)
from pandas.tseries.offsets import BDay, CDay, DateOffset, MonthEnd, prefix_mapping

START, END = datetime(2009, 1, 1), datetime(2010, 1, 1)

Expand Down
84 changes: 0 additions & 84 deletions pandas/tseries/offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import numpy as np

from pandas._libs.tslibs import (
NaT,
Period,
Timedelta,
Timestamp,
Expand Down Expand Up @@ -2305,89 +2304,6 @@ class Nano(Tick):
CBMonthBegin = CustomBusinessMonthBegin
CDay = CustomBusinessDay

# ---------------------------------------------------------------------


def generate_range(start=None, end=None, periods=None, offset=BDay()):
"""
Generates a sequence of dates corresponding to the specified time
offset. Similar to dateutil.rrule except uses pandas DateOffset
objects to represent time increments.

Parameters
----------
start : datetime, (default None)
end : datetime, (default None)
periods : int, (default None)
offset : DateOffset, (default BDay())

Notes
-----
* This method is faster for generating weekdays than dateutil.rrule
* At least two of (start, end, periods) must be specified.
* If both start and end are specified, the returned dates will
satisfy start <= date <= end.

Returns
-------
dates : generator object
"""
from pandas.tseries.frequencies import to_offset

offset = to_offset(offset)

start = Timestamp(start)
start = start if start is not NaT else None
end = Timestamp(end)
end = end if end is not NaT else None

if start and not offset.is_on_offset(start):
start = offset.rollforward(start)

elif end and not offset.is_on_offset(end):
end = offset.rollback(end)

if periods is None and end < start and offset.n >= 0:
end = None
periods = 0

if end is None:
end = start + (periods - 1) * offset

if start is None:
start = end - (periods - 1) * offset

cur = start
if offset.n >= 0:
while cur <= end:
yield cur

if cur == end:
# GH#24252 avoid overflows by not performing the addition
# in offset.apply unless we have to
break

# faster than cur + offset
next_date = offset.apply(cur)
if next_date <= cur:
raise ValueError(f"Offset {offset} did not increment date")
cur = next_date
else:
while cur >= end:
yield cur

if cur == end:
# GH#24252 avoid overflows by not performing the addition
# in offset.apply unless we have to
break

# faster than cur + offset
next_date = offset.apply(cur)
if next_date >= cur:
raise ValueError(f"Offset {offset} did not decrement date")
cur = next_date


prefix_mapping = {
offset._prefix: offset
for offset in [
Expand Down