.. only:: html
.. note::
:class: sphx-glr-download-link-note
Click :ref:`here ` to download the full example code
.. rst-class:: sphx-glr-example-title
.. _sphx_glr_gallery_ticks_and_spines_date_precision_and_epochs.py:
=========================
Date Precision and Epochs
=========================
Matplotlib can handle `.datetime` objects and `numpy.datetime64` objects using
a unit converter that recognizes these dates and converts them to floating
point numbers.
Before Matplotlib 3.3, the default for this conversion returns a float that was
days since "0000-12-31T00:00:00". As of Matplotlib 3.3, the default is
days from "1970-01-01T00:00:00". This allows more resolution for modern
dates. "2020-01-01" with the old epoch converted to 730120, and a 64-bit
floating point number has a resolution of 2^{-52}, or approximately
14 microseconds, so microsecond precision was lost. With the new default
epoch "2020-01-01" is 10957.0, so the achievable resolution is 0.21
microseconds.
.. code-block:: default
import datetime
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
def _reset_epoch_for_tutorial():
"""
Users (and downstream libraries) should not use the private method of
resetting the epoch.
"""
mdates._reset_epoch_test_example()
Datetime
--------
Python `.datetime` objects have microsecond resolution, so with the
old default matplotlib dates could not round-trip full-resolution datetime
objects.
.. code-block:: default
old_epoch = '0000-12-31T00:00:00'
new_epoch = '1970-01-01T00:00:00'
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
mdates.set_epoch(old_epoch) # old epoch (pre MPL 3.3)
date1 = datetime.datetime(2000, 1, 1, 0, 10, 0, 12,
tzinfo=datetime.timezone.utc)
mdate1 = mdates.date2num(date1)
print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
date2 = mdates.num2date(mdate1)
print('After Roundtrip: ', date2)
.. rst-class:: sphx-glr-script-out
Out:
.. code-block:: none
Before Roundtrip: 2000-01-01 00:10:00.000012+00:00 Matplotlib date: 730120.0069444446
After Roundtrip: 2000-01-01 00:10:00.000020+00:00
Note this is only a round-off error, and there is no problem for
dates closer to the old epoch:
.. code-block:: default
date1 = datetime.datetime(10, 1, 1, 0, 10, 0, 12,
tzinfo=datetime.timezone.utc)
mdate1 = mdates.date2num(date1)
print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
date2 = mdates.num2date(mdate1)
print('After Roundtrip: ', date2)
.. rst-class:: sphx-glr-script-out
Out:
.. code-block:: none
Before Roundtrip: 0010-01-01 00:10:00.000012+00:00 Matplotlib date: 3288.006944444583
After Roundtrip: 0010-01-01 00:10:00.000012+00:00
If a user wants to use modern dates at microsecond precision, they
can change the epoch using `~.set_epoch`. However, the epoch has to be
set before any date operations to prevent confusion between different
epochs. Trying to change the epoch later will raise a `RuntimeError`.
.. code-block:: default
try:
mdates.set_epoch(new_epoch) # this is the new MPL 3.3 default.
except RuntimeError as e:
print('RuntimeError:', str(e))
.. rst-class:: sphx-glr-script-out
Out:
.. code-block:: none
RuntimeError: set_epoch must be called before dates plotted.
For this tutorial, we reset the sentinel using a private method, but users
should just set the epoch once, if at all.
.. code-block:: default
_reset_epoch_for_tutorial() # Just being done for this tutorial.
mdates.set_epoch(new_epoch)
date1 = datetime.datetime(2020, 1, 1, 0, 10, 0, 12,
tzinfo=datetime.timezone.utc)
mdate1 = mdates.date2num(date1)
print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
date2 = mdates.num2date(mdate1)
print('After Roundtrip: ', date2)
.. rst-class:: sphx-glr-script-out
Out:
.. code-block:: none
Before Roundtrip: 2020-01-01 00:10:00.000012+00:00 Matplotlib date: 18262.006944444583
After Roundtrip: 2020-01-01 00:10:00.000012+00:00
datetime64
----------
`numpy.datetime64` objects have microsecond precision for a much larger
timespace than `.datetime` objects. However, currently Matplotlib time is
only converted back to datetime objects, which have microsecond resolution,
and years that only span 0000 to 9999.
.. code-block:: default
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
mdates.set_epoch(new_epoch)
date1 = np.datetime64('2000-01-01T00:10:00.000012')
mdate1 = mdates.date2num(date1)
print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
date2 = mdates.num2date(mdate1)
print('After Roundtrip: ', date2)
.. rst-class:: sphx-glr-script-out
Out:
.. code-block:: none
Before Roundtrip: 2000-01-01T00:10:00.000012 Matplotlib date: 10957.006944444583
After Roundtrip: 2000-01-01 00:10:00.000012+00:00
Plotting
--------
This all of course has an effect on plotting. With the old default epoch
the times were rounded, leading to jumps in the data:
.. code-block:: default
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
mdates.set_epoch(old_epoch)
x = np.arange('2000-01-01T00:00:00.0', '2000-01-01T00:00:00.000100',
dtype='datetime64[us]')
y = np.arange(0, len(x))
fig, ax = plt.subplots(constrained_layout=True)
ax.plot(x, y)
ax.set_title('Epoch: ' + mdates.get_epoch())
plt.setp(ax.xaxis.get_majorticklabels(), rotation=40)
plt.show()
.. image:: /gallery/ticks_and_spines/images/sphx_glr_date_precision_and_epochs_001.png
:alt: Epoch: 0000-12-31T00:00:00
:class: sphx-glr-single-img
.. rst-class:: sphx-glr-script-out
Out:
.. code-block:: none
/mpl_source/examples/ticks_and_spines/date_precision_and_epochs.py:127: UserWarning: Plotting microsecond time intervals for dates far from the epoch (time origin: 0000-12-31T00:00:00) is not well-supported. See matplotlib.dates.set_epoch to change the epoch.
plt.setp(ax.xaxis.get_majorticklabels(), rotation=40)
For a more recent epoch, the plot is smooth:
.. code-block:: default
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
mdates.set_epoch(new_epoch)
fig, ax = plt.subplots(constrained_layout=True)
ax.plot(x, y)
ax.set_title('Epoch: ' + mdates.get_epoch())
plt.setp(ax.xaxis.get_majorticklabels(), rotation=40)
plt.show()
_reset_epoch_for_tutorial() # Don't do this. Just for this tutorial.
.. image:: /gallery/ticks_and_spines/images/sphx_glr_date_precision_and_epochs_002.png
:alt: Epoch: 1970-01-01T00:00:00
:class: sphx-glr-single-img
------------
References
""""""""""
The use of the following functions, methods and classes is shown
in this example:
.. code-block:: default
matplotlib.dates.num2date
matplotlib.dates.date2num
matplotlib.dates.set_epoch
.. rst-class:: sphx-glr-script-out
Out:
.. code-block:: none
.. _sphx_glr_download_gallery_ticks_and_spines_date_precision_and_epochs.py:
.. only :: html
.. container:: sphx-glr-footer
:class: sphx-glr-footer-example
.. container:: sphx-glr-download sphx-glr-download-python
:download:`Download Python source code: date_precision_and_epochs.py `
.. container:: sphx-glr-download sphx-glr-download-jupyter
:download:`Download Jupyter notebook: date_precision_and_epochs.ipynb `
.. only:: html
.. rst-class:: sphx-glr-signature
Keywords: matplotlib code example, codex, python plot, pyplot
`Gallery generated by Sphinx-Gallery
`_