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

Skip to content

Commit d25a321

Browse files
committed
Doc fixes
1 parent a765bb9 commit d25a321

File tree

5 files changed

+54
-46
lines changed

5 files changed

+54
-46
lines changed

doc/users/next_whats_new/2020-04-09-datetime-epoch-change.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
Dates now use a modern epoch
22
----------------------------
33

4-
Matplotlib must convert dates to floating point numbers for plotting, which
5-
is done as floating point days since an epoch. Previously, `.dates.date2num`
6-
used an epoch of ``0000-12-31T00:00:00``, which aside from meaning
7-
``0001-01-01`` was converted to 1.0, also meant that a modern date was not
8-
able to preserve microseconds because 2000 years times the 2^(-52) resolution
9-
of a 64-bit float gives 14 microseconds.
4+
Matplotlib converts dates to days since an epoch using `.dates.date2num` (via
5+
`matplotlib.units`). Previously, an epoch of ``0000-12-31T00:00:00`` was used
6+
so that ``0001-01-01`` was converted to 1.0. An epoch so distant in the
7+
past meant that a modern date was not able to preserve microseconds because
8+
2000 years times the 2^(-52) resolution of a 64-bit float gives 14
9+
microseconds.
1010

1111
Here we change the default epoch to the more reasonable UNIX default of
1212
``1970-01-01T00:00:00`` which for a modern date has 0.35 microsecond

examples/ticks_and_spines/date_precision_and_epochs.py

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@
2525
import matplotlib.pyplot as plt
2626
import matplotlib.dates as mdates
2727

28+
29+
def _reset_epoch_for_tutorial():
30+
"""
31+
Users (and downstream libraries) should not use the private method of
32+
resetting the epoch.
33+
"""
34+
mdates._reset_epoch_test_example()
35+
36+
2837
#############################################################################
2938
# Datetime
3039
# --------
@@ -36,7 +45,7 @@
3645
old_epoch = '0000-12-31T00:00:00'
3746
new_epoch = '1970-01-01T00:00:00'
3847

39-
mdates._reset_epoch() # Don't do this. Just being done for this tutorial.
48+
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
4049
mdates.set_epoch(old_epoch) # old epoch (pre MPL 3.3)
4150

4251
date1 = datetime.datetime(2000, 1, 1, 0, 10, 0, 12,
@@ -59,27 +68,20 @@
5968

6069
#############################################################################
6170
# If a user wants to use modern dates at microsecond precision, they
62-
# can change the epoch. However, note that calling `.set_epoch` yields an
63-
# error here. That is because mixing epochs in a script is confusing, so
64-
# we attach a sentinel that doesn't let it be changed after any dates
65-
# code has been run. In order to run `~.set_epoch` it must be called before
66-
# any date code has been executed (i.e. near the top of a script).
71+
# can change the epoch using `~.set_epoch`. However, the epoch has to be
72+
# set before any date operations to prevent confusion between different
73+
# epochs. Trying to change the epoch later will raise a `RuntimeError`.
6774

6875
try:
6976
mdates.set_epoch(new_epoch) # this is the new MPL 3.3 default.
7077
except RuntimeError as e:
7178
print('RuntimeError:', str(e))
7279

7380
#############################################################################
74-
# For this tutorial, we reset the sentinel using a private method.
75-
#
76-
# .. warning::
77-
#
78-
# Users (and downstream libraries) should avoid using the private
79-
# method of resetting sentinel.
80-
#
81+
# For this tutorial, we reset the sentinel using a private method, but users
82+
# should just set the epoch once, if at all.
8183

82-
mdates._reset_epoch() # Don't do this. Just being done for this tutorial.
84+
_reset_epoch_for_tutorial() # Just being done for this tutorial.
8385
mdates.set_epoch(new_epoch)
8486

8587
date1 = datetime.datetime(2020, 1, 1, 0, 10, 0, 12,
@@ -98,7 +100,7 @@
98100
# only converted back to datetime objects, which have microsecond resolution,
99101
# and years that only span 0000 to 9999.
100102

101-
mdates._reset_epoch() # Don't do this. Just being done for this tutorial.
103+
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
102104
mdates.set_epoch(new_epoch)
103105

104106
date1 = np.datetime64('2000-01-01T00:10:00.000012')
@@ -114,7 +116,7 @@
114116
# This all of course has an effect on plotting. With the old default epoch
115117
# the times were rounded, leading to jumps in the data:
116118

117-
mdates._reset_epoch() # Don't do this. Just being done for this tutorial.
119+
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
118120
mdates.set_epoch(old_epoch)
119121

120122
x = np.arange('2000-01-01T00:00:00.0', '2000-01-01T00:00:00.000100',
@@ -129,7 +131,7 @@
129131
#############################################################################
130132
# For a more recent epoch, the plot is smooth:
131133

132-
mdates._reset_epoch() # Don't do this. Just being done for this tutorial.
134+
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
133135
mdates.set_epoch(new_epoch)
134136

135137
fig, ax = plt.subplots(constrained_layout=True)
@@ -138,7 +140,7 @@
138140
plt.setp(ax.xaxis.get_majorticklabels(), rotation=40)
139141
plt.show()
140142

141-
mdates._reset_epoch() # Don't do this. Just being done for this tutorial.
143+
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
142144

143145
#############################################################################
144146
# ------------

lib/matplotlib/dates.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,8 @@ def _get_rc_timezone():
225225
_epoch_used = False
226226

227227

228-
def _reset_epoch():
229-
"""
230-
Reset the Matplotlib date epoch to its rcParams value.
231-
"""
228+
def _reset_epoch_test_example():
229+
"""Reset the Matplotlib date epoch to its rcParams value."""
232230
global _epoch
233231
global _epoch_used
234232

@@ -238,23 +236,28 @@ def _reset_epoch():
238236

239237
def set_epoch(epoch):
240238
"""
241-
Set the epoch (origin for dates) for datetime calculations. The
242-
default epoch is '1970-01-01T00:00:00'. The `.date2num` method returns
243-
floating point days from the epoch, and dates that are too far
244-
from the epoch will experience floating point round off errors.
245-
246-
This must be called before any dates are converted (i.e. right after
247-
import of `.matplotlib.dates`) or a RuntimeError will be raised.
248-
249-
See :doc:`/gallery/ticks_and_spines/date_precision_and_epochs` for a
250-
discussion.
239+
Set the epoch (origin for dates) for datetime calculations.
251240
252241
Parameters
253242
----------
254243
epoch : str
255244
valid UTC date parsable by `numpy.datetime64` (do not include
256245
timezone).
257246
247+
The default epoch is :rc:`dates.epoch`, and users should not need to
248+
change this.
249+
250+
If microsecond accuracy is desired, the date being plotted needs to be
251+
within approximately 70 years of the epoch. Matplotlib internally
252+
represents dates as days since the epoch, so floating point dynamic
253+
range needs to be within a factor fo 2^52.
254+
255+
`~dates.set_epoch` must be called before any dates are converted
256+
(i.e. near the import section) or a RuntimeError will be raised.
257+
258+
See :doc:`/gallery/ticks_and_spines/date_precision_and_epochs` for a
259+
discussion.
260+
258261
"""
259262
global _epoch
260263
global _epoch_used
@@ -265,17 +268,20 @@ def set_epoch(epoch):
265268

266269
def get_epoch():
267270
"""
268-
Get the epoch currently used by `.dates`.
271+
Get the epoch used by `.dates`.
269272
270273
Returns
271274
-------
272275
epoch: str
276+
String for the epoch (parsable by `numpy.datetime64`)
273277
"""
274278
global _epoch
275279
global _epoch_used
276280

277281
if _epoch is None:
278282
_epoch = matplotlib.rcParams['date.epoch']
283+
# if get_epoch is called, then set_epoch can no longer be called.
284+
# _epoch_used is the latch for checking this.
279285
_epoch_used = True
280286
return _epoch
281287

lib/matplotlib/mpl-data/stylelib/classic.mplstyle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ date.autoformatter.hour : %H:%M:%S
229229
date.autoformatter.minute : %H:%M:%S.%f
230230
date.autoformatter.second : %H:%M:%S.%f
231231
date.autoformatter.microsecond : %H:%M:%S.%f
232-
date.epoch : 0000-12-31T00:00:00 # old epoch
232+
date.epoch : 0000-12-31T00:00:00 # old epoch
233233

234234
### TICKS
235235
# see http://matplotlib.org/api/axis_api.html#matplotlib.axis.Tick

lib/matplotlib/tests/test_dates.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,10 @@ def test_too_many_date_ticks(caplog):
161161

162162
def _new_epoch_decorator(thefunc):
163163
def wrapper():
164-
mdates._reset_epoch()
164+
mdates._reset_epoch_test_example()
165165
mdates.set_epoch('2000-01-01')
166166
thefunc()
167-
mdates._reset_epoch()
167+
mdates._reset_epoch_test_example()
168168
return wrapper
169169

170170

@@ -938,20 +938,20 @@ def test_change_epoch():
938938
mdates.set_epoch('1970-01-01')
939939

940940
# use private method to clear the epoch and allow it to be set...
941-
mdates._reset_epoch()
941+
mdates._reset_epoch_test_example()
942942
mdates.set_epoch('1970-01-01')
943943
dt = (date - np.datetime64('1970-01-01')).astype('datetime64[D]')
944944
dt = dt.astype('int')
945945
np.testing.assert_equal(mdates.date2num(date), float(dt))
946946

947-
mdates._reset_epoch()
947+
mdates._reset_epoch_test_example()
948948
mdates.set_epoch('0000-12-31')
949949
np.testing.assert_equal(mdates.date2num(date), 730120.0)
950950

951-
mdates._reset_epoch()
951+
mdates._reset_epoch_test_example()
952952
mdates.set_epoch('1970-01-01T01:00:00')
953953
np.testing.assert_allclose(mdates.date2num(date), dt - 1./24.)
954-
mdates._reset_epoch()
954+
mdates._reset_epoch_test_example()
955955
mdates.set_epoch('1970-01-01T00:00:00')
956956
np.testing.assert_allclose(
957957
mdates.date2num(np.datetime64('1970-01-01T12:00:00')),

0 commit comments

Comments
 (0)