{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n\n# Format date ticks using ConciseDateFormatter\n\nFinding good tick values and formatting the ticks for an axis that\nhas date data is often a challenge.  `~.dates.ConciseDateFormatter` is\nmeant to improve the strings chosen for the ticklabels, and to minimize\nthe strings used in those tick labels as much as possible.\n\n<div class=\"alert alert-info\"><h4>Note</h4><p>This formatter is a candidate to become the default date tick formatter\n    in future versions of Matplotlib.  Please report any issues or\n    suggestions for improvement to the GitHub repository or mailing list.</p></div>\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import datetime\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nimport matplotlib.dates as mdates"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "First, the default formatter.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "base = datetime.datetime(2005, 2, 1)\ndates = [base + datetime.timedelta(hours=(2 * i)) for i in range(732)]\nN = len(dates)\nnp.random.seed(19680801)\ny = np.cumsum(np.random.randn(N))\n\nfig, axs = plt.subplots(3, 1, layout='constrained', figsize=(6, 6))\nlims = [(np.datetime64('2005-02'), np.datetime64('2005-04')),\n        (np.datetime64('2005-02-03'), np.datetime64('2005-02-15')),\n        (np.datetime64('2005-02-03 11:00'), np.datetime64('2005-02-04 13:20'))]\nfor nn, ax in enumerate(axs):\n    ax.plot(dates, y)\n    ax.set_xlim(lims[nn])\n    ax.tick_params(axis='x', rotation=40, rotation_mode='xtick')\naxs[0].set_title('Default Date Formatter')\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The default date formatter is quite verbose, so we have the option of\nusing `~.dates.ConciseDateFormatter`, as shown below.  Note that\nfor this example the labels do not need to be rotated as they do for the\ndefault formatter because the labels are as small as possible.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fig, axs = plt.subplots(3, 1, layout='constrained', figsize=(6, 6))\nfor nn, ax in enumerate(axs):\n    locator = mdates.AutoDateLocator(minticks=3, maxticks=7)\n    formatter = mdates.ConciseDateFormatter(locator)\n    ax.xaxis.set_major_locator(locator)\n    ax.xaxis.set_major_formatter(formatter)\n\n    ax.plot(dates, y)\n    ax.set_xlim(lims[nn])\naxs[0].set_title('Concise Date Formatter')\n\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "If all calls to axes that have dates are to be made using this converter,\nit is probably most convenient to use the units registry where you do\nimports:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import matplotlib.units as munits\n\nconverter = mdates.ConciseDateConverter()\nmunits.registry[np.datetime64] = converter\nmunits.registry[datetime.date] = converter\nmunits.registry[datetime.datetime] = converter\n\nfig, axs = plt.subplots(3, 1, figsize=(6, 6), layout='constrained')\nfor nn, ax in enumerate(axs):\n    ax.plot(dates, y)\n    ax.set_xlim(lims[nn])\naxs[0].set_title('Concise Date Formatter')\n\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Localization of date formats\n\nDates formats can be localized if the default formats are not desirable by\nmanipulating one of three lists of strings.\n\nThe ``formatter.formats`` list of formats is for the normal tick labels,\nThere are six levels: years, months, days, hours, minutes, seconds.\nThe ``formatter.offset_formats`` is how the \"offset\" string on the right\nof the axis is formatted.  This is usually much more verbose than the tick\nlabels. Finally, the ``formatter.zero_formats`` are the formats of the\nticks that are \"zeros\".  These are tick values that are either the first of\nthe year, month, or day of month, or the zeroth hour, minute, or second.\nThese are usually the same as the format of\nthe ticks a level above.  For example if the axis limits mean the ticks are\nmostly days, then we label 1 Mar 2005 simply with a \"Mar\".  If the axis\nlimits are mostly hours, we label Feb 4 00:00 as simply \"Feb-4\".\n\nNote that these format lists can also be passed to `.ConciseDateFormatter`\nas optional keyword arguments.\n\nHere we modify the labels to be \"day month year\", instead of the ISO\n\"year month day\":\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fig, axs = plt.subplots(3, 1, layout='constrained', figsize=(6, 6))\n\nfor nn, ax in enumerate(axs):\n    locator = mdates.AutoDateLocator()\n    formatter = mdates.ConciseDateFormatter(locator)\n    formatter.formats = ['%y',  # ticks are mostly years\n                         '%b',       # ticks are mostly months\n                         '%d',       # ticks are mostly days\n                         '%H:%M',    # hrs\n                         '%H:%M',    # min\n                         '%S.%f', ]  # secs\n    # these are mostly just the level above...\n    formatter.zero_formats = [''] + formatter.formats[:-1]\n    # ...except for ticks that are mostly hours, then it is nice to have\n    # month-day:\n    formatter.zero_formats[3] = '%d-%b'\n\n    formatter.offset_formats = ['',\n                                '%Y',\n                                '%b %Y',\n                                '%d %b %Y',\n                                '%d %b %Y',\n                                '%d %b %Y %H:%M', ]\n    ax.xaxis.set_major_locator(locator)\n    ax.xaxis.set_major_formatter(formatter)\n\n    ax.plot(dates, y)\n    ax.set_xlim(lims[nn])\naxs[0].set_title('Concise Date Formatter')\n\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Registering a converter with localization\n\n`.ConciseDateFormatter` doesn't have rcParams entries, but localization can\nbe accomplished by passing keyword arguments to `.ConciseDateConverter` and\nregistering the datatypes you will use with the units registry:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import datetime\n\nformats = ['%y',          # ticks are mostly years\n           '%b',     # ticks are mostly months\n           '%d',     # ticks are mostly days\n           '%H:%M',  # hrs\n           '%H:%M',  # min\n           '%S.%f', ]  # secs\n# these can be the same, except offset by one level....\nzero_formats = [''] + formats[:-1]\n# ...except for ticks that are mostly hours, then it's nice to have month-day\nzero_formats[3] = '%d-%b'\noffset_formats = ['',\n                  '%Y',\n                  '%b %Y',\n                  '%d %b %Y',\n                  '%d %b %Y',\n                  '%d %b %Y %H:%M', ]\n\nconverter = mdates.ConciseDateConverter(\n    formats=formats, zero_formats=zero_formats, offset_formats=offset_formats)\n\nmunits.registry[np.datetime64] = converter\nmunits.registry[datetime.date] = converter\nmunits.registry[datetime.datetime] = converter\n\nfig, axs = plt.subplots(3, 1, layout='constrained', figsize=(6, 6))\nfor nn, ax in enumerate(axs):\n    ax.plot(dates, y)\n    ax.set_xlim(lims[nn])\naxs[0].set_title('Concise Date Formatter registered non-default')\n\nplt.show()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.14.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}