Note

Go to the endto download the full example code.

Date precision and epochs#

Matplotlib can handledatetime objects andnumpy.datetime64 objects usinga unit converter that recognizes these dates and converts them to floatingpoint numbers.

Before Matplotlib 3.3, the default for this conversion returns a float that wasdays since "0000-12-31T00:00:00". As of Matplotlib 3.3, the default isdays from "1970-01-01T00:00:00". This allows more resolution for moderndates. "2020-01-01" with the old epoch converted to 730120, and a 64-bitfloating point number has a resolution of 2^{-52}, or approximately14 microseconds, so microsecond precision was lost. With the new defaultepoch "2020-01-01" is 10957.0, so the achievable resolution is 0.21microseconds.

importdatetimeimportmatplotlib.pyplotaspltimportnumpyasnpimportmatplotlib.datesasmdatesdef_reset_epoch_for_tutorial():"""    Users (and downstream libraries) should not use the private method of    resetting the epoch.    """mdates._reset_epoch_test_example()

Datetime#

Pythondatetime objects have microsecond resolution, so with theold default matplotlib dates could not round-trip full-resolution datetimeobjects.

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)
Before Roundtrip:  2000-01-01 00:10:00.000012+00:00 Matplotlib date: 730120.0069444446After Roundtrip:   2000-01-01 00:10:00.000020+00:00

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

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)
Before Roundtrip:  0010-01-01 00:10:00.000012+00:00 Matplotlib date: 3288.006944444583After Roundtrip:   0010-01-01 00:10:00.000012+00:00

If a user wants to use modern dates at microsecond precision, theycan change the epoch usingset_epoch. However, the epoch has to beset before any date operations to prevent confusion between differentepochs. Trying to change the epoch later will raise aRuntimeError.

try:mdates.set_epoch(new_epoch)# this is the new MPL 3.3 default.exceptRuntimeErrorase:print('RuntimeError:',str(e))
RuntimeError: set_epoch must be called before dates plotted.

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

_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)
Before Roundtrip:  2020-01-01 00:10:00.000012+00:00 Matplotlib date: 18262.006944444583After Roundtrip:   2020-01-01 00:10:00.000012+00:00

datetime64#

numpy.datetime64 objects have microsecond precision for a much largertimespace thandatetime objects. However, currently Matplotlib time isonly converted back to datetime objects, which have microsecond resolution,and years that only span 0000 to 9999.

_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)
Before Roundtrip:  2000-01-01T00:10:00.000012 Matplotlib date: 10957.006944444583After Roundtrip:   2000-01-01 00:10:00.000012+00:00

Plotting#

This all of course has an effect on plotting. With the old default epochthe times were rounded during the internaldate2num conversion, leadingto jumps in the data:

_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 epochxold=np.array([mdates.num2date(mdates.date2num(d))fordinx])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()
Epoch: 1970-01-01T00:00:00

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

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.
Epoch: 1970-01-01T00:00:00

References

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

Total running time of the script: (0 minutes 1.047 seconds)

Gallery generated by Sphinx-Gallery