Skip to content

ENH: to_datetime support iso week year (16607) #16661

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 15 commits into from
23 changes: 14 additions & 9 deletions pandas/_libs/tslib.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3827,25 +3827,30 @@ def array_strptime(ndarray[object] values, object fmt,
if iso_week == -1 or weekday == -1:
raise ValueError("ISO year directive '%G' must be used with "
"the ISO week directive '%V' and a weekday "
"directive ('%w', or '%u').")
"directive ('%A', '%a', '%w', or '%u').")
if julian != -1:
raise ValueError("Day of the year directive '%j' is not "
"compatible with ISO year directive '%G'. "
"Use '%Y' instead.")
elif week_of_year == -1 and iso_week != -1:
elif year != -1 and week_of_year == -1 and iso_week != -1:
if weekday == -1:
raise ValueError("ISO week directive '%V' must be used with "
"the ISO year directive '%G' and a weekday "
"directive ('%w', or '%u').")
"directive ('%A', '%a', '%w', or '%u').")
else:
raise ValueError("ISO week directive '%V' is incompatible with "
" the year directive '%Y'. Use the ISO year "
"'%G' instead.")

# If we know the wk of the year and what day of that wk, we can figure
# out the Julian day of the year.
if julian == -1 and week_of_year != -1 and weekday != -1:
week_starts_Mon = True if week_of_year_start == 0 else False
julian = _calc_julian_from_U_or_W(year, week_of_year, weekday,
week_starts_Mon)
elif iso_year != -1 and iso_week != -1 and weekday != -1:
year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1)
if julian == -1 and weekday != -1:
if week_of_year != -1:
week_starts_Mon = True if week_of_year_start == 0 else False
julian = _calc_julian_from_U_or_W(year, week_of_year, weekday,
week_starts_Mon)
elif iso_year != -1 and iso_week != -1:
year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1)

# Cannot pre-calculate datetime_date() since can change in Julian
# calculation and thus could have different value for the day of the wk
Expand Down
32 changes: 30 additions & 2 deletions pandas/tests/indexes/datetimes/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,40 @@ class TestToDatetime(object):

def test_to_datetime_iso_week_year_format(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • do these cases separately.
  • need tests for each of the ValueErrors that you are raising
  • as I indicated above, need to exercise the paths in the code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jreback Can you please clarify this--> as I indicated above, need to exercise the paths in the code.?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#16661 (comment)

I mean exactly this. you have an if clause so we need a test for both cases.

ordinal may be negative or 0 now, which means the date is in the previous calendar year

data = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's parametrize this using pytest:

@pytest.mark.parametrize(...)
def test_to_datetime_iso_week_year_format(s, _format, dt):
...

Also note that I use "_format" instead of "format" because as a personal preference, I prefer not to use keywords when creating local variables.

['2015-53-1', '%G-%V-%u',
datetime(2015, 12, 28, 0, 0)]
['2015-1-1', '%G-%V-%u',
datetime(2014, 12, 29, 0, 0)], #negative ordinal (date in previous year)
['2015-1-4', '%G-%V-%u',
datetime(2015, 1, 1, 0, 0)],
['2015-1-7', '%G-%V-%u',
datetime(2015, 1, 4, 0, 0)]
]
for s, format, dt in data:
assert to_datetime(s, format=format) == dt

def test_ValueError_iso_week_year(self):
# 1. ISO week (%V) is specified, but the year is specified with %Y
# instead of %G
with pytest.raises(ValueError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use tm.assert_raises_regex instead. It is stronger to test not only the Exception raised but also the message that comes along with it.

to_datetime("1999 50", format="%Y %V")
# 2. ISO year (%G) and ISO week (%V) are specified, but weekday is not
with pytest.raises(ValueError):
to_datetime("1999 51", format="%G %V")
# 3. ISO year (%G) and weekday are specified, but ISO week (%V) is not
for w in ('A', 'a', 'w', 'u'):
with pytest.raises(ValueError):
to_datetime("1999 51", format="%G %{}".format(w))
# 4. ISO year is specified alone
with pytest.raises(ValueError):
to_datetime("2015", format="%G")
# 5. Julian/ordinal day (%j) is specified with %G, but not %Y
with pytest.raises(ValueError):
to_datetime("1999 256", format="%G %j")
#6. ISO week (%V) and weekday are specified, but ISO year (%G) is not
for w in ('A', 'a', 'w', 'u'):
with pytest.raises(ValueError):
to_datetime("51 11", format="%V %{}".format(w))


def test_to_datetime_dt64s(self):
in_bound_dts = [
np.datetime64('2000-01-01'),
Expand Down