-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Make YearLocator a subclass of RRuleLocator #19348
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 all commits
0803389
7b59cb2
8c665bf
1a4d6c6
734a454
b369afe
63bfcba
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 |
---|---|---|
@@ -0,0 +1,6 @@ | ||
Removed ``dates.YearLocator.replaced`` attribute | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
`.YearLocator` is now a subclass of `.RRuleLocator`, and the attribute | ||
``YearLocator.replaced`` has been removed. For tick locations that | ||
required modifying this, a custom rrule and `.RRuleLocator` can be used instead. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1168,6 +1168,15 @@ def __call__(self): | |
return self.tick_values(dmin, dmax) | ||
|
||
def tick_values(self, vmin, vmax): | ||
start, stop = self._create_rrule(vmin, vmax) | ||
dates = self.rule.between(start, stop, True) | ||
if len(dates) == 0: | ||
return date2num([vmin, vmax]) | ||
return self.raise_if_exceeds(date2num(dates)) | ||
|
||
def _create_rrule(self, vmin, vmax): | ||
# set appropriate rrule dtstart and until and return | ||
# start and end | ||
delta = relativedelta(vmax, vmin) | ||
|
||
# We need to cap at the endpoints of valid datetime | ||
|
@@ -1187,10 +1196,7 @@ def tick_values(self, vmin, vmax): | |
|
||
self.rule.set(dtstart=start, until=stop) | ||
|
||
dates = self.rule.between(vmin, vmax, True) | ||
if len(dates) == 0: | ||
return date2num([vmin, vmax]) | ||
return self.raise_if_exceeds(date2num(dates)) | ||
return vmin, vmax | ||
|
||
def _get_unit(self): | ||
# docstring inherited | ||
|
@@ -1454,7 +1460,7 @@ def get_locator(self, dmin, dmax): | |
return locator | ||
|
||
|
||
class YearLocator(DateLocator): | ||
class YearLocator(RRuleLocator): | ||
""" | ||
Make ticks on a given day of each year that is a multiple of base. | ||
|
||
|
@@ -1471,52 +1477,28 @@ def __init__(self, base=1, month=1, day=1, tz=None): | |
Mark years that are multiple of base on a given month and day | ||
(default jan 1). | ||
""" | ||
super().__init__(tz) | ||
rule = rrulewrapper(YEARLY, interval=base, bymonth=month, | ||
bymonthday=day, **self.hms0d) | ||
super().__init__(rule, tz) | ||
self.base = ticker._Edge_integer(base, 0) | ||
self.replaced = {'month': month, | ||
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. overall this seems fine. I suspect you need a minor API note that says 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 it worth putting in the a property what warns on get/set? On one hand, most likely no one is using this, on the other hand it is not much work to make sure we do not needlessly break anyone who is trying to access this. |
||
'day': day, | ||
'hour': 0, | ||
'minute': 0, | ||
'second': 0, | ||
} | ||
if not hasattr(tz, 'localize'): | ||
# if tz is pytz, we need to do this w/ the localize fcn, | ||
# otherwise datetime.replace works fine... | ||
self.replaced['tzinfo'] = tz | ||
|
||
def __call__(self): | ||
# if no data have been set, this will tank with a ValueError | ||
try: | ||
dmin, dmax = self.viewlim_to_dt() | ||
except ValueError: | ||
return [] | ||
def _create_rrule(self, vmin, vmax): | ||
# 'start' needs to be a multiple of the interval to create ticks on | ||
# interval multiples when the tick frequency is YEARLY | ||
ymin = max(self.base.le(vmin.year) * self.base.step, 1) | ||
ymax = min(self.base.ge(vmax.year) * self.base.step, 9999) | ||
|
||
return self.tick_values(dmin, dmax) | ||
c = self.rule._construct | ||
replace = {'year': ymin, | ||
'month': c.get('bymonth', 1), | ||
'day': c.get('bymonthday', 1), | ||
'hour': 0, 'minute': 0, 'second': 0} | ||
|
||
def tick_values(self, vmin, vmax): | ||
ymin = self.base.le(vmin.year) * self.base.step | ||
ymax = self.base.ge(vmax.year) * self.base.step | ||
|
||
vmin = vmin.replace(year=ymin, **self.replaced) | ||
if hasattr(self.tz, 'localize'): | ||
# look after pytz | ||
if not vmin.tzinfo: | ||
vmin = self.tz.localize(vmin, is_dst=True) | ||
|
||
ticks = [vmin] | ||
|
||
while True: | ||
dt = ticks[-1] | ||
if dt.year >= ymax: | ||
return date2num(ticks) | ||
year = dt.year + self.base.step | ||
dt = dt.replace(year=year, **self.replaced) | ||
if hasattr(self.tz, 'localize'): | ||
# look after pytz | ||
if not dt.tzinfo: | ||
dt = self.tz.localize(dt, is_dst=True) | ||
|
||
ticks.append(dt) | ||
start = vmin.replace(**replace) | ||
stop = start.replace(year=ymax) | ||
self.rule.set(dtstart=start, until=stop) | ||
|
||
return start, stop | ||
|
||
|
||
class MonthLocator(RRuleLocator): | ||
|
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.
Yes can you add a test for this. Otherwise I think this is good?
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.
@jklymak done