4242from __future__ import annotations
4343
4444import math
45- import warnings
4645from datetime import timedelta
47- from typing import TYPE_CHECKING , Any
46+ from typing import TYPE_CHECKING , Any , Optional
4847
4948import numpy as np
5049import pandas as pd
5857)
5958from xarray .core .common import _contains_cftime_datetimes
6059from xarray .core .options import OPTIONS
61- from xarray .core .utils import attempt_import , is_scalar
60+ from xarray .core .types import PDDatetimeUnitOptions
61+ from xarray .core .utils import attempt_import , emit_user_level_warning , is_scalar
6262
6363if TYPE_CHECKING :
6464 from xarray .coding .cftime_offsets import BaseCFTimeOffset
@@ -239,6 +239,8 @@ class CFTimeIndex(pd.Index):
239239 cftime_range
240240 """
241241
242+ _data : np .ndarray
243+
242244 year = _field_accessor ("year" , "The year of the datetime" )
243245 month = _field_accessor ("month" , "The month of the datetime" )
244246 day = _field_accessor ("day" , "The days of the datetime" )
@@ -544,14 +546,18 @@ def __rsub__(self, other):
544546 "that can be expressed at the nanosecond resolution."
545547 ) from err
546548
547- def to_datetimeindex (self , unsafe = False ):
549+ def to_datetimeindex (
550+ self , unsafe : bool = False , time_unit : Optional [PDDatetimeUnitOptions ] = None
551+ ) -> pd .DatetimeIndex :
548552 """If possible, convert this index to a pandas.DatetimeIndex.
549553
550554 Parameters
551555 ----------
552556 unsafe : bool
553- Flag to turn off warning when converting from a CFTimeIndex with
554- a non-standard calendar to a DatetimeIndex (default ``False``).
557+ Flag to turn off calendar mismatch warnings (default ``False``).
558+ time_unit : str
559+ Time resolution of resulting DatetimeIndex. Can be one of `"s"`,
560+ ``"ms"``, ``"us"``, or ``"ns"`` (default ``"ns"``).
555561
556562 Returns
557563 -------
@@ -561,45 +567,68 @@ def to_datetimeindex(self, unsafe=False):
561567 ------
562568 ValueError
563569 If the CFTimeIndex contains dates that are not possible in the
564- standard calendar or outside the nanosecond-precision range.
570+ standard calendar or outside the range representable by the
571+ specified ``time_unit``.
565572
566573 Warns
567574 -----
568575 RuntimeWarning
569- If converting from a non-standard calendar to a DatetimeIndex.
576+ If converting from a non-standard calendar, or a Gregorian
577+ calendar with dates prior to the reform (1582-10-15).
570578
571579 Warnings
572580 --------
573- Note that for non-standard calendars, this will change the calendar
574- type of the index. In that case the result of this method should be
575- used with caution.
581+ Note that for non-proleptic Gregorian calendars, this will change the
582+ calendar type of the index. In that case the result of this method
583+ should be used with caution.
576584
577585 Examples
578586 --------
579587 >>> times = xr.cftime_range("2000", periods=2, calendar="gregorian")
580588 >>> times
581589 CFTimeIndex([2000-01-01 00:00:00, 2000-01-02 00:00:00],
582590 dtype='object', length=2, calendar='standard', freq=None)
583- >>> times.to_datetimeindex()
584- DatetimeIndex(['2000-01-01', '2000-01-02'], dtype='datetime64[us ]', freq=None)
591+ >>> times.to_datetimeindex(time_unit="ns" )
592+ DatetimeIndex(['2000-01-01', '2000-01-02'], dtype='datetime64[ns ]', freq=None)
585593 """
586594
587595 if not self ._data .size :
588596 return pd .DatetimeIndex ([])
589597
590- # transform to us-resolution is needed for DatetimeIndex
591- nptimes = cftime_to_nptime (self , time_unit = "us" )
598+ if time_unit is None :
599+ emit_user_level_warning (
600+ "In a future version of xarray to_datetimeindex will default "
601+ "to returning a 'us'-resolution DatetimeIndex instead of a "
602+ "'ns'-resolution DatetimeIndex. This warning can be silenced "
603+ "by explicitly passing the `time_unit` keyword argument." ,
604+ FutureWarning ,
605+ )
606+ time_unit = "ns"
607+
608+ nptimes = cftime_to_nptime (self , time_unit = time_unit )
592609 calendar = infer_calendar_name (self )
593610 if calendar not in _STANDARD_CALENDARS and not unsafe :
594- warnings . warn (
611+ emit_user_level_warning (
595612 "Converting a CFTimeIndex with dates from a non-standard "
596- f"calendar, { calendar !r} , to a pandas.DatetimeIndex, which uses dates "
597- "from the standard calendar. This may lead to subtle errors "
598- "in operations that depend on the length of time between "
599- "dates." ,
613+ f"calendar, { calendar !r} , to a pandas.DatetimeIndex, which "
614+ "uses dates from the standard calendar. This may lead to "
615+ "subtle errors in operations that depend on the length of "
616+ "time between dates." ,
600617 RuntimeWarning ,
601- stacklevel = 2 ,
602618 )
619+ if calendar == "standard" and not unsafe :
620+ reform_date = self .date_type (1582 , 10 , 15 )
621+ if self .min () < reform_date :
622+ emit_user_level_warning (
623+ "Converting a CFTimeIndex with dates from a Gregorian "
624+ "calendar that fall before the reform date of 1582-10-15 "
625+ "to a pandas.DatetimeIndex. During this time period the "
626+ "Gregorian calendar and the proleptic Gregorian calendar "
627+ "of the DatetimeIndex do not exactly align. This warning "
628+ "can be silenced by setting unsafe=True." ,
629+ RuntimeWarning ,
630+ )
631+
603632 return pd .DatetimeIndex (nptimes )
604633
605634 def strftime (self , date_format ):
0 commit comments