|
115 | 115 | <../gallery/ticks_and_spines/date_demo_rrule.html>`_.
|
116 | 116 |
|
117 | 117 | * :class:`AutoDateLocator`: On autoscale, this class picks the best
|
118 |
| - :class:`RRuleLocator` to set the view limits and the tick |
| 118 | + :class:`DateLocator` (e.g., :class:`RRuleLocator`) |
| 119 | + to set the view limits and the tick |
119 | 120 | locations. If called with ``interval_multiples=True`` it will
|
120 | 121 | make ticks line up with sensible multiples of the tick intervals. E.g.
|
121 | 122 | if the interval is 4 hours, it will pick hours 0, 4, 8, etc as ticks.
|
122 |
| - This behaviour is not garaunteed by default. |
| 123 | + This behaviour is not guaranteed by default. |
123 | 124 |
|
124 | 125 | Date formatters
|
125 | 126 | ---------------
|
|
146 | 147 | import functools
|
147 | 148 |
|
148 | 149 | import warnings
|
| 150 | +import logging |
149 | 151 |
|
150 | 152 | from dateutil.rrule import (rrule, MO, TU, WE, TH, FR, SA, SU, YEARLY,
|
151 | 153 | MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY,
|
|
177 | 179 | 'seconds', 'minutes', 'hours', 'weeks')
|
178 | 180 |
|
179 | 181 |
|
| 182 | +_log = logging.getLogger(__name__) |
| 183 | + |
| 184 | + |
180 | 185 | # Make a simple UTC instance so we don't always have to import
|
181 | 186 | # pytz. From the python datetime library docs:
|
182 | 187 |
|
@@ -311,14 +316,21 @@ def _from_ordinalf(x, tz=None):
|
311 | 316 |
|
312 | 317 | remainder = float(x) - ix
|
313 | 318 |
|
314 |
| - # Round down to the nearest microsecond. |
315 |
| - dt += datetime.timedelta(microseconds=int(remainder * MUSECONDS_PER_DAY)) |
| 319 | + # Since the input date `x` float is unable to preserve microsecond |
| 320 | + # precision of time representation in non-antique years, the |
| 321 | + # resulting datetime is rounded to the nearest multiple of |
| 322 | + # `musec_prec`. A value of 20 is appropriate for current dates. |
| 323 | + musec_prec = 20 |
| 324 | + remainder_musec = int(round(remainder * MUSECONDS_PER_DAY / |
| 325 | + float(musec_prec)) * musec_prec) |
| 326 | + |
| 327 | + # For people trying to plot with full microsecond precision, enable |
| 328 | + # an early-year workaround |
| 329 | + if x < 30 * 365: |
| 330 | + remainder_musec = int(round(remainder * MUSECONDS_PER_DAY)) |
316 | 331 |
|
317 |
| - # Compensate for rounding errors |
318 |
| - if dt.microsecond < 10: |
319 |
| - dt = dt.replace(microsecond=0) |
320 |
| - elif dt.microsecond > 999990: |
321 |
| - dt += datetime.timedelta(microseconds=1e6 - dt.microsecond) |
| 332 | + # add hours, minutes, seconds, microseconds |
| 333 | + dt += datetime.timedelta(microseconds=remainder_musec) |
322 | 334 |
|
323 | 335 | return dt.astimezone(tz)
|
324 | 336 |
|
@@ -1347,6 +1359,11 @@ def get_locator(self, dmin, dmax):
|
1347 | 1359 | locator = RRuleLocator(rrule, self.tz)
|
1348 | 1360 | else:
|
1349 | 1361 | locator = MicrosecondLocator(interval, tz=self.tz)
|
| 1362 | + if dmin.year > 20 and interval < 1000: |
| 1363 | + _log.warn('Plotting microsecond time intervals is not' |
| 1364 | + ' well supported. Please see the' |
| 1365 | + ' MicrosecondLocator documentation' |
| 1366 | + ' for details.') |
1350 | 1367 |
|
1351 | 1368 | locator.set_axis(self.axis)
|
1352 | 1369 |
|
@@ -1562,7 +1579,21 @@ def __init__(self, bysecond=None, interval=1, tz=None):
|
1562 | 1579 |
|
1563 | 1580 | class MicrosecondLocator(DateLocator):
|
1564 | 1581 | """
|
1565 |
| - Make ticks on occurances of each microsecond. |
| 1582 | + Make ticks on regular intervals of one or more microsecond(s). |
| 1583 | +
|
| 1584 | + .. note:: |
| 1585 | +
|
| 1586 | + Due to the floating point representation of time in days since |
| 1587 | + 0001-01-01 UTC (plus 1), plotting data with microsecond time |
| 1588 | + resolution does not work well with current dates. |
| 1589 | +
|
| 1590 | + If you want microsecond resolution time plots, it is strongly |
| 1591 | + recommended to use floating point seconds, not datetime-like |
| 1592 | + time representation. |
| 1593 | +
|
| 1594 | + If you really must use datetime.datetime() or similar and still |
| 1595 | + need microsecond precision, your only chance is to use very |
| 1596 | + early years; using year 0001 is recommended. |
1566 | 1597 |
|
1567 | 1598 | """
|
1568 | 1599 | def __init__(self, interval=1, tz=None):
|
|
0 commit comments