-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
BUG: args offset in Timestamp.__new__ causes bugs #52344
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
Changes from 20 commits
9371f22
b7b37ad
e97541d
b3282a5
6ee4770
e0699ba
ebd419e
870dc40
635e0cc
c942b70
7e369a9
09fcdec
f3c7731
831597b
77ae64a
1d77bb5
e292b47
5da6b8d
9aa0187
6257147
162bf90
a4682bb
5ddc6fb
9b821e5
0382fcd
f355aaf
2811a1c
6fd531c
837c625
b4c389e
f3b53ad
acefe47
80b2b69
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -165,6 +165,7 @@ Datetimelike | |
- Bug in :meth:`Timestamp.round` with values close to the implementation bounds returning incorrect results instead of raising ``OutOfBoundsDatetime`` (:issue:`51494`) | ||
- :meth:`arrays.DatetimeArray.map` can now take a ``na_action`` argument. :meth:`DatetimeIndex.map` with ``na_action="ignore"`` now works as expected. (:issue:`51644`) | ||
- Bug in :meth:`arrays.DatetimeArray.map` and :meth:`DatetimeIndex.map`, where the supplied callable operated array-wise instead of element-wise (:issue:`51977`) | ||
- Bug in :class:`Timestamp` raising an error when passing fold when constructing from positional arguments. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you put "fold" in quotes/ticks |
||
- | ||
|
||
Timedelta | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1520,31 +1520,14 @@ class Timestamp(_Timestamp): | |
""" | ||
return cls(datetime.combine(date, time)) | ||
|
||
def __new__( | ||
cls, | ||
object ts_input=_no_input, | ||
year=None, | ||
month=None, | ||
day=None, | ||
hour=None, | ||
minute=None, | ||
second=None, | ||
microsecond=None, | ||
tzinfo_type tzinfo=None, | ||
*, | ||
nanosecond=None, | ||
tz=None, | ||
unit=None, | ||
fold=None, | ||
): | ||
def __new__(cls, *args, **kwargs): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this get rendered in the docs somewhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AlexKirko any idea about this? |
||
# The parameter list folds together legacy parameter names (the first | ||
# four) and positional and keyword parameter names from pydatetime. | ||
# | ||
# There are three calling forms: | ||
# | ||
# - In the legacy form, the first parameter, ts_input, is required | ||
# and may be datetime-like, str, int, or float. The second | ||
# parameter, offset, is optional and may be str or DateOffset. | ||
# and may be datetime-like, str, int, or float. | ||
# | ||
# - ints in the first, second, and third arguments indicate | ||
# pydatetime positional arguments. Only the first 8 arguments | ||
|
@@ -1553,17 +1536,81 @@ class Timestamp(_Timestamp): | |
# check that the second argument is an int. | ||
# | ||
# - Nones for the first four (legacy) arguments indicate pydatetime | ||
# keyword arguments. year, month, and day are required. As a | ||
# shortcut, we just check that the first argument was not passed. | ||
# | ||
# Mixing pydatetime positional and keyword arguments is forbidden! | ||
# keyword arguments. year, month, and day are required. We just | ||
# check that no positional arguments were passed. | ||
|
||
cdef: | ||
object ts_input=_no_input | ||
_TSObject ts | ||
tzinfo_type tzobj | ||
tzinfo_type tzinfo, tzobj | ||
|
||
_date_attributes = [year, month, day, hour, minute, second, | ||
microsecond, nanosecond] | ||
args_len = len(args) | ||
|
||
# GH 30543 if pd.Timestamp already passed, return it | ||
# check that only ts_input is passed | ||
# checking verbosely, because cython doesn't optimize | ||
# list comprehensions (as of cython 0.29.x) | ||
if args_len == 1 and len(kwargs) == 0 and isinstance(args[0], _Timestamp): | ||
return args[0] | ||
AlexKirko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Check that kwargs weren't passed as args | ||
# if args_len > 9: | ||
# raise ValueError(invalid_args_msg) | ||
|
||
# Unpack the arguments | ||
# Building from ts_input | ||
if args_len == 1: | ||
if kwargs: | ||
AlexKirko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if ("year" in kwargs or "month" in kwargs or "day" in kwargs or | ||
"hour" in kwargs or "minute" in kwargs or "second" in kwargs or | ||
"microsecond" in kwargs): | ||
raise ValueError("Cannot pass a date attribute keyword argument") | ||
if isinstance(args[0], str) and "nanosecond" in kwargs: | ||
raise ValueError( | ||
"Cannot pass a date attribute keyword " | ||
"argument when passing a date string; 'tz' is keyword-only" | ||
) | ||
|
||
ts_input = args[0] | ||
tzinfo = kwargs.get("tzinfo") | ||
# Building from positional arguments | ||
elif 9 > args_len > 2 and isinstance(args[1], int): | ||
args = args + (None,) * (8 - args_len) | ||
year, month, day, hour, minute, second, microsecond, tzinfo = args | ||
|
||
if kwargs: | ||
# Positional or keyword arguments | ||
hour = kwargs.get("hour", hour) | ||
AlexKirko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
minute = kwargs.get("minute", minute) | ||
second = kwargs.get("second", second) | ||
microsecond = kwargs.get("microsecond", microsecond) | ||
tzinfo = kwargs.get("tzinfo", tzinfo) | ||
# Keywords only | ||
elif args_len == 0: | ||
ts_input = kwargs.get("ts_input", _no_input) | ||
year = kwargs.get("year") | ||
month = kwargs.get("month") | ||
day = kwargs.get("day") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there anyway to simply translate the args to kwargs near the top this looks so fragile and error prone |
||
hour = kwargs.get("hour") | ||
minute = kwargs.get("minute") | ||
second = kwargs.get("second") | ||
microsecond = kwargs.get("microsecond") | ||
tzinfo = kwargs.get("tzinfo") | ||
# kludge for reading legacy pickle with read_pickle in test_pickle | ||
elif (args_len == 3 and isinstance(args[0], int) and | ||
(not isinstance(args[1], int) or not isinstance(args[2], int))): | ||
ts_input = args[0] | ||
tzinfo = args[-1] | ||
else: | ||
raise ValueError( | ||
f"Invalid Timestamp arguments. args: {args}, kwargs: {kwargs}" | ||
) | ||
|
||
# Unpack keyword-only arguments | ||
nanosecond = kwargs.get("nanosecond", 0) | ||
tz = kwargs.get("tz") | ||
unit = kwargs.get("unit") | ||
fold = kwargs.get("fold") | ||
|
||
if tzinfo is not None: | ||
# GH#17690 tzinfo must be a datetime.tzinfo object, ensured | ||
|
@@ -1600,27 +1647,7 @@ class Timestamp(_Timestamp): | |
if hasattr(ts_input, "fold"): | ||
ts_input = ts_input.replace(fold=fold) | ||
|
||
# GH 30543 if pd.Timestamp already passed, return it | ||
# check that only ts_input is passed | ||
# checking verbosely, because cython doesn't optimize | ||
# list comprehensions (as of cython 0.29.x) | ||
if (isinstance(ts_input, _Timestamp) and | ||
tz is None and unit is None and year is None and | ||
month is None and day is None and hour is None and | ||
minute is None and second is None and | ||
microsecond is None and nanosecond is None and | ||
tzinfo is None): | ||
return ts_input | ||
elif isinstance(ts_input, str): | ||
# User passed a date string to parse. | ||
# Check that the user didn't also pass a date attribute kwarg. | ||
if any(arg is not None for arg in _date_attributes): | ||
raise ValueError( | ||
"Cannot pass a date attribute keyword " | ||
"argument when passing a date string; 'tz' is keyword-only" | ||
) | ||
|
||
elif ts_input is _no_input: | ||
if ts_input is _no_input: | ||
# GH 31200 | ||
# When year, month or day is not given, we call the datetime | ||
# constructor to make sure we get the same error message | ||
|
@@ -1641,14 +1668,6 @@ class Timestamp(_Timestamp): | |
|
||
ts_input = datetime(**datetime_kwargs) | ||
|
||
elif is_integer_object(year): | ||
# User passed positional arguments: | ||
# Timestamp(year, month, day[, hour[, minute[, second[, | ||
# microsecond[, tzinfo]]]]]) | ||
ts_input = datetime(ts_input, year, month, day or 0, | ||
hour or 0, minute or 0, second or 0, fold=fold or 0) | ||
unit = None | ||
|
||
if getattr(ts_input, "tzinfo", None) is not None and tz is not None: | ||
raise ValueError("Cannot pass a datetime or Timestamp with tzinfo with " | ||
"the tz parameter. Use tz_convert instead.") | ||
|
@@ -1659,9 +1678,7 @@ class Timestamp(_Timestamp): | |
# wall-time (consistent with DatetimeIndex) | ||
return cls(ts_input).tz_localize(tzobj) | ||
|
||
if nanosecond is None: | ||
nanosecond = 0 | ||
elif not (999 >= nanosecond >= 0): | ||
if not (999 >= nanosecond >= 0): | ||
raise ValueError("nanosecond must be in 0..999") | ||
|
||
ts = convert_to_tsobject(ts_input, tzobj, unit, 0, 0, nanosecond) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An additional benchmark for future regressions might be helpful.