Skip to content

enhancement on times.py #59994

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

Closed
wants to merge 1 commit into from
Closed
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
124 changes: 58 additions & 66 deletions pandas/core/tools/times.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,48 @@
from __future__ import annotations

from datetime import (
datetime,
time,
)
from typing import TYPE_CHECKING

from datetime import datetime, time
from typing import TYPE_CHECKING, List, Union
import numpy as np

from pandas._libs.lib import is_list_like

from pandas.core.dtypes.generic import (
ABCIndex,
ABCSeries,
)
from pandas.core.dtypes.generic import ABCIndex, ABCSeries
from pandas.core.dtypes.missing import notna

if TYPE_CHECKING:
from pandas._typing import DateTimeErrorChoices


def to_time(
arg,
format: str | None = None,
infer_time_format: bool = False,
errors: DateTimeErrorChoices = "raise",
arg,
format: Union[str, List[str], None] = None,
infer_time_format: bool = False,
errors: DateTimeErrorChoices = "raise",
custom_formats: List[str] = None,
):
"""
Parse time strings to time objects using fixed strptime formats ("%H:%M",
"%H%M", "%I:%M%p", "%I%M%p", "%H:%M:%S", "%H%M%S", "%I:%M:%S%p",
"%I%M%S%p")

Use infer_time_format if all the strings are in the same format to speed
up conversion.

Parameters
----------
arg : string in time format, datetime.time, list, tuple, 1-d array, Series
format : str, default None
Format used to convert arg into a time object. If None, fixed formats
are used.
infer_time_format: bool, default False
Infer the time format based on the first non-NaN element. If all
strings are in the same format, this will speed up conversion.
errors : {'raise', 'coerce'}, default 'raise'
- If 'raise', then invalid parsing will raise an exception
- If 'coerce', then invalid parsing will be set as None

Returns
-------
datetime.time
"""
Parse time strings to time objects using fixed strptime formats ("%H:%M",
"%H%M", "%I:%M%p", "%I%M%p", "%H:%M:%S", "%H%M%S", "%I:%M:%S%p",
"%I%M%S%p") and additional custom formats.

Use infer_time_format if all the strings are in the same format to speed
up conversion.

Parameters
----------
arg : string in time format, datetime.time, list, tuple, 1-d array, Series
format : str or list of str, default None
Format(s) used to convert arg into a time object. If None, fixed
formats are used.
infer_time_format: bool, default False
Infer the time format based on the first non-NaN element. If all
strings are in the same format, this will speed up conversion.
errors : {'raise', 'coerce'}, default 'raise'
- If 'raise', then invalid parsing will raise an exception
- If 'coerce', then invalid parsing will be set as None
custom_formats : list of str, default None
Additional custom time formats to use.
Returns
-------
datetime.time or list of datetime.time
"""
if errors not in ("raise", "coerce"):
raise ValueError("errors must be one of 'raise', or 'coerce'.")

Expand All @@ -62,28 +54,37 @@ def _convert_listlike(arg, format):
raise TypeError(
"arg must be a string, datetime, list, tuple, 1-d array, or Series"
)

arg = np.asarray(arg, dtype="O")

if infer_time_format and format is None:
format = _guess_time_format_for_array(arg)

times: list[time | None] = []
times = []
if format is not None:
for element in arg:
try:
times.append(datetime.strptime(element, format).time())
except (ValueError, TypeError) as err:
if errors == "raise":
msg = (
f"Cannot convert {element} to a time with given "
f"format {format}"
)
raise ValueError(msg) from err
times.append(None)
if isinstance(format, list):
for element in arg:
for fmt in format:
try:
times.append(datetime.strptime(element, fmt).time())
break
except (ValueError, TypeError):
continue
else:
if errors == "raise":
msg = (
f"Cannot convert {element} to a time with given "f"formats {format}")
raise ValueError(msg)
times.append(None)
else:
for element in arg:
try:
times.append(datetime.strptime(element, format).time())
except (ValueError, TypeError) as err:
if errors == "raise":
msg = (f"Cannot convert {element} to a time withgiven "f"format {format}")
raise ValueError(msg) from err
times.append(None)
else:
formats = _time_formats[:]
format_found = False
formats = _time_formats + (custom_formats or [])
for element in arg:
time_object = None
try:
Expand All @@ -92,22 +93,15 @@ def _convert_listlike(arg, format):
for time_format in formats:
try:
time_object = datetime.strptime(element, time_format).time()
if not format_found:
# Put the found format in front
fmt = formats.pop(formats.index(time_format))
formats.insert(0, fmt)
format_found = True
break
except (ValueError, TypeError):
continue

if time_object is not None:
times.append(time_object)
elif errors == "raise":
raise ValueError(f"Cannot convert arg {arg} to a time")
else:
times.append(None)

return times

if arg is None:
Expand All @@ -121,7 +115,6 @@ def _convert_listlike(arg, format):
return _convert_listlike(arg, format)
elif is_list_like(arg):
return _convert_listlike(arg, format)

return _convert_listlike(np.array([arg]), format)[0]


Expand Down Expand Up @@ -149,5 +142,4 @@ def _guess_time_format_for_array(arr):
return time_format
except ValueError:
pass

return None
Loading