diff --git a/aospy/utils/times.py b/aospy/utils/times.py
index beaea13..702e3ec 100644
--- a/aospy/utils/times.py
+++ b/aospy/utils/times.py
@@ -55,8 +55,7 @@ def apply_time_offset(time, years=0, months=0, days=0, hours=0):
                   freq=None)
     """
     return (pd.to_datetime(time.values) +
-            pd.tseries.offsets.DateOffset(years=years, months=months,
-                                          days=days, hours=hours))
+            pd.DateOffset(years=years, months=months, days=days, hours=hours))
 
 
 def average_time_bounds(ds):
@@ -512,12 +511,15 @@ def ensure_time_as_index(ds):
     time_indexed_coords = {TIME_WEIGHTS_STR, TIME_BOUNDS_STR}
     time_indexed_vars = set(ds.data_vars).union(time_indexed_coords)
     time_indexed_vars = time_indexed_vars.intersection(ds.variables)
+    variables_to_replace = {}
     for name in time_indexed_vars:
         if TIME_STR not in ds[name].indexes:
-            da = ds[name].expand_dims(TIME_STR)
-            da[TIME_STR] = ds[TIME_STR]
-            ds[name] = da
-    return ds
+            da = ds[name]
+            if TIME_STR not in da.dims:
+                da = ds[name].expand_dims(TIME_STR)
+            da = da.assign_coords(**{TIME_STR: ds[TIME_STR]})
+            variables_to_replace[name] = da
+    return ds.assign(**variables_to_replace)
 
 
 def infer_year(date):

diff --git a/aospy/test/test_utils_times.py b/aospy/test/test_utils_times.py
index ce94760..3fe4bd4 100755
--- a/aospy/test/test_utils_times.py
+++ b/aospy/test/test_utils_times.py
@@ -49,9 +49,10 @@ def test_apply_time_offset():
     # test lengths 0, 1, and >1 of input time array
     for periods in range(3):
         times = pd.date_range(start=start, freq='M', periods=periods)
+        times = pd.to_datetime(times.values)  # Workaround for pandas bug
         actual = apply_time_offset(xr.DataArray(times), years=years,
                                    months=months, days=days, hours=hours)
-        desired = (times + pd.tseries.offsets.DateOffset(
+        desired = (times + pd.DateOffset(
             years=years, months=months, days=days, hours=hours
         ))
         assert actual.identical(desired)
@@ -455,6 +456,7 @@ def test_ensure_time_as_index_with_change():
         coords={TIME_STR: arr[TIME_STR]}
     )
     ds = ds.isel(**{TIME_STR: 0}).expand_dims(TIME_STR)
+    initial_ds = ds.copy()
     actual = ensure_time_as_index(ds)
     expected = arr.to_dataset(name='a')
     expected.coords[TIME_WEIGHTS_STR] = xr.DataArray(
@@ -465,6 +467,9 @@ def test_ensure_time_as_index_with_change():
         coords={TIME_STR: arr[TIME_STR]}
         )
     xr.testing.assert_identical(actual, expected)
+    # Make sure input Dataset was not mutated by the call
+    # to ensure_time_as_index
+    xr.testing.assert_identical(ds, initial_ds)
 
 
 def test_sel_time():

New failure in test_apply_time_offset
After reverting to xarray 0.11, c.f. #319, the test suite loads without error but has one failure, which I've pasted below.  @spencerkclark, since this seems cftime-related (or adjacent), I'm going to punt this one to you.

```python
test_apply_time_offset ___________________________________________________________________

cls = <class 'pandas.core.arrays.datetimes.DatetimeArray'>
index = <DatetimeArray>
['1898-06-30 00:00:00', '1898-07-30 00:00:00']
Length: 2, dtype: datetime64[ns], freq = <MonthEnd>, kwargs = {}, inferred = None
on_freq = <DatetimeArray>
['1898-06-30 00:00:00', '1898-07-31 00:00:00']
Length: 2, dtype: datetime64[ns]

    @classmethod
    def _validate_frequency(cls, index, freq, **kwargs):
        """
        Validate that a frequency is compatible with the values of a given
        Datetime Array/Index or Timedelta Array/Index

        Parameters
        ----------
        index : DatetimeIndex or TimedeltaIndex
            The index on which to determine if the given frequency is valid
        freq : DateOffset
            The frequency to validate
        """
        if is_period_dtype(cls):
            # Frequency validation is not meaningful for Period Array/Index
            return None

        inferred = index.inferred_freq
        if index.size == 0 or inferred == freq.freqstr:
            return None

        try:
            on_freq = cls._generate_range(start=index[0], end=None,
                                          periods=len(index), freq=freq,
                                          **kwargs)
            if not np.array_equal(index.asi8, on_freq.asi8):
>               raise ValueError
E               ValueError

../../pandas/core/arrays/datetimelike.py:884: ValueError

During handling of the above exception, another exception occurred:

    def test_apply_time_offset():
        start = datetime.datetime(1900, 5, 10)
        years, months, days, hours = -2, 1, 7, 3
        # test lengths 0, 1, and >1 of input time array
        for periods in range(3):
            times = pd.date_range(start=start, freq='M', periods=periods)
            actual = apply_time_offset(xr.DataArray(times), years=years,
                                       months=months, days=days, hours=hours)
            desired = (times + pd.tseries.offsets.DateOffset(
>               years=years, months=months, days=days, hours=hours
            ))

test_utils_times.py:55:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../pandas/core/indexes/datetimelike.py:489: in __add__
    result = self._data.__add__(maybe_unwrap_index(other))
../../pandas/core/arrays/datetimelike.py:1190: in __add__
    result = self._add_offset(other)
../../pandas/core/arrays/datetimes.py:737: in _add_offset
    result = offset.apply_index(values)
pandas/_libs/tslibs/offsets.pyx:116: in pandas._libs.tslibs.offsets.apply_index_wraps.wrapper
    ???
../../pandas/tseries/offsets.py:278: in apply_index
    i = type(i)(shifted, freq=i.freq, dtype=i.dtype)
../../pandas/core/arrays/datetimes.py:351: in __init__
    type(self)._validate_frequency(self, freq)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cls = <class 'pandas.core.arrays.datetimes.DatetimeArray'>
index = <DatetimeArray>
['1898-06-30 00:00:00', '1898-07-30 00:00:00']
Length: 2, dtype: datetime64[ns], freq = <MonthEnd>, kwargs = {}, inferred = None
on_freq = <DatetimeArray>
['1898-06-30 00:00:00', '1898-07-31 00:00:00']
Length: 2, dtype: datetime64[ns]

    @classmethod
    def _validate_frequency(cls, index, freq, **kwargs):
        """
        Validate that a frequency is compatible with the values of a given
        Datetime Array/Index or Timedelta Array/Index

        Parameters
        ----------
        index : DatetimeIndex or TimedeltaIndex
            The index on which to determine if the given frequency is valid
        freq : DateOffset
            The frequency to validate
        """
        if is_period_dtype(cls):
            # Frequency validation is not meaningful for Period Array/Index
            return None

        inferred = index.inferred_freq
        if index.size == 0 or inferred == freq.freqstr:
            return None

        try:
            on_freq = cls._generate_range(start=index[0], end=None,
                                          periods=len(index), freq=freq,
                                          **kwargs)
            if not np.array_equal(index.asi8, on_freq.asi8):
                raise ValueError
        except ValueError as e:
            if "non-fixed" in str(e):
                # non-fixed frequencies are not meaningful for timedelta64;
                #  we retain that error message
                raise e
            # GH#11587 the main way this is reached is if the `np.array_equal`
            #  check above is False.  This can also be reached if index[0]
            #  is `NaT`, in which case the call to `cls._generate_range` will
            #  raise a ValueError, which we re-raise with a more targeted
            #  message.
            raise ValueError('Inferred frequency {infer} from passed values '
                             'does not conform to passed frequency {passed}'
>                            .format(infer=inferred, passed=freq.freqstr))
E           ValueError: Inferred frequency None from passed values does not conform to passed frequency M

../../pandas/core/arrays/datetimelike.py:897: ValueError
```
aospy/test/test_utils_times.py::test_ensure_time_as_index_with_change
aospy/test/test_utils_times.py::test_apply_time_offset aospy/test/test_utils_times.py::test_monthly_mean_ts_single_month aospy/test/test_utils_times.py::test_monthly_mean_ts_submonthly aospy/test/test_utils_times.py::test_monthly_mean_ts_monthly aospy/test/test_utils_times.py::test_monthly_mean_ts_na aospy/test/test_utils_times.py::test_ensure_datetime_valid_input[date0] aospy/test/test_utils_times.py::test_ensure_datetime_valid_input[date1] aospy/test/test_utils_times.py::test_ensure_datetime_valid_input[date2] aospy/test/test_utils_times.py::test_ensure_datetime_valid_input[2000-01-01] aospy/test/test_utils_times.py::test_ensure_datetime_invalid_input aospy/test/test_utils_times.py::test_datetime_or_default aospy/test/test_utils_times.py::test_month_indices aospy/test/test_utils_times.py::test_month_conditional aospy/test/test_utils_times.py::test_extract_months aospy/test/test_utils_times.py::test_extract_months_single_month aospy/test/test_utils_times.py::test_ensure_time_avg_has_cf_metadata aospy/test/test_utils_times.py::test_add_uniform_time_weights aospy/test/test_utils_times.py::test_assert_has_data_for_time aospy/test/test_utils_times.py::test_assert_has_data_for_time_cftime_datetimes[noleap-DatetimeNoLeap] aospy/test/test_utils_times.py::test_assert_has_data_for_time_cftime_datetimes[365_day-DatetimeNoLeap] aospy/test/test_utils_times.py::test_assert_has_data_for_time_cftime_datetimes[360_day-Datetime360Day] aospy/test/test_utils_times.py::test_assert_has_data_for_time_cftime_datetimes[julian-DatetimeJulian] aospy/test/test_utils_times.py::test_assert_has_data_for_time_cftime_datetimes[all_leap-DatetimeAllLeap] aospy/test/test_utils_times.py::test_assert_has_data_for_time_cftime_datetimes[366_day-DatetimeAllLeap] aospy/test/test_utils_times.py::test_assert_has_data_for_time_cftime_datetimes[gregorian-DatetimeGregorian] aospy/test/test_utils_times.py::test_assert_has_data_for_time_cftime_datetimes[proleptic_gregorian-DatetimeProlepticGregorian] aospy/test/test_utils_times.py::test_assert_has_data_for_time_str_input aospy/test/test_utils_times.py::test_assert_matching_time_coord aospy/test/test_utils_times.py::test_ensure_time_as_index_no_change aospy/test/test_utils_times.py::test_sel_time aospy/test/test_utils_times.py::test_yearly_average_no_mask aospy/test/test_utils_times.py::test_yearly_average_masked_data aospy/test/test_utils_times.py::test_average_time_bounds aospy/test/test_utils_times.py::test_infer_year[date0-2000] aospy/test/test_utils_times.py::test_infer_year[date1-2000] aospy/test/test_utils_times.py::test_infer_year[2000-2000] aospy/test/test_utils_times.py::test_infer_year[2000-01-2000] aospy/test/test_utils_times.py::test_infer_year[2000-01-01-2000] aospy/test/test_utils_times.py::test_infer_year[date5-2000] aospy/test/test_utils_times.py::test_infer_year[date6-2000] aospy/test/test_utils_times.py::test_infer_year[date7-2000] aospy/test/test_utils_times.py::test_infer_year[date8-2000] aospy/test/test_utils_times.py::test_infer_year[date9-2000] aospy/test/test_utils_times.py::test_infer_year[date10-2000] aospy/test/test_utils_times.py::test_infer_year[date11-2000] aospy/test/test_utils_times.py::test_infer_year[date12-2000] aospy/test/test_utils_times.py::test_infer_year_invalid[-0001] aospy/test/test_utils_times.py::test_infer_year_invalid[A001] aospy/test/test_utils_times.py::test_infer_year_invalid[01] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-noleap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-365_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-360_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-julian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-all_leap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-366_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-proleptic_gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-datetime.datetime] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-np.datetime64] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[DatetimeIndex-str] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[noleap]-noleap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[noleap]-365_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[noleap]-360_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[noleap]-julian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[noleap]-all_leap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[noleap]-366_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[noleap]-gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[noleap]-proleptic_gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[365_day]-noleap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[365_day]-365_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[365_day]-360_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[365_day]-julian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[365_day]-all_leap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[365_day]-366_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[365_day]-gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[365_day]-proleptic_gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[360_day]-noleap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[360_day]-365_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[360_day]-360_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[360_day]-julian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[360_day]-all_leap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[360_day]-366_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[360_day]-gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[360_day]-proleptic_gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[julian]-noleap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[julian]-365_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[julian]-360_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[julian]-julian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[julian]-all_leap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[julian]-366_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[julian]-gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[julian]-proleptic_gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[all_leap]-noleap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[all_leap]-365_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[all_leap]-360_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[all_leap]-julian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[all_leap]-all_leap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[all_leap]-366_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[all_leap]-gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[all_leap]-proleptic_gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[366_day]-noleap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[366_day]-365_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[366_day]-360_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[366_day]-julian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[366_day]-all_leap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[366_day]-366_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[366_day]-gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[366_day]-proleptic_gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[gregorian]-noleap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[gregorian]-365_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[gregorian]-360_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[gregorian]-julian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[gregorian]-all_leap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[gregorian]-366_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[gregorian]-gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[gregorian]-proleptic_gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-noleap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-365_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-360_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-julian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-all_leap] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-366_day] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-proleptic_gregorian] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-datetime.datetime] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-np.datetime64] aospy/test/test_utils_times.py::test_maybe_convert_to_index_date_type[CFTimeIndex[proleptic_gregorian]-str]