Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

FIX: properly set tz for YearLocator#12678

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
tacaswell merged 2 commits intomatplotlib:masterfromjklymak:fix-yearlocator-tz
Jan 28, 2019

Conversation

jklymak
Copy link
Member

PR Summary

dates.YearLocator was not always being called with the timezone info. Now it is...

Closes#12675:

importmatplotlib.pyplotaspltimportpytzfromdatetimeimportdatetime,timedelta,timezonex= [datetime(2010,1,1).astimezone(pytz.timezone('America/New_York'))+timedelta(i)foriinrange(2000)]plt.plot(x, [0]*len(x));plt.show()

Now has the first tick at 2010 instead of 2009....

PR Checklist

  • Has Pytest style unit tests
  • Code isFlake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@QuLogic
Copy link
Member

Add/modify a test?

@jklymak
Copy link
MemberAuthor

Yeah, good point - we should have caught this in testing before.

@jklymak
Copy link
MemberAuthor

test added...

@jklymak
Copy link
MemberAuthor

jklymak commentedOct 30, 2018
edited
Loading

I have a bad feeling that this solution will fail on 3.5 for some datetime objects. As of 3.6 you can do datetime.astimezone, but before 3.6 you cannot if there is no timezone info. So I'm re-milestoning this fix for 3.1. There may be a way to work around this for 3.0.x, but I'm not clear what it is.

@jklymakjklymak added this to thev3.1 milestoneOct 30, 2018
@jklymakjklymak modified the milestones:v3.1,v3.0.xOct 31, 2018
@jklymak
Copy link
MemberAuthor

OK, this will fail for py 3.5 if the user supplies a naive datetime (no timezone info) and they are using theYearLocator, but will work otherwise. Added a slightly less mysterious error message for that case.

@jklymak
Copy link
MemberAuthor

test_constrainedlayout.py::test_colorbar_location seems to be a bit of a flaky test. its a bit complicated, involves a solver, and sometimes seems to fail.

timhoffm
timhoffm previously approved these changesNov 1, 2018
try:
ticks = [vmin.astimezone(self.tz)]
except ValueError:
raise ValueError('naive datetime objects cannot be used '
Copy link
Member

Choose a reason for hiding this comment

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

This may be a good place to use theraise ... from e functionality of python3 (https://www.python.org/dev/peps/pep-3134/)

try:ticks= [vmin.astimezone(self.tz)]exceptValueErrorasE:raiseValueError()fromE

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

OH, Cool, didn't know about that. I'll do soon.

}
# Note that tzinfo does not work with pytz timezones, and
Copy link
Member

Choose a reason for hiding this comment

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

This is moot if we drop py3.5?

Copy link
Member

Choose a reason for hiding this comment

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

ah, this is targetted at 3.0.x so have to support 3.5 still.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Yep... Could milestone 3.1 if you prefer.

@tacaswell
Copy link
Member

attn@pganssle for advise on how to deal with timezones cleanly....

@tacaswell
Copy link
Member

Requiring timezone aware datetimes to use the YearLocator seems like a pretty big API change? Am I misunderstanding this?

@jklymak
Copy link
MemberAuthor

No, this just fixes the YearLocator - it wasn't getting the timezone information passed to it properly, so 1 Jan 00:00 sometimes looked like 31 Dec 23:00, and the year was formatted as the previous year. This shouldn't affect the other Locators or Formatters in the autodateLocator (I'd hope we'd fail a few tests if it did!).

for t_delta, expected in results:
d2 = d1 + t_delta
locator = _create_auto_date_locator(d1, d2)
assert list(map(str, mdates.num2date(locator()))) == expected


@pytest.mark.pytz
@pytest.mark.skipif(not __has_pytz(), reason="Requires pytz")
Copy link
Member

Choose a reason for hiding this comment

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

Why are you usingpytz for this? Tests withpytz are just to ensurematplotlib iscompatible withpytz, but this is a general functionality test. It's better to usedateutil.tz, which is not conditional onpytz installation.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

The original bug report was pytz, and pytz doesn't work if the code is not as written in the PR. i.e. I needed to test pytz to get the PR to work, so it makes sense to test it here...

Copy link
Member

Choose a reason for hiding this comment

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

Is it not possible to reproduce the bug withdateutil.tz? Just because the reporter usedpytz doesn't mean you can't create an MWE without that dependency.

Copy link
Member

Choose a reason for hiding this comment

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

Just checked, the original bug is also present when you usedateutil.tz, with the original MWE now (more accurately in this case):

importmatplotlib.pyplotaspltfromdateutilimporttzfromdatetimeimportdatetime,timedeltax= [datetime(2010,1,1,tzinfo=tz.gettz('America/New_York'))+timedelta(i)foriinrange(2000)]plt.plot(x, [0]*len(x));

}
# Note that tzinfo does not work with pytz timezones, and
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

@pganssle : the way that tz info was put in before was to dovmin.replace(year=ymin, **self.replaced), butpytz doesn't work properly (at all) withreplace. So we need a test with pytz to test that this code works.

To be honest, we roundtrip most dates (datetime-> ordinal -> datetime) so the fact that pytz doesn't do the right thing w/tzinfo isn't that big a deal practically, but it would be nice if it worked.

I guess we could add another test that doesn't use pytz, but I think pytz should be tested on this code path.

Copy link
Member

Choose a reason for hiding this comment

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

replace(tzinfo=tz) andastimezone(tz) do two very different things.

If you think of time zones as units for datetimes,replace(tzinfo=tz) is like getting a value that says "1 ft", scratching off the "ft" and adding "m" to get "1m".astimezone(tz) is like getting something that says 1ft and converting it to meters to get0.3m.

Generally speaking,replace is used if you know that the local portion of the datetime is correct and want to say what time zone it's in.astimezone is used if you know that the absolute time represented by the datetime is correct and you want to convert it to a different local time.

In Python >=3.6,astimezone can be used on naive datetimes, but what that does is that it interprets the naive datetime aslocal time, and converts that to the time zone of interest. What that means is that if you are in Tokyo and give this a naive datetime likedatetime(2018, 1, 1) and haveYearLocator(tz=tz.gettz("America/New_York"), that's going to get converted to2017-12-31 10:00:00-05:00, which is probably not what you wanted.

Copy link
Member

@pgansslepganssleNov 4, 2018
edited
Loading

Choose a reason for hiding this comment

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

pytz doesn't work properly (at all) with replace. So we need a test with pytz to test that this code works.

Also, I'll note that I'mwell aware of the limitations ofpytz.

I think probably it's best to use two tests, one withpytz (skipped ifpytz is not present) and one withdateutil. If you intend for the interface to look the same, you can likely parametrize the test function with a conditional skip onpytz.

@pganssle
Copy link
Member

I haven't totally grokked how your code works, but it's worth noting that Isolved a similar problem in the RRULE locator some time back, soRRuleLocatorshould be able to handle time zones correctly. The way it works inrrulewrapper is that if an RRULE has an attached time zone, that time zone is stripped off, the RRULE generates dates, then the time zone is re-attached before the datetime object is emitted.

I think you probably want to do something similar forYearlyLocator.

Copy link
Member

@timhoffmtimhoffm left a comment
edited
Loading

Choose a reason for hiding this comment

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

I would create acbook.localize_tz() helper to make this more readable. Something like (untested):

def localize_tz(date, tz):    if hasattr(tz, 'localize'):        return tz.localize(date, is_dst=True)    else:        return date.replace(tzinfo=tz)

Or alteratively, acbook.datetime_replace() that can handle the tz.

@jklymakjklymakforce-pushed thefix-yearlocator-tz branch 2 times, most recently from49b5d55 to478ea3dCompareNovember 18, 2018 18:13
@jklymak
Copy link
MemberAuthor

I didn't make acbook function because I can't see this being applicable outside ofdates.py. But otherwise, thanks for the suggestion

timhoffm reacted with thumbs up emoji

@timhoffm
Copy link
Member

The solution with a private method is equally fine.

Copy link
Member

@timhoffmtimhoffm left a comment

Choose a reason for hiding this comment

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

Looking at the code, you always do a replace followed by_localize_tz. In that context a_date_replace migh even be better. Less clutter in the calling code and for non-pytz even a bit faster since you don't replace twice.

def _date_replace(date, tzinfo=None, **kwargs):    """    Drop-in replacement for datetime.replace() with support for pytz timezones.    """    if hasattr(tzinfo, 'localize'):        date = date.replace(tzinfo=None, **kwargs)        return tzinfo.localize(date, is_dst=True)    return date.replace(tzinfo=tzinfo, **kwargs)

But I won't hold up if you want to stay with_localize_tz.

@@ -1409,13 +1419,22 @@ def tick_values(self, vmin, vmax):
ymin = self.base.le(vmin.year) * self.base.step
ymax = self.base.ge(vmax.year) * self.base.step

ticks = [vmin.replace(year=ymin, **self.replaced)]
vmin = vmin.replace(year=ymin, **self.replaced)
print('vmin before', vmin)
Copy link
Member

Choose a reason for hiding this comment

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

Debug print?

Suggested change
print('vmin before', vmin)

jklymak reacted with thumbs up emoji
vmin = vmin.replace(year=ymin, **self.replaced)
print('vmin before', vmin)
vmin = _localize_tz(vmin, self.tz)
print('vmin after', vmin)
Copy link
Member

Choose a reason for hiding this comment

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

Debug print?

Suggested change
print('vmin after', vmin)

jklymak reacted with thumbs up emoji
@jklymak
Copy link
MemberAuthor

Fair enough, that's an easy refactor....

@@ -1426,7 +1441,9 @@ def autoscale(self):
ymin = self.base.le(dmin.year)
ymax = self.base.ge(dmax.year)
vmin = dmin.replace(year=ymin, **self.replaced)
vmin = vmin.astimezone(self.tz)
Copy link
Member

Choose a reason for hiding this comment

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

Is this acutally different from he above cases?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Ummm, hmmm, not sure this was properly tested....

@jklymakjklymakforce-pushed thefix-yearlocator-tz branch 2 times, most recently from0478bc1 tob244b3dCompareNovember 19, 2018 03:17
Copy link
Member

@pgansslepganssle left a comment

Choose a reason for hiding this comment

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

I think we worked everything out on this one, yeah? I think this can be merged.

@jklymak
Copy link
MemberAuthor

Rebased. Note two reviews...

@timhoffm
Copy link
Member

The other reviewer does not have write access. Do we count this as a full review?

@jklymak
Copy link
MemberAuthor

No not really. Just hoping that someone else would take a quick look on the strength of the existing reviews and merge.

FIX: add check for py3.5 to fail if naive datetimeTST: make pytz yearlylocator test
@tacaswelltacaswell merged commit00d71ce intomatplotlib:masterJan 28, 2019
@lumberbot-app
Copy link

Owee, I'm MrMeeseeks, Look at me.

There seem to be a conflict, please backport manually. Here are approximate instructions:

  1. Checkout backport branch and update it.
$ git checkout v3.0.x$ git pull
  1. Cherry pick the first parent branch of the this PR on top of the older branch:
$ git cherry-pick -m1 00d71cebb3f3e1eed5774b8942ebc9607961a61e
  1. You will likely have some merge/cherry-pick conflict here, fix them and commit:
$ git commit -am 'Backport PR #12678: FIX: properly set tz for YearLocator'
  1. Push to a named branch :
git push YOURFORK v3.0.x:auto-backport-of-pr-12678-on-v3.0.x
  1. Create a PR against branch v3.0.x, I would have named this PR:

"Backport PR#12678 on branch v3.0.x"

And apply the correct labels and milestones.

Congratulation you did some good work ! Hopefully your backport PR will be tested by the continuous integration and merged soon!

If these instruction are inaccurate, feel free tosuggest an improvement.

jklymak pushed a commit to jklymak/matplotlib that referenced this pull requestJan 28, 2019
jklymak added a commit to jklymak/matplotlib that referenced this pull requestJan 28, 2019
jklymak added a commit to jklymak/matplotlib that referenced this pull requestJan 28, 2019
jklymak pushed a commit to jklymak/matplotlib that referenced this pull requestJan 28, 2019
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@tacaswelltacaswelltacaswell left review comments

@pgansslepgansslepganssle approved these changes

@timhoffmtimhoffmtimhoffm approved these changes

Assignees
No one assigned
Projects
None yet
Milestone
v3.0.3
Development

Successfully merging this pull request may close these issues.

Off-by-one bug in annual axis labels when localized time crosses year boundary
5 participants
@jklymak@QuLogic@tacaswell@pganssle@timhoffm

[8]ページ先頭

©2009-2025 Movatter.jp