Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit b220319

Browse files
committed
feat: add bigframes.pandas.date_range()
1 parent 6370d3b commit b220319

File tree

6 files changed

+286
-3
lines changed

6 files changed

+286
-3
lines changed

bigframes/core/indexes/datetimes.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from bigframes_vendored.pandas.core.indexes import (
2020
datetimes as vendored_pandas_datetime_index,
2121
)
22+
import pandas
2223

2324
from bigframes.core import expression as ex
2425
from bigframes.core.indexes.base import Index
@@ -54,3 +55,38 @@ def day_of_week(self) -> Index:
5455
@property
5556
def weekday(self) -> Index:
5657
return self.dayofweek
58+
59+
60+
def date_range(
61+
session,
62+
start=None,
63+
end=None,
64+
periods=None,
65+
freq=None,
66+
tz=None,
67+
normalize: bool = False,
68+
name=None,
69+
inclusive="both",
70+
*,
71+
unit: str | None = None,
72+
) -> DatetimeIndex:
73+
kwargs = {}
74+
if unit is not None:
75+
kwargs["unit"] = unit
76+
77+
pd_index = pandas.date_range(
78+
start=start,
79+
end=end,
80+
periods=periods,
81+
freq=freq,
82+
tz=tz,
83+
normalize=normalize,
84+
name=name,
85+
inclusive=inclusive,
86+
**kwargs, # type: ignore
87+
)
88+
89+
return session.read_pandas(pd_index)
90+
91+
92+
date_range.__doc__ = vendored_pandas_datetime_index.date_range.__doc__

bigframes/pandas/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import bigframes.enums
3838
import bigframes.functions._utils as bff_utils
3939
from bigframes.pandas.core.api import to_timedelta
40+
from bigframes.pandas.core.indexes.datetimes import date_range
4041
from bigframes.pandas.io.api import (
4142
_read_gbq_colab,
4243
from_glob_path,
@@ -367,6 +368,7 @@ def reset_session():
367368
clean_up_by_session_id,
368369
concat,
369370
cut,
371+
date_range,
370372
deploy_remote_function,
371373
deploy_udf,
372374
get_default_session_id,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from __future__ import annotations
16+
17+
import inspect
18+
19+
import bigframes.core.global_session as global_session
20+
import bigframes.core.indexes
21+
import bigframes.session
22+
23+
24+
def date_range(
25+
start=None,
26+
end=None,
27+
periods=None,
28+
freq=None,
29+
tz=None,
30+
normalize: bool = False,
31+
name=None,
32+
inclusive="both",
33+
*,
34+
unit: str | None = None,
35+
) -> bigframes.core.indexes.DatetimeIndex:
36+
return global_session.with_default_session(
37+
bigframes.session.Session.date_range,
38+
start=start,
39+
end=end,
40+
periods=periods,
41+
freq=freq,
42+
tz=tz,
43+
normalize=normalize,
44+
name=name,
45+
inclusive=inclusive,
46+
unit=unit,
47+
)
48+
49+
50+
date_range.__doc__ = inspect.getdoc(bigframes.session.Session.date_range)

bigframes/session/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,8 @@
6868
import bigframes.constants
6969
import bigframes.core
7070
from bigframes.core import blocks, log_adapter, utils
71+
import bigframes.core.indexes.datetimes
7172
import bigframes.core.pyformat
72-
73-
# Even though the ibis.backends.bigquery import is unused, it's needed
74-
# to register new and replacement ops with the Ibis BigQuery backend.
7573
import bigframes.functions._function_session as bff_session
7674
import bigframes.functions.function as bff
7775
from bigframes.session import bigquery_session, bq_caching_executor, executor
@@ -387,6 +385,8 @@ def close(self):
387385
self.bqclient, self.cloudfunctionsclient, self.session_id
388386
)
389387

388+
date_range = bigframes.core.indexes.datetimes.date_range
389+
390390
@overload
391391
def read_gbq( # type: ignore[overload-overlap]
392392
self,

third_party/bigframes_vendored/pandas/core/indexes/datetimes.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,185 @@ def weekday(self) -> base.Index:
104104
Index([5], dtype='Int64')
105105
"""
106106
raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE)
107+
108+
109+
def date_range(
110+
start=None,
111+
end=None,
112+
periods=None,
113+
freq=None,
114+
tz=None,
115+
normalize: bool = False,
116+
name=None,
117+
inclusive="both",
118+
*,
119+
unit: str | None = None,
120+
**kwargs,
121+
) -> DatetimeIndex:
122+
"""
123+
Return a fixed frequency DatetimeIndex.
124+
125+
Returns the range of equally spaced time points (where the difference between any
126+
two adjacent points is specified by the given frequency) such that they fall in the
127+
range `[start, end]` , where the first one and the last one are, resp., the first
128+
and last time points in that range that fall on the boundary of ``freq`` (if given
129+
as a frequency string) or that are valid for ``freq`` (if given as a
130+
:class:`pandas.tseries.offsets.DateOffset`). If ``freq`` is positive, the points
131+
satisfy `start <[=] x <[=] end`, and if ``freq`` is negative, the points satisfy
132+
`end <[=] x <[=] start`. (If exactly one of ``start``, ``end``, or ``freq`` is *not*
133+
specified, this missing parameter can be computed given ``periods``, the number of
134+
timesteps in the range.)
135+
136+
Of the four parameters ``start``, ``end``, ``periods``, and ``freq``,
137+
exactly three must be specified. If ``freq`` is omitted, the resulting
138+
``DatetimeIndex`` will have ``periods`` linearly spaced elements between
139+
``start`` and ``end`` (closed on both sides).
140+
141+
To learn more about the frequency strings, please see
142+
:ref:`this link<timeseries.offset_aliases>`.
143+
144+
**Examples:**
145+
146+
>>> import bigframes.pandas as bpd
147+
>>> import pandas as pd
148+
>>> bpd.options.display.progress_bar = None
149+
150+
**Specifying the values**
151+
152+
The next four examples generate the same `DatetimeIndex`, but vary
153+
the combination of `start`, `end` and `periods`.
154+
155+
Specify `start` and `end`, with the default daily frequency.
156+
157+
>>> bpd.date_range(start="1/1/2018", end="1/08/2018")
158+
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
159+
'2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'],
160+
dtype='datetime64[ns]', freq='D')
161+
162+
Specify timezone-aware `start` and `end`, with the default daily frequency.
163+
164+
>>> bpd.date_range(
165+
... start=pd.to_datetime("1/1/2018").tz_localize("Europe/Berlin"),
166+
... end=pd.to_datetime("1/08/2018").tz_localize("Europe/Berlin"),
167+
... )
168+
DatetimeIndex(['2018-01-01 00:00:00+01:00', '2018-01-02 00:00:00+01:00',
169+
'2018-01-03 00:00:00+01:00', '2018-01-04 00:00:00+01:00',
170+
'2018-01-05 00:00:00+01:00', '2018-01-06 00:00:00+01:00',
171+
'2018-01-07 00:00:00+01:00', '2018-01-08 00:00:00+01:00'],
172+
dtype='datetime64[ns, Europe/Berlin]', freq='D')
173+
174+
Specify `start` and `periods`, the number of periods (days).
175+
176+
>>> bpd.date_range(start="1/1/2018", periods=8)
177+
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
178+
'2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'],
179+
dtype='datetime64[ns]', freq='D')
180+
181+
Specify `end` and `periods`, the number of periods (days).
182+
183+
>>> bpd.date_range(end="1/1/2018", periods=8)
184+
DatetimeIndex(['2017-12-25', '2017-12-26', '2017-12-27', '2017-12-28',
185+
'2017-12-29', '2017-12-30', '2017-12-31', '2018-01-01'],
186+
dtype='datetime64[ns]', freq='D')
187+
188+
Specify `start`, `end`, and `periods`; the frequency is generated
189+
automatically (linearly spaced).
190+
191+
>>> bpd.date_range(start="2018-04-24", end="2018-04-27", periods=3)
192+
DatetimeIndex(['2018-04-24 00:00:00', '2018-04-25 12:00:00',
193+
'2018-04-27 00:00:00'],
194+
dtype='datetime64[ns]', freq=None)
195+
196+
**Other Parameters**
197+
198+
Changed the `freq` (frequency) to ``'ME'`` (month end frequency).
199+
200+
>>> bpd.date_range(start="1/1/2018", periods=5, freq="ME")
201+
DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31', '2018-04-30',
202+
'2018-05-31'],
203+
dtype='datetime64[ns]', freq='ME')
204+
205+
Multiples are allowed
206+
207+
>>> bpd.date_range(start="1/1/2018", periods=5, freq="3ME")
208+
DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31',
209+
'2019-01-31'],
210+
dtype='datetime64[ns]', freq='3ME')
211+
212+
`freq` can also be specified as an Offset object.
213+
214+
>>> bpd.date_range(start="1/1/2018", periods=5, freq=pd.offsets.MonthEnd(3))
215+
DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31',
216+
'2019-01-31'],
217+
dtype='datetime64[ns]', freq='3ME')
218+
219+
Specify `tz` to set the timezone.
220+
221+
>>> bpd.date_range(start="1/1/2018", periods=5, tz="Asia/Tokyo")
222+
DatetimeIndex(['2018-01-01 00:00:00+09:00', '2018-01-02 00:00:00+09:00',
223+
'2018-01-03 00:00:00+09:00', '2018-01-04 00:00:00+09:00',
224+
'2018-01-05 00:00:00+09:00'],
225+
dtype='datetime64[ns, Asia/Tokyo]', freq='D')
226+
227+
`inclusive` controls whether to include `start` and `end` that are on the
228+
boundary. The default, "both", includes boundary points on either end.
229+
230+
>>> bpd.date_range(start="2017-01-01", end="2017-01-04", inclusive="both")
231+
DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03', '2017-01-04'],
232+
dtype='datetime64[ns]', freq='D')
233+
234+
Use ``inclusive='left'`` to exclude `end` if it falls on the boundary.
235+
236+
>>> bpd.date_range(start="2017-01-01", end="2017-01-04", inclusive="left")
237+
DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03'],
238+
dtype='datetime64[ns]', freq='D')
239+
240+
Use ``inclusive='right'`` to exclude `start` if it falls on the boundary, and
241+
similarly ``inclusive='neither'`` will exclude both `start` and `end`.
242+
243+
>>> bpd.date_range(start="2017-01-01", end="2017-01-04", inclusive="right")
244+
DatetimeIndex(['2017-01-02', '2017-01-03', '2017-01-04'],
245+
dtype='datetime64[ns]', freq='D')
246+
247+
**Specify a unit**
248+
249+
>>> bpd.date_range(start="2017-01-01", periods=10, freq="100YS", unit="s")
250+
DatetimeIndex(['2017-01-01', '2117-01-01', '2217-01-01', '2317-01-01',
251+
'2417-01-01', '2517-01-01', '2617-01-01', '2717-01-01',
252+
'2817-01-01', '2917-01-01'],
253+
dtype='datetime64[s]', freq='100YS-JAN')
254+
255+
Arguments:
256+
start (str or datetime-like, optional):
257+
Left bound for generating dates.
258+
end (str or datetime-like, optional):
259+
Right bound for generating dates.
260+
periods (int, optional):
261+
Number of periods to generate.
262+
freq (str, Timedelta, datetime.timedelta, or DateOffset, default 'D'):
263+
Frequency strings can have multiples, e.g. '5h'. See
264+
:ref:`here <timeseries.offset_aliases>` for a list of
265+
frequency aliases.
266+
tz (str or tzinfo, optional):
267+
Time zone name for returning localized DatetimeIndex. By default,
268+
the resulting DatetimeIndex is timezone-naive unless timezone-aware
269+
datetime-likes are passed.
270+
271+
"UTC" is the only currently-supported timezone.
272+
normalize (bool, default False):
273+
Normalize start/end dates to midnight before generating date range.
274+
name (str, default None):
275+
Name of the resulting DatetimeIndex.
276+
inclusive ({"both", "neither", "left", "right"}, default "both"):
277+
Include boundaries; Whether to set each bound as closed or open.
278+
unit (str, default None):
279+
Specify the desired resolution of the result.
280+
281+
"us" is the only currently-supported resolution.
282+
283+
Returns:
284+
DatetimeIndex
285+
A DatetimeIndex object of the generated dates.
286+
"""
287+
288+
raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE)

0 commit comments

Comments
 (0)