|
118 | 118 | import time
|
119 | 119 | import math
|
120 | 120 | import datetime
|
| 121 | +import string |
121 | 122 |
|
122 | 123 | import warnings
|
123 | 124 |
|
124 |
| - |
125 | 125 | from dateutil.rrule import (rrule, MO, TU, WE, TH, FR, SA, SU, YEARLY,
|
126 | 126 | MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY,
|
127 | 127 | SECONDLY)
|
@@ -217,6 +217,9 @@ def _to_ordinalf(dt):
|
217 | 217 | dt = dt.astimezone(UTC)
|
218 | 218 | tzi = UTC
|
219 | 219 |
|
| 220 | + if isinstance(dt, datetime.timedelta): |
| 221 | + return dt.total_seconds() / SEC_PER_DAY |
| 222 | + |
220 | 223 | base = float(dt.toordinal())
|
221 | 224 |
|
222 | 225 | # If it's sufficiently datetime-like, it will have a `date()` method
|
@@ -605,6 +608,82 @@ def strftime(self, dt, fmt=None):
|
605 | 608 | return self.strftime_pre_1900(dt, fmt)
|
606 | 609 |
|
607 | 610 |
|
| 611 | +class TimedeltaFormatter(ticker.Formatter): |
| 612 | + |
| 613 | + def __init__(self, fmt): |
| 614 | + r""" |
| 615 | + *fmt* is a format string, with accepted format arguments given in the |
| 616 | + table below. For more information on format strings see |
| 617 | + https://docs.python.org/3/library/string.html#format-string-syntax |
| 618 | +
|
| 619 | + .. table:: Accepted format arguments |
| 620 | + :widths: auto |
| 621 | +
|
| 622 | + ======== ======= |
| 623 | + Argument Meaning |
| 624 | + ======== ======= |
| 625 | + {D} Days |
| 626 | + {H} Hours |
| 627 | + {M} Minutes |
| 628 | + {S} Seconds |
| 629 | + {f} Microseconds |
| 630 | + ========= ======= |
| 631 | +
|
| 632 | + Examples |
| 633 | + -------- |
| 634 | + >>> from datetime import timedelta |
| 635 | + >>> from matplotlib.dates import TimedeltaFormatter |
| 636 | + >>> |
| 637 | + >>> dt = timedelta(hours=1, minutes=0, seconds=3) |
| 638 | + >>> fmt = '{H:02}:{M:02}:{S:02}' |
| 639 | + >>> formatter = TimedeltaFormatter(fmt) |
| 640 | + >>> formatter(dt) |
| 641 | + 01:00:03 |
| 642 | + >>> |
| 643 | + >>> fmt = '{S}' |
| 644 | + >>> formatter = TimedeltaFormatter(fmt) |
| 645 | + >>> formatter(dt) |
| 646 | + 3603 |
| 647 | + >>> |
| 648 | + >>> fmt = '{S}' |
| 649 | + >>> dt = timedelta(microseconds=1e5) |
| 650 | + >>> formatter(dt) |
| 651 | + 0.1 |
| 652 | + """ |
| 653 | + self.fmt = fmt |
| 654 | + |
| 655 | + def __call__(self, x): |
| 656 | + dt = num2timedelta(x) |
| 657 | + return self._strfdelta(dt) |
| 658 | + |
| 659 | + def _strfdelta(self, dt): |
| 660 | + # A custom method to format timedelta objects. See above for examples |
| 661 | + formatter = string.Formatter() |
| 662 | + d = {} |
| 663 | + allkeys = ['D', 'H', 'M', 'S', 'f'] |
| 664 | + secs = [SEC_PER_DAY, SEC_PER_HOUR, SEC_PER_MIN, 1, 1 / 1e6] |
| 665 | + # Get list of all keys in the format string |
| 666 | + keys = list(map(lambda x: x[1], list(formatter.parse(self.fmt)))) |
| 667 | + |
| 668 | + rem = dt.total_seconds() |
| 669 | + # Cycle through all keys, and if key present in format calculate value |
| 670 | + # of that key |
| 671 | + for key, seconds in zip(allkeys, secs): |
| 672 | + if key in keys: |
| 673 | + d[key] = rem / seconds |
| 674 | + _, rem = divmod(rem, seconds) |
| 675 | + |
| 676 | + # Cycle through and round every entry down to an int APART from the |
| 677 | + # smallest unit present in format |
| 678 | + foundlast = False |
| 679 | + for key in allkeys[::-1]: |
| 680 | + if key in keys: |
| 681 | + if foundlast or key == 'f': |
| 682 | + d[key] = int(np.floor(d[key])) |
| 683 | + foundlast = True |
| 684 | + return formatter.format(self.fmt, **d) |
| 685 | + |
| 686 | + |
608 | 687 | class IndexDateFormatter(ticker.Formatter):
|
609 | 688 | """
|
610 | 689 | Use with :class:`~matplotlib.ticker.IndexLocator` to cycle format
|
|
0 commit comments