
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "gallery/ticks/date_precision_and_epochs.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. meta::
        :keywords: codex

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_gallery_ticks_date_precision_and_epochs.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_gallery_ticks_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.

.. GENERATED FROM PYTHON SOURCE LINES 20-36

.. code-block:: Python

    import datetime

    import matplotlib.pyplot as plt
    import numpy as np

    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()









.. GENERATED FROM PYTHON SOURCE LINES 37-43

Datetime
--------

Python `.datetime` objects have microsecond resolution, so with the
old default matplotlib dates could not round-trip full-resolution datetime
objects.

.. GENERATED FROM PYTHON SOURCE LINES 43-57

.. code-block:: Python


    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

 .. 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




.. GENERATED FROM PYTHON SOURCE LINES 58-60

Note this is only a round-off error, and there is no problem for
dates closer to the old epoch:

.. GENERATED FROM PYTHON SOURCE LINES 60-68

.. code-block:: Python


    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

 .. 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




.. GENERATED FROM PYTHON SOURCE LINES 69-73

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`.

.. GENERATED FROM PYTHON SOURCE LINES 73-79

.. code-block:: Python


    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

 .. code-block:: none

    RuntimeError: set_epoch must be called before dates plotted.




.. GENERATED FROM PYTHON SOURCE LINES 80-82

For this tutorial, we reset the sentinel using a private method, but users
should just set the epoch once, if at all.

.. GENERATED FROM PYTHON SOURCE LINES 82-93

.. code-block:: Python


    _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

 .. 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




.. GENERATED FROM PYTHON SOURCE LINES 94-101

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.

.. GENERATED FROM PYTHON SOURCE LINES 101-111

.. code-block:: Python


    _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

 .. 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




.. GENERATED FROM PYTHON SOURCE LINES 112-118

Plotting
--------

This all of course has an effect on plotting.  With the old default epoch
the times were rounded during the internal ``date2num`` conversion, leading
to jumps in the data:

.. GENERATED FROM PYTHON SOURCE LINES 118-138

.. code-block:: Python


    _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]')
    # simulate the plot being made using the old epoch
    xold = np.array([mdates.num2date(mdates.date2num(d)) for d in x])
    y = np.arange(0, len(x))

    # resetting the Epoch so plots are comparable
    _reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
    mdates.set_epoch(new_epoch)

    fig, ax = plt.subplots(layout='constrained')
    ax.plot(xold, y)
    ax.set_title('Epoch: ' + mdates.get_epoch())
    ax.xaxis.set_tick_params(rotation=40)
    plt.show()




.. image-sg:: /gallery/ticks/images/sphx_glr_date_precision_and_epochs_001.png
   :alt: Epoch: 1970-01-01T00:00:00
   :srcset: /gallery/ticks/images/sphx_glr_date_precision_and_epochs_001.png, /gallery/ticks/images/sphx_glr_date_precision_and_epochs_001_2_00x.png 2.00x
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 139-140

For dates plotted using the more recent epoch, the plot is smooth:

.. GENERATED FROM PYTHON SOURCE LINES 140-149

.. code-block:: Python


    fig, ax = plt.subplots(layout='constrained')
    ax.plot(x, y)
    ax.set_title('Epoch: ' + mdates.get_epoch())
    ax.xaxis.set_tick_params(rotation=40)
    plt.show()

    _reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.




.. image-sg:: /gallery/ticks/images/sphx_glr_date_precision_and_epochs_002.png
   :alt: Epoch: 1970-01-01T00:00:00
   :srcset: /gallery/ticks/images/sphx_glr_date_precision_and_epochs_002.png, /gallery/ticks/images/sphx_glr_date_precision_and_epochs_002_2_00x.png 2.00x
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 150-158

.. admonition:: References

   The use of the following functions, methods, classes and modules is shown
   in this example:

   - `matplotlib.dates.num2date`
   - `matplotlib.dates.date2num`
   - `matplotlib.dates.set_epoch`


.. _sphx_glr_download_gallery_ticks_date_precision_and_epochs.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: date_precision_and_epochs.ipynb <date_precision_and_epochs.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: date_precision_and_epochs.py <date_precision_and_epochs.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: date_precision_and_epochs.zip <date_precision_and_epochs.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
