Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cc6854c

Browse files
authored
Merge pull request #22196 from oscargus/datestest
ENH: `dates` classes and functions support `tz` both as string and `tzinfo`
2 parents da25853 + 1158778 commit cc6854c

File tree

3 files changed

+161
-52
lines changed

3 files changed

+161
-52
lines changed

lib/matplotlib/dates.py

Lines changed: 58 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
22
Matplotlib provides sophisticated date plotting capabilities, standing on the
3-
shoulders of python :mod:`datetime` and the add-on module :mod:`dateutil`.
3+
shoulders of python :mod:`datetime` and the add-on module dateutil_.
44
55
By default, Matplotlib uses the units machinery described in
66
`~matplotlib.units` to convert `datetime.datetime`, and `numpy.datetime64`
@@ -83,10 +83,11 @@
8383
Out[1]: 732401
8484
8585
All the Matplotlib date converters, tickers and formatters are timezone aware.
86-
If no explicit timezone is provided, :rc:`timezone` is assumed. If you want to
87-
use a custom time zone, pass a `datetime.tzinfo` instance with the tz keyword
88-
argument to `num2date`, `.Axis.axis_date`, and any custom date tickers or
89-
locators you create.
86+
If no explicit timezone is provided, :rc:`timezone` is assumed, provided as a
87+
string. If you want to use a different timezone, pass the *tz* keyword
88+
argument of `num2date` to any date tickers or locators you create. This can
89+
be either a `datetime.tzinfo` instance or a string with the timezone name that
90+
can be parsed by `~dateutil.tz.gettz`.
9091
9192
A wide range of specific and general purpose date tick locators and
9293
formatters are provided in this module. See
@@ -141,10 +142,10 @@
141142
142143
* `YearLocator`: Locate years that are multiples of base.
143144
144-
* `RRuleLocator`: Locate using a `matplotlib.dates.rrulewrapper`.
145-
`.rrulewrapper` is a simple wrapper around dateutil_'s `dateutil.rrule` which
146-
allow almost arbitrary date tick specifications. See :doc:`rrule example
147-
</gallery/ticks/date_demo_rrule>`.
145+
* `RRuleLocator`: Locate using a ``matplotlib.dates.rrulewrapper``.
146+
``rrulewrapper`` is a simple wrapper around dateutil_'s `dateutil.rrule`
147+
which allow almost arbitrary date tick specifications.
148+
See :doc:`rrule example </gallery/ticks/date_demo_rrule>`.
148149
149150
* `AutoDateLocator`: On autoscale, this class picks the best `DateLocator`
150151
(e.g., `RRuleLocator`) to set the view limits and the tick locations. If
@@ -202,12 +203,24 @@
202203
UTC = datetime.timezone.utc
203204

204205

205-
def _get_rc_timezone():
206-
"""Retrieve the preferred timezone from the rcParams dictionary."""
207-
s = mpl.rcParams['timezone']
208-
if s == 'UTC':
209-
return UTC
210-
return dateutil.tz.gettz(s)
206+
def _get_tzinfo(tz=None):
207+
"""
208+
Generate tzinfo from a string or return tzinfo. If None,
209+
retrieve the preferred timezone from the rcParams dictionary.
210+
"""
211+
if tz is None:
212+
tz = mpl.rcParams['timezone']
213+
if tz == 'UTC':
214+
return UTC
215+
if isinstance(tz, str):
216+
tzinfo = dateutil.tz.gettz(tz)
217+
if tzinfo is None:
218+
raise ValueError(f"{tz} is not a valid timezone as parsed by"
219+
" dateutil.tz.gettz.")
220+
return tzinfo
221+
if isinstance(tz, datetime.tzinfo):
222+
return tz
223+
raise TypeError("tz must be string or tzinfo subclass.")
211224

212225

213226
"""
@@ -341,8 +354,7 @@ def _from_ordinalf(x, tz=None):
341354
:rc:`timezone`.
342355
"""
343356

344-
if tz is None:
345-
tz = _get_rc_timezone()
357+
tz = _get_tzinfo(tz)
346358

347359
dt = (np.datetime64(get_epoch()) +
348360
np.timedelta64(int(np.round(x * MUSECONDS_PER_DAY)), 'us'))
@@ -395,7 +407,9 @@ def datestr2num(d, default=None):
395407
return date2num(dt)
396408
else:
397409
if default is not None:
398-
d = [dateutil.parser.parse(s, default=default) for s in d]
410+
d = [date2num(dateutil.parser.parse(s, default=default))
411+
for s in d]
412+
return np.asarray(d)
399413
d = np.asarray(d)
400414
if not d.size:
401415
return d
@@ -506,8 +520,8 @@ def num2date(x, tz=None):
506520
Number of days (fraction part represents hours, minutes, seconds)
507521
since the epoch. See `.get_epoch` for the
508522
epoch, which can be changed by :rc:`date.epoch` or `.set_epoch`.
509-
tz : str, default: :rc:`timezone`
510-
Timezone of *x*.
523+
tz : str or `~datetime.tzinfo`, default: :rc:`timezone`
524+
Timezone of *x*. If a string, *tz* is passed to `dateutil.tz`.
511525
512526
Returns
513527
-------
@@ -523,8 +537,7 @@ def num2date(x, tz=None):
523537
Gregorian calendar is assumed; this is not universal practice.
524538
For details, see the module docstring.
525539
"""
526-
if tz is None:
527-
tz = _get_rc_timezone()
540+
tz = _get_tzinfo(tz)
528541
return _from_ordinalf_np_vectorized(x, tz).tolist()
529542

530543

@@ -619,16 +632,14 @@ def __init__(self, fmt, tz=None, *, usetex=None):
619632
----------
620633
fmt : str
621634
`~datetime.datetime.strftime` format string
622-
tz : `datetime.tzinfo`, default: :rc:`timezone`
635+
tz : str or `~datetime.tzinfo`, default: :rc:`timezone`
623636
Ticks timezone.
624637
usetex : bool, default: :rc:`text.usetex`
625638
To enable/disable the use of TeX's math mode for rendering the
626639
results of the formatter.
627640
"""
628-
if tz is None:
629-
tz = _get_rc_timezone()
641+
self.tz = _get_tzinfo(tz)
630642
self.fmt = fmt
631-
self.tz = tz
632643
self._usetex = (usetex if usetex is not None else
633644
mpl.rcParams['text.usetex'])
634645

@@ -637,7 +648,7 @@ def __call__(self, x, pos=0):
637648
return _wrap_in_tex(result) if self._usetex else result
638649

639650
def set_tzinfo(self, tz):
640-
self.tz = tz
651+
self.tz = _get_tzinfo(tz)
641652

642653

643654
class ConciseDateFormatter(ticker.Formatter):
@@ -654,8 +665,8 @@ class ConciseDateFormatter(ticker.Formatter):
654665
locator : `.ticker.Locator`
655666
Locator that this axis is using.
656667
657-
tz : str, optional
658-
Passed to `.dates.date2num`.
668+
tz : str or `~datetime.tzinfo`, default: :rc:`timezone`
669+
Passed to `.dates.num2date`.
659670
660671
formats : list of 6 strings, optional
661672
Format strings for 6 levels of tick labelling: mostly years,
@@ -755,7 +766,7 @@ def __init__(self, locator, tz=None, formats=None, offset_formats=None,
755766

756767
if offset_formats:
757768
if len(offset_formats) != 6:
758-
raise ValueError('offsetfmts argument must be a list of '
769+
raise ValueError('offset_formats argument must be a list of '
759770
'6 format strings (or None)')
760771
self.offset_formats = offset_formats
761772
else:
@@ -925,8 +936,8 @@ def __init__(self, locator, tz=None, defaultfmt='%Y-%m-%d', *,
925936
locator : `.ticker.Locator`
926937
Locator that this axis is using.
927938
928-
tz : str, optional
929-
Passed to `.dates.date2num`.
939+
tz : str or `~datetime.tzinfo`, optional
940+
Passed to `.dates.num2date`.
930941
931942
defaultfmt : str
932943
The default format to use if none of the values in ``self.scaled``
@@ -998,7 +1009,7 @@ def set(self, **kwargs):
9981009
def _update_rrule(self, **kwargs):
9991010
tzinfo = self._base_tzinfo
10001011

1001-
# rrule does not play nicely with time zones - especially pytz time
1012+
# rrule does not play nicely with timezones - especially pytz time
10021013
# zones, it's best to use naive zones and attach timezones once the
10031014
# datetimes are returned
10041015
if 'dtstart' in kwargs:
@@ -1101,17 +1112,15 @@ def __init__(self, tz=None):
11011112
"""
11021113
Parameters
11031114
----------
1104-
tz : `datetime.tzinfo`
1115+
tz : str or `~datetime.tzinfo`, default: :rc:`timezone`
11051116
"""
1106-
if tz is None:
1107-
tz = _get_rc_timezone()
1108-
self.tz = tz
1117+
self.tz = _get_tzinfo(tz)
11091118

11101119
def set_tzinfo(self, tz):
11111120
"""
1112-
Set time zone info.
1121+
Set timezone info. str or `~datetime.tzinfo`.
11131122
"""
1114-
self.tz = tz
1123+
self.tz = _get_tzinfo(tz)
11151124

11161125
def datalim_to_dt(self):
11171126
"""Convert axis data interval to datetime objects."""
@@ -1281,7 +1290,7 @@ def __init__(self, tz=None, minticks=5, maxticks=None,
12811290
"""
12821291
Parameters
12831292
----------
1284-
tz : `datetime.tzinfo`
1293+
tz : str or `~datetime.tzinfo`, default: :rc:`timezone`
12851294
Ticks timezone.
12861295
minticks : int
12871296
The minimum number of ticks desired; controls whether ticks occur
@@ -1301,7 +1310,7 @@ def __init__(self, tz=None, minticks=5, maxticks=None,
13011310
the ticks to be at hours 0, 6, 12, 18 when hourly ticking is done
13021311
at 6 hour intervals.
13031312
"""
1304-
super().__init__(tz)
1313+
super().__init__(tz=tz)
13051314
self._freq = YEARLY
13061315
self._freqs = [YEARLY, MONTHLY, DAILY, HOURLY, MINUTELY,
13071316
SECONDLY, MICROSECONDLY]
@@ -1455,7 +1464,7 @@ def get_locator(self, dmin, dmax):
14551464
byhour=byhour, byminute=byminute,
14561465
bysecond=bysecond)
14571466

1458-
locator = RRuleLocator(rrule, self.tz)
1467+
locator = RRuleLocator(rrule, tz=self.tz)
14591468
else:
14601469
locator = MicrosecondLocator(interval, tz=self.tz)
14611470
if date2num(dmin) > 70 * 365 and interval < 1000:
@@ -1488,7 +1497,7 @@ def __init__(self, base=1, month=1, day=1, tz=None):
14881497
"""
14891498
rule = rrulewrapper(YEARLY, interval=base, bymonth=month,
14901499
bymonthday=day, **self.hms0d)
1491-
super().__init__(rule, tz)
1500+
super().__init__(rule, tz=tz)
14921501
self.base = ticker._Edge_integer(base, 0)
14931502

14941503
def _create_rrule(self, vmin, vmax):
@@ -1532,7 +1541,7 @@ def __init__(self, bymonth=None, bymonthday=1, interval=1, tz=None):
15321541

15331542
rule = rrulewrapper(MONTHLY, bymonth=bymonth, bymonthday=bymonthday,
15341543
interval=interval, **self.hms0d)
1535-
super().__init__(rule, tz)
1544+
super().__init__(rule, tz=tz)
15361545

15371546

15381547
class WeekdayLocator(RRuleLocator):
@@ -1560,7 +1569,7 @@ def __init__(self, byweekday=1, interval=1, tz=None):
15601569

15611570
rule = rrulewrapper(DAILY, byweekday=byweekday,
15621571
interval=interval, **self.hms0d)
1563-
super().__init__(rule, tz)
1572+
super().__init__(rule, tz=tz)
15641573

15651574

15661575
class DayLocator(RRuleLocator):
@@ -1586,7 +1595,7 @@ def __init__(self, bymonthday=None, interval=1, tz=None):
15861595

15871596
rule = rrulewrapper(DAILY, bymonthday=bymonthday,
15881597
interval=interval, **self.hms0d)
1589-
super().__init__(rule, tz)
1598+
super().__init__(rule, tz=tz)
15901599

15911600

15921601
class HourLocator(RRuleLocator):
@@ -1606,7 +1615,7 @@ def __init__(self, byhour=None, interval=1, tz=None):
16061615

16071616
rule = rrulewrapper(HOURLY, byhour=byhour, interval=interval,
16081617
byminute=0, bysecond=0)
1609-
super().__init__(rule, tz)
1618+
super().__init__(rule, tz=tz)
16101619

16111620

16121621
class MinuteLocator(RRuleLocator):
@@ -1626,7 +1635,7 @@ def __init__(self, byminute=None, interval=1, tz=None):
16261635

16271636
rule = rrulewrapper(MINUTELY, byminute=byminute, interval=interval,
16281637
bysecond=0)
1629-
super().__init__(rule, tz)
1638+
super().__init__(rule, tz=tz)
16301639

16311640

16321641
class SecondLocator(RRuleLocator):
@@ -1646,7 +1655,7 @@ def __init__(self, bysecond=None, interval=1, tz=None):
16461655
bysecond = range(60)
16471656

16481657
rule = rrulewrapper(SECONDLY, bysecond=bysecond, interval=interval)
1649-
super().__init__(rule, tz)
1658+
super().__init__(rule, tz=tz)
16501659

16511660

16521661
class MicrosecondLocator(DateLocator):

0 commit comments

Comments
 (0)