Skip to content

Commit 239b6a7

Browse files
authored
REF: separate to_time, avoid runtime imports (#34145)
1 parent e71052c commit 239b6a7

File tree

6 files changed

+187
-162
lines changed

6 files changed

+187
-162
lines changed

pandas/core/indexes/datetimelike.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from pandas._libs import NaT, Timedelta, iNaT, join as libjoin, lib
1010
from pandas._libs.tslibs import timezones
11+
from pandas._libs.tslibs.parsing import DateParseError
1112
from pandas._typing import Label
1213
from pandas.compat.numpy import function as nv
1314
from pandas.errors import AbstractMethodError
@@ -41,7 +42,6 @@
4142
from pandas.core.indexes.numeric import Int64Index
4243
from pandas.core.ops import get_op_result_name
4344
from pandas.core.sorting import ensure_key_mapped
44-
from pandas.core.tools.datetimes import DateParseError
4545
from pandas.core.tools.timedeltas import to_timedelta
4646

4747
from pandas.tseries.offsets import DateOffset, Tick

pandas/core/indexes/datetimes.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from pandas.core.indexes.base import Index, InvalidIndexError, maybe_extract_name
2828
from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin
2929
from pandas.core.indexes.extension import inherit_names
30-
import pandas.core.tools.datetimes as tools
30+
from pandas.core.tools.times import to_time
3131

3232
from pandas.tseries.frequencies import to_offset
3333
from pandas.tseries.offsets import prefix_mapping
@@ -778,8 +778,8 @@ def indexer_between_time(
778778
indexer_at_time : Get index locations of values at particular time of day.
779779
DataFrame.between_time : Select values between particular times of day.
780780
"""
781-
start_time = tools.to_time(start_time)
782-
end_time = tools.to_time(end_time)
781+
start_time = to_time(start_time)
782+
end_time = to_time(end_time)
783783
time_micros = self._get_time_micros()
784784
start_micros = _time_to_micros(start_time)
785785
end_micros = _time_to_micros(end_time)

pandas/core/indexes/period.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pandas._libs.lib import no_default
88
from pandas._libs.tslibs import Period
99
from pandas._libs.tslibs.frequencies import get_freq_group
10-
from pandas._libs.tslibs.parsing import parse_time_string
10+
from pandas._libs.tslibs.parsing import DateParseError, parse_time_string
1111
from pandas._typing import DtypeObj, Label
1212
from pandas.util._decorators import Appender, cache_readonly, doc
1313

@@ -43,7 +43,6 @@
4343
from pandas.core.indexes.extension import inherit_names
4444
from pandas.core.indexes.numeric import Int64Index
4545
from pandas.core.ops import get_op_result_name
46-
from pandas.core.tools.datetimes import DateParseError
4746

4847
from pandas.tseries.offsets import DateOffset, Tick
4948

pandas/core/tools/datetimes.py

+30-155
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from collections import abc
2-
from datetime import datetime, time
2+
from datetime import datetime
33
from functools import partial
44
from itertools import islice
5-
from typing import List, Optional, TypeVar, Union
5+
from typing import TYPE_CHECKING, Optional, TypeVar, Union
6+
import warnings
67

78
import numpy as np
89

@@ -28,28 +29,31 @@
2829
is_numeric_dtype,
2930
is_scalar,
3031
)
31-
from pandas.core.dtypes.generic import (
32-
ABCDataFrame,
33-
ABCDatetimeIndex,
34-
ABCIndex,
35-
ABCIndexClass,
36-
ABCSeries,
37-
)
32+
from pandas.core.dtypes.generic import ABCDataFrame, ABCSeries
3833
from pandas.core.dtypes.missing import notna
3934

4035
from pandas.arrays import DatetimeArray, IntegerArray
4136
from pandas.core import algorithms
4237
from pandas.core.algorithms import unique
43-
from pandas.core.arrays.datetimes import tz_to_dtype
38+
from pandas.core.arrays.datetimes import (
39+
maybe_convert_dtype,
40+
objects_to_datetime64ns,
41+
tz_to_dtype,
42+
)
43+
from pandas.core.indexes.base import Index
44+
from pandas.core.indexes.datetimes import DatetimeIndex
45+
46+
if TYPE_CHECKING:
47+
from pandas import Series # noqa:F401
4448

4549
# ---------------------------------------------------------------------
4650
# types used in annotations
4751

48-
ArrayConvertible = Union[list, tuple, ArrayLike, ABCSeries]
52+
ArrayConvertible = Union[list, tuple, ArrayLike, "Series"]
4953
Scalar = Union[int, float, str]
5054
DatetimeScalar = TypeVar("DatetimeScalar", Scalar, datetime)
5155
DatetimeScalarOrArrayConvertible = Union[
52-
DatetimeScalar, list, tuple, ArrayLike, ABCSeries
56+
DatetimeScalar, list, tuple, ArrayLike, "Series"
5357
]
5458

5559

@@ -156,7 +160,7 @@ def _maybe_cache(arg, format, cache, convert_listlike):
156160

157161
def _box_as_indexlike(
158162
dt_array: ArrayLike, utc: Optional[bool] = None, name: Optional[str] = None
159-
) -> Union[ABCIndex, ABCDatetimeIndex]:
163+
) -> Index:
160164
"""
161165
Properly boxes the ndarray of datetimes to DatetimeIndex
162166
if it is possible or to generic Index instead
@@ -176,7 +180,6 @@ def _box_as_indexlike(
176180
- DatetimeIndex if convertible to sole datetime64 type
177181
- general Index otherwise
178182
"""
179-
from pandas import DatetimeIndex, Index
180183

181184
if is_datetime64_dtype(dt_array):
182185
tz = "utc" if utc else None
@@ -186,9 +189,9 @@ def _box_as_indexlike(
186189

187190
def _convert_and_box_cache(
188191
arg: DatetimeScalarOrArrayConvertible,
189-
cache_array: ABCSeries,
192+
cache_array: "Series",
190193
name: Optional[str] = None,
191-
) -> ABCIndexClass:
194+
) -> "Index":
192195
"""
193196
Convert array of dates with a cache and wrap the result in an Index.
194197
@@ -235,7 +238,6 @@ def _return_parsed_timezone_results(result, timezones, tz, name):
235238
if tz is not None:
236239
# Convert to the same tz
237240
tz_results = np.array([tz_result.tz_convert(tz) for tz_result in tz_results])
238-
from pandas import Index
239241

240242
return Index(tz_results, name=name)
241243

@@ -281,11 +283,6 @@ def _convert_listlike_datetimes(
281283
-------
282284
Index-like of parsed dates
283285
"""
284-
from pandas import DatetimeIndex
285-
from pandas.core.arrays.datetimes import (
286-
maybe_convert_dtype,
287-
objects_to_datetime64ns,
288-
)
289286

290287
if isinstance(arg, (list, tuple)):
291288
arg = np.array(arg, dtype="O")
@@ -332,7 +329,6 @@ def _convert_listlike_datetimes(
332329
)
333330

334331
if errors == "ignore":
335-
from pandas import Index
336332

337333
result = Index(result, name=name)
338334
else:
@@ -366,8 +362,6 @@ def _convert_listlike_datetimes(
366362
result = np.array(["NaT"], dtype="datetime64[ns]").repeat(len(arg))
367363
return DatetimeIndex(result, name=name)
368364
elif errors == "ignore":
369-
from pandas import Index
370-
371365
result = Index(arg, name=name)
372366
return result
373367
raise
@@ -539,9 +533,7 @@ def _adjust_to_origin(arg, origin, unit):
539533
offset = offset // tslibs.Timedelta(1, unit=unit)
540534

541535
# scalars & ndarray-like can handle the addition
542-
if is_list_like(arg) and not isinstance(
543-
arg, (ABCSeries, ABCIndexClass, np.ndarray)
544-
):
536+
if is_list_like(arg) and not isinstance(arg, (ABCSeries, Index, np.ndarray)):
545537
arg = np.asarray(arg)
546538
arg = arg + offset
547539
return arg
@@ -749,7 +741,7 @@ def to_datetime(
749741
result = arg._constructor(values, index=arg.index, name=arg.name)
750742
elif isinstance(arg, (ABCDataFrame, abc.MutableMapping)):
751743
result = _assemble_from_unit_mappings(arg, errors, tz)
752-
elif isinstance(arg, ABCIndexClass):
744+
elif isinstance(arg, Index):
753745
cache_array = _maybe_cache(arg, format, cache, convert_listlike)
754746
if not cache_array.empty:
755747
result = _convert_and_box_cache(arg, cache_array, name=arg.name)
@@ -944,131 +936,14 @@ def calc_with_mask(carg, mask):
944936
return None
945937

946938

947-
# Fixed time formats for time parsing
948-
_time_formats = [
949-
"%H:%M",
950-
"%H%M",
951-
"%I:%M%p",
952-
"%I%M%p",
953-
"%H:%M:%S",
954-
"%H%M%S",
955-
"%I:%M:%S%p",
956-
"%I%M%S%p",
957-
]
958-
959-
960-
def _guess_time_format_for_array(arr):
961-
# Try to guess the format based on the first non-NaN element
962-
non_nan_elements = notna(arr).nonzero()[0]
963-
if len(non_nan_elements):
964-
element = arr[non_nan_elements[0]]
965-
for time_format in _time_formats:
966-
try:
967-
datetime.strptime(element, time_format)
968-
return time_format
969-
except ValueError:
970-
pass
971-
972-
return None
973-
974-
975939
def to_time(arg, format=None, infer_time_format=False, errors="raise"):
976-
"""
977-
Parse time strings to time objects using fixed strptime formats ("%H:%M",
978-
"%H%M", "%I:%M%p", "%I%M%p", "%H:%M:%S", "%H%M%S", "%I:%M:%S%p",
979-
"%I%M%S%p")
980-
981-
Use infer_time_format if all the strings are in the same format to speed
982-
up conversion.
983-
984-
Parameters
985-
----------
986-
arg : string in time format, datetime.time, list, tuple, 1-d array, Series
987-
format : str, default None
988-
Format used to convert arg into a time object. If None, fixed formats
989-
are used.
990-
infer_time_format: bool, default False
991-
Infer the time format based on the first non-NaN element. If all
992-
strings are in the same format, this will speed up conversion.
993-
errors : {'ignore', 'raise', 'coerce'}, default 'raise'
994-
- If 'raise', then invalid parsing will raise an exception
995-
- If 'coerce', then invalid parsing will be set as None
996-
- If 'ignore', then invalid parsing will return the input
997-
998-
Returns
999-
-------
1000-
datetime.time
1001-
"""
1002-
1003-
def _convert_listlike(arg, format):
1004-
1005-
if isinstance(arg, (list, tuple)):
1006-
arg = np.array(arg, dtype="O")
1007-
1008-
elif getattr(arg, "ndim", 1) > 1:
1009-
raise TypeError(
1010-
"arg must be a string, datetime, list, tuple, 1-d array, or Series"
1011-
)
1012-
1013-
arg = ensure_object(arg)
1014-
1015-
if infer_time_format and format is None:
1016-
format = _guess_time_format_for_array(arg)
1017-
1018-
times: List[Optional[time]] = []
1019-
if format is not None:
1020-
for element in arg:
1021-
try:
1022-
times.append(datetime.strptime(element, format).time())
1023-
except (ValueError, TypeError) as err:
1024-
if errors == "raise":
1025-
msg = (
1026-
f"Cannot convert {element} to a time with given "
1027-
f"format {format}"
1028-
)
1029-
raise ValueError(msg) from err
1030-
elif errors == "ignore":
1031-
return arg
1032-
else:
1033-
times.append(None)
1034-
else:
1035-
formats = _time_formats[:]
1036-
format_found = False
1037-
for element in arg:
1038-
time_object = None
1039-
for time_format in formats:
1040-
try:
1041-
time_object = datetime.strptime(element, time_format).time()
1042-
if not format_found:
1043-
# Put the found format in front
1044-
fmt = formats.pop(formats.index(time_format))
1045-
formats.insert(0, fmt)
1046-
format_found = True
1047-
break
1048-
except (ValueError, TypeError):
1049-
continue
1050-
1051-
if time_object is not None:
1052-
times.append(time_object)
1053-
elif errors == "raise":
1054-
raise ValueError(f"Cannot convert arg {arg} to a time")
1055-
elif errors == "ignore":
1056-
return arg
1057-
else:
1058-
times.append(None)
1059-
1060-
return times
1061-
1062-
if arg is None:
1063-
return arg
1064-
elif isinstance(arg, time):
1065-
return arg
1066-
elif isinstance(arg, ABCSeries):
1067-
values = _convert_listlike(arg._values, format)
1068-
return arg._constructor(values, index=arg.index, name=arg.name)
1069-
elif isinstance(arg, ABCIndexClass):
1070-
return _convert_listlike(arg, format)
1071-
elif is_list_like(arg):
1072-
return _convert_listlike(arg, format)
940+
# GH#34145
941+
warnings.warn(
942+
"`to_time` has been moved, should be imported from pandas.core.tools.times. "
943+
"This alias will be removed in a future version.",
944+
FutureWarning,
945+
stacklevel=2,
946+
)
947+
from pandas.core.tools.times import to_time
1073948

1074-
return _convert_listlike(np.array([arg]), format)[0]
949+
return to_time(arg, format, infer_time_format, errors)

0 commit comments

Comments
 (0)