Skip to content

Commit 1a4d6c6

Browse files
committed
Improvements
1 parent 8c665bf commit 1a4d6c6

File tree

2 files changed

+51
-53
lines changed

2 files changed

+51
-53
lines changed
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
``dates.YearLocator`` attribute
2-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1+
Removed ``dates.YearLocator.replaced`` attribute
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33

4-
The attribute ``YearLocator.replaced`` has been removed. For tick locations which
4+
`.YearLocator` is now a subclass of `RRuleLocator`, and the attribute
5+
``YearLocator.replaced`` has been removed. For tick locations that
56
required modifying this, a custom rrule and ``RRuleLocator`` can be used instead.

lib/matplotlib/dates.py

Lines changed: 47 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,16 +1154,10 @@ def nonsingular(self, vmin, vmax):
11541154
class RRuleLocator(DateLocator):
11551155
# use the dateutil rrule instance
11561156

1157-
def __init__(self, o, tz=None, interval_multiples=False):
1157+
def __init__(self, o, tz=None):
11581158
super().__init__(tz)
11591159
self.rule = o
11601160

1161-
# rrule cannot create interval multiples for YEARLY, this
1162-
# needs to be handled separately
1163-
self.mult_base = None
1164-
if interval_multiples and o._rrule._freq == YEARLY:
1165-
self.mult_base = ticker._Edge_integer(self._get_interval(), 0)
1166-
11671161
def __call__(self):
11681162
# if no data have been set, this will tank with a ValueError
11691163
try:
@@ -1174,52 +1168,35 @@ def __call__(self):
11741168
return self.tick_values(dmin, dmax)
11751169

11761170
def tick_values(self, vmin, vmax):
1177-
if self.mult_base:
1178-
# start and stop need to be multiples of the interval
1179-
start, stop = self._get_intmult_limits(vmin, vmax)
1180-
vmin, vmax = start, stop
1181-
1182-
else:
1183-
delta = relativedelta(vmax, vmin)
1184-
1185-
# We need to cap at the endpoints of valid datetime
1186-
try:
1187-
start = vmin - delta
1188-
except (ValueError, OverflowError):
1189-
# cap
1190-
start = datetime.datetime(1, 1, 1, 0, 0, 0,
1191-
tzinfo=datetime.timezone.utc)
1192-
1193-
try:
1194-
stop = vmax + delta
1195-
except (ValueError, OverflowError):
1196-
# cap
1197-
stop = datetime.datetime(9999, 12, 31, 23, 59, 59,
1198-
tzinfo=datetime.timezone.utc)
1199-
1200-
self.rule.set(dtstart=start, until=stop)
1201-
1202-
dates = self.rule.between(vmin, vmax, True)
1171+
start, stop = self._create_rrule(vmin, vmax)
1172+
dates = self.rule.between(start, stop, True)
12031173
if len(dates) == 0:
12041174
return date2num([vmin, vmax])
12051175
return self.raise_if_exceeds(date2num(dates))
12061176

1207-
def _get_intmult_limits(self, vmin, vmax):
1208-
# used for interval multiples when freq == YEARLY; make start and stop
1209-
# multiples of the interval but cap at the endpoints of valid datetime
1210-
ymin = max(self.mult_base.le(vmin.year) * self.mult_base.step, 1)
1211-
ymax = min(self.mult_base.ge(vmax.year) * self.mult_base.step, 9999)
1177+
def _create_rrule(self, vmin, vmax):
1178+
# set appropriate rrule dtstart and until and return
1179+
# start and end
1180+
delta = relativedelta(vmax, vmin)
12121181

1213-
c = self.rule._construct
1214-
replace = {'year': ymin,
1215-
'month': getattr(c, 'bymonth', 1),
1216-
'day': getattr(c, 'bymonthday', 1),
1217-
'hour': 0, 'minute': 0, 'second': 0}
1182+
# We need to cap at the endpoints of valid datetime
1183+
try:
1184+
start = vmin - delta
1185+
except (ValueError, OverflowError):
1186+
# cap
1187+
start = datetime.datetime(1, 1, 1, 0, 0, 0,
1188+
tzinfo=datetime.timezone.utc)
12181189

1219-
start = vmin.replace(**replace)
1220-
stop = vmax.replace(year=ymax)
1190+
try:
1191+
stop = vmax + delta
1192+
except (ValueError, OverflowError):
1193+
# cap
1194+
stop = datetime.datetime(9999, 12, 31, 23, 59, 59,
1195+
tzinfo=datetime.timezone.utc)
12211196

1222-
return start, stop
1197+
self.rule.set(dtstart=start, until=stop)
1198+
1199+
return vmin, vmax
12231200

12241201
def _get_unit(self):
12251202
# docstring inherited
@@ -1459,16 +1436,17 @@ def get_locator(self, dmin, dmax):
14591436
else:
14601437
interval = 1
14611438

1462-
if use_rrule_locator[i]:
1439+
if (freq == YEARLY) and self.interval_multiples:
1440+
locator = YearLocator(interval, tz=self.tz)
1441+
elif use_rrule_locator[i]:
14631442
_, bymonth, bymonthday, byhour, byminute, bysecond, _ = byranges
14641443
rrule = rrulewrapper(self._freq, interval=interval,
14651444
dtstart=dmin, until=dmax,
14661445
bymonth=bymonth, bymonthday=bymonthday,
14671446
byhour=byhour, byminute=byminute,
14681447
bysecond=bysecond)
14691448

1470-
locator = RRuleLocator(rrule, self.tz,
1471-
interval_multiples=self.interval_multiples)
1449+
locator = RRuleLocator(rrule, self.tz)
14721450
else:
14731451
locator = MicrosecondLocator(interval, tz=self.tz)
14741452
if date2num(dmin) > 70 * 365 and interval < 1000:
@@ -1501,7 +1479,26 @@ def __init__(self, base=1, month=1, day=1, tz=None):
15011479
"""
15021480
rule = rrulewrapper(YEARLY, interval=base, bymonth=month,
15031481
bymonthday=day, **self.hms0d)
1504-
super().__init__(rule, tz, interval_multiples=True)
1482+
super().__init__(rule, tz)
1483+
self.base = ticker._Edge_integer(base, 0)
1484+
1485+
def _create_rrule(self, vmin, vmax):
1486+
# 'start' needs to be a multiple of the interval to create ticks on
1487+
# interval multiples when the tick frequency is YEARLY
1488+
ymin = max(self.base.le(vmin.year) * self.base.step, 1)
1489+
ymax = min(self.base.ge(vmax.year) * self.base.step, 9999)
1490+
1491+
c = self.rule._construct
1492+
replace = {'year': ymin,
1493+
'month': c.get('bymonth', 1),
1494+
'day': c.get('bymonthday', 1),
1495+
'hour': 0, 'minute': 0, 'second': 0}
1496+
1497+
start = vmin.replace(**replace)
1498+
stop = start.replace(year=ymax)
1499+
self.rule.set(dtstart=start, until=stop)
1500+
1501+
return start, stop
15051502

15061503

15071504
class MonthLocator(RRuleLocator):

0 commit comments

Comments
 (0)