diff --git a/lib/matplotlib/dates.py b/lib/matplotlib/dates.py index e073a3b0659c..3464d2edfd25 100644 --- a/lib/matplotlib/dates.py +++ b/lib/matplotlib/dates.py @@ -332,11 +332,7 @@ def _dt64_to_ordinalf(d): NaT_int = np.datetime64('NaT').astype(np.int64) d_int = d.astype(np.int64) - try: - dt[d_int == NaT_int] = np.nan - except TypeError: - if d_int == NaT_int: - dt = np.nan + dt[d_int == NaT_int] = np.nan return dt @@ -592,7 +588,8 @@ def drange(dstart, dend, delta): # ensure, that an half open interval will be generated [dstart, dend) if dinterval_end >= dend: - # if the endpoint is greater than dend, just subtract one delta + # if the endpoint is greater than or equal to dend, + # just subtract one delta dinterval_end -= delta num -= 1 @@ -1534,11 +1531,6 @@ def __init__(self, bymonth=None, bymonthday=1, interval=1, tz=None): """ if bymonth is None: bymonth = range(1, 13) - elif isinstance(bymonth, np.ndarray): - # This fixes a bug in dateutil <= 2.3 which prevents the use of - # numpy arrays in (among other things) the bymonthday, byweekday - # and bymonth parameters. - bymonth = [x.item() for x in bymonth.astype(int)] rule = rrulewrapper(MONTHLY, bymonth=bymonth, bymonthday=bymonthday, interval=interval, **self.hms0d) @@ -1562,12 +1554,6 @@ def __init__(self, byweekday=1, interval=1, tz=None): *interval* specifies the number of weeks to skip. For example, ``interval=2`` plots every second week. """ - if isinstance(byweekday, np.ndarray): - # This fixes a bug in dateutil <= 2.3 which prevents the use of - # numpy arrays in (among other things) the bymonthday, byweekday - # and bymonth parameters. - [x.item() for x in byweekday.astype(int)] - rule = rrulewrapper(DAILY, byweekday=byweekday, interval=interval, **self.hms0d) super().__init__(rule, tz=tz) @@ -1588,11 +1574,6 @@ def __init__(self, bymonthday=None, interval=1, tz=None): raise ValueError("interval must be an integer greater than 0") if bymonthday is None: bymonthday = range(1, 32) - elif isinstance(bymonthday, np.ndarray): - # This fixes a bug in dateutil <= 2.3 which prevents the use of - # numpy arrays in (among other things) the bymonthday, byweekday - # and bymonth parameters. - bymonthday = [x.item() for x in bymonthday.astype(int)] rule = rrulewrapper(DAILY, bymonthday=bymonthday, interval=interval, **self.hms0d) diff --git a/lib/matplotlib/tests/test_dates.py b/lib/matplotlib/tests/test_dates.py index e5ad2130a5ea..b6caebf83cfc 100644 --- a/lib/matplotlib/tests/test_dates.py +++ b/lib/matplotlib/tests/test_dates.py @@ -357,9 +357,13 @@ def test_drange(): # dates from an half open interval [start, end) assert len(mdates.drange(start, end, delta)) == 24 + # Same if interval ends slightly earlier + end = end - datetime.timedelta(microseconds=1) + assert len(mdates.drange(start, end, delta)) == 24 + # if end is a little bit later, we expect the range to contain one element # more - end = end + datetime.timedelta(microseconds=1) + end = end + datetime.timedelta(microseconds=2) assert len(mdates.drange(start, end, delta)) == 25 # reset end @@ -1012,6 +1016,20 @@ def attach_tz(dt, zi): _test_rrulewrapper(attach_tz, dateutil.tz.gettz) + SYD = dateutil.tz.gettz('Australia/Sydney') + dtstart = datetime.datetime(2017, 4, 1, 0) + dtend = datetime.datetime(2017, 4, 4, 0) + rule = mdates.rrulewrapper(freq=dateutil.rrule.DAILY, dtstart=dtstart, + tzinfo=SYD, until=dtend) + assert rule.after(dtstart) == datetime.datetime(2017, 4, 2, 0, 0, + tzinfo=SYD) + assert rule.before(dtend) == datetime.datetime(2017, 4, 3, 0, 0, + tzinfo=SYD) + + # Test parts of __getattr__ + assert rule._base_tzinfo == SYD + assert rule._interval == 1 + @pytest.mark.pytz def test_rrulewrapper_pytz(): @@ -1046,6 +1064,15 @@ def test_yearlocator_pytz(): '2014-01-01 00:00:00-05:00', '2015-01-01 00:00:00-05:00'] st = list(map(str, mdates.num2date(locator(), tz=tz))) assert st == expected + assert np.allclose(locator.tick_values(x[0], x[1]), np.array( + [14610.20833333, 14610.33333333, 14610.45833333, 14610.58333333, + 14610.70833333, 14610.83333333, 14610.95833333, 14611.08333333, + 14611.20833333])) + assert np.allclose(locator.get_locator(x[1], x[0]).tick_values(x[0], x[1]), + np.array( + [14610.20833333, 14610.33333333, 14610.45833333, 14610.58333333, + 14610.70833333, 14610.83333333, 14610.95833333, 14611.08333333, + 14611.20833333])) def test_YearLocator(): @@ -1290,18 +1317,14 @@ def test_datestr2num(): month=1, day=10)).size == 0 -def test_concise_formatter_exceptions(): +@pytest.mark.parametrize('kwarg', + ('formats', 'zero_formats', 'offset_formats')) +def test_concise_formatter_exceptions(kwarg): locator = mdates.AutoDateLocator() - with pytest.raises(ValueError, match="formats argument must be a list"): - mdates.ConciseDateFormatter(locator, formats=['', '%Y']) - - with pytest.raises(ValueError, - match="zero_formats argument must be a list"): - mdates.ConciseDateFormatter(locator, zero_formats=['', '%Y']) - - with pytest.raises(ValueError, - match="offset_formats argument must be a list"): - mdates.ConciseDateFormatter(locator, offset_formats=['', '%Y']) + kwargs = {kwarg: ['', '%Y']} + match = f"{kwarg} argument must be a list" + with pytest.raises(ValueError, match=match): + mdates.ConciseDateFormatter(locator, **kwargs) def test_concise_formatter_call(): @@ -1340,3 +1363,29 @@ def test_datetime_masked(): fig, ax = plt.subplots() ax.plot(x, m) assert ax.get_xlim() == (0, 1) + + +@pytest.mark.parametrize('val', (-1000000, 10000000)) +def test_num2date_error(val): + with pytest.raises(ValueError, match=f"Date ordinal {val} converts"): + mdates.num2date(val) + + +def test_num2date_roundoff(): + assert mdates.num2date(100000.0000578702) == datetime.datetime( + 2243, 10, 17, 0, 0, 4, 999980, tzinfo=datetime.timezone.utc) + # Slightly larger, steps of 20 microseconds + assert mdates.num2date(100000.0000578703) == datetime.datetime( + 2243, 10, 17, 0, 0, 5, tzinfo=datetime.timezone.utc) + + +def test_DateFormatter_settz(): + time = mdates.date2num(datetime.datetime(2011, 1, 1, 0, 0, + tzinfo=mdates.UTC)) + formatter = mdates.DateFormatter('%Y-%b-%d %H:%M') + # Default UTC + assert formatter(time) == '2011-Jan-01 00:00' + + # Set tzinfo + formatter.set_tzinfo('Pacific/Kiritimati') + assert formatter(time) == '2011-Jan-01 14:00'