Skip to content

Commit 0803389

Browse files
committed
Make YearLocator a subclass of RRuleLocator
1 parent 4723f8a commit 0803389

File tree

1 file changed

+51
-66
lines changed

1 file changed

+51
-66
lines changed

lib/matplotlib/dates.py

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

1157-
def __init__(self, o, tz=None):
1157+
def __init__(self, o, tz=None, interval_multiples=False):
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+
11611167
def __call__(self):
11621168
# if no data have been set, this will tank with a ValueError
11631169
try:
@@ -1168,22 +1174,28 @@ def __call__(self):
11681174
return self.tick_values(dmin, dmax)
11691175

11701176
def tick_values(self, vmin, vmax):
1171-
delta = relativedelta(vmax, vmin)
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
11721181

1173-
# We need to cap at the endpoints of valid datetime
1174-
try:
1175-
start = vmin - delta
1176-
except (ValueError, OverflowError):
1177-
# cap
1178-
start = datetime.datetime(1, 1, 1, 0, 0, 0,
1179-
tzinfo=datetime.timezone.utc)
1182+
else:
1183+
delta = relativedelta(vmax, vmin)
11801184

1181-
try:
1182-
stop = vmax + delta
1183-
except (ValueError, OverflowError):
1184-
# cap
1185-
stop = datetime.datetime(9999, 12, 31, 23, 59, 59,
1186-
tzinfo=datetime.timezone.utc)
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)
11871199

11881200
self.rule.set(dtstart=start, until=stop)
11891201

@@ -1192,6 +1204,23 @@ def tick_values(self, vmin, vmax):
11921204
return date2num([vmin, vmax])
11931205
return self.raise_if_exceeds(date2num(dates))
11941206

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)
1212+
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}
1218+
1219+
start = vmin.replace(**replace)
1220+
stop = vmax.replace(year=ymax)
1221+
1222+
return start, stop
1223+
11951224
def _get_unit(self):
11961225
# docstring inherited
11971226
freq = self.rule._rrule._freq
@@ -1430,17 +1459,16 @@ def get_locator(self, dmin, dmax):
14301459
else:
14311460
interval = 1
14321461

1433-
if (freq == YEARLY) and self.interval_multiples:
1434-
locator = YearLocator(interval, tz=self.tz)
1435-
elif use_rrule_locator[i]:
1462+
if use_rrule_locator[i]:
14361463
_, bymonth, bymonthday, byhour, byminute, bysecond, _ = byranges
14371464
rrule = rrulewrapper(self._freq, interval=interval,
14381465
dtstart=dmin, until=dmax,
14391466
bymonth=bymonth, bymonthday=bymonthday,
14401467
byhour=byhour, byminute=byminute,
14411468
bysecond=bysecond)
14421469

1443-
locator = RRuleLocator(rrule, self.tz)
1470+
locator = RRuleLocator(rrule, self.tz,
1471+
interval_multiples=self.interval_multiples)
14441472
else:
14451473
locator = MicrosecondLocator(interval, tz=self.tz)
14461474
if date2num(dmin) > 70 * 365 and interval < 1000:
@@ -1454,7 +1482,7 @@ def get_locator(self, dmin, dmax):
14541482
return locator
14551483

14561484

1457-
class YearLocator(DateLocator):
1485+
class YearLocator(RRuleLocator):
14581486
"""
14591487
Make ticks on a given day of each year that is a multiple of base.
14601488
@@ -1471,52 +1499,9 @@ def __init__(self, base=1, month=1, day=1, tz=None):
14711499
Mark years that are multiple of base on a given month and day
14721500
(default jan 1).
14731501
"""
1474-
super().__init__(tz)
1475-
self.base = ticker._Edge_integer(base, 0)
1476-
self.replaced = {'month': month,
1477-
'day': day,
1478-
'hour': 0,
1479-
'minute': 0,
1480-
'second': 0,
1481-
}
1482-
if not hasattr(tz, 'localize'):
1483-
# if tz is pytz, we need to do this w/ the localize fcn,
1484-
# otherwise datetime.replace works fine...
1485-
self.replaced['tzinfo'] = tz
1486-
1487-
def __call__(self):
1488-
# if no data have been set, this will tank with a ValueError
1489-
try:
1490-
dmin, dmax = self.viewlim_to_dt()
1491-
except ValueError:
1492-
return []
1493-
1494-
return self.tick_values(dmin, dmax)
1495-
1496-
def tick_values(self, vmin, vmax):
1497-
ymin = self.base.le(vmin.year) * self.base.step
1498-
ymax = self.base.ge(vmax.year) * self.base.step
1499-
1500-
vmin = vmin.replace(year=ymin, **self.replaced)
1501-
if hasattr(self.tz, 'localize'):
1502-
# look after pytz
1503-
if not vmin.tzinfo:
1504-
vmin = self.tz.localize(vmin, is_dst=True)
1505-
1506-
ticks = [vmin]
1507-
1508-
while True:
1509-
dt = ticks[-1]
1510-
if dt.year >= ymax:
1511-
return date2num(ticks)
1512-
year = dt.year + self.base.step
1513-
dt = dt.replace(year=year, **self.replaced)
1514-
if hasattr(self.tz, 'localize'):
1515-
# look after pytz
1516-
if not dt.tzinfo:
1517-
dt = self.tz.localize(dt, is_dst=True)
1518-
1519-
ticks.append(dt)
1502+
rule = rrulewrapper(YEARLY, interval=base, bymonth=month,
1503+
bymonthday=day, **self.hms0d)
1504+
super().__init__(rule, tz, interval_multiples=True)
15201505

15211506

15221507
class MonthLocator(RRuleLocator):

0 commit comments

Comments
 (0)