Repository: xCDAT/xcdat

Issue:
[Bug]: Out of bounds timestamp for temporal averaging
### What happened?

In trying to compute annual averages for data with non-cf-compliant time units I get an OutOfBoundsError. 

### What did you expect to happen?

I think this functionality should work with non-cf-compliant time. 

### Minimal Complete Verifiable Example

```python
import xcdat

fn = '/p/user_pub/climate_work/pochedley1/cmip6_msu/ttt_Amon_ACCESS-ESM1-5_piControl_r1i1p1f1_gr_10101-100012.nc'
ds = xcdat.open_dataset(fn)
ds_yearly = ds.temporal.group_average('tf2', freq='year', weighted=True)
```


### Relevant log output

```python
.
.
.

File ~/code/xcdat/xcdat/temporal.py:1358, in TemporalAccessor._convert_df_to_dt(self, df)
   1351     dates = [
   1352         cftime.datetime(year, month, day, hour)
   1353         for year, month, day, hour in zip(
   1354             df_new.year, df_new.month, df_new.day, df_new.hour
   1355         )
   1356     ]
   1357 else:
-> 1358     dates = pd.to_datetime(df_new)
   1360 return np.array(dates)

.
.
.

ValueError: cannot assemble the datetimes: Out of bounds nanosecond timestamp: 1010-10-01 00:00:00
```


### Anything else we need to know?

This issue does not occur with original data (with cf-compliant datetimes).

The condensed log above shows that this issue arises in the `_convert_df_to_dt` function when the dataframe is cast to datetime objects using pandas (because `year_is_unused = False`). `year_is_unused` is set to `False` because the mode is set to `average` and the `_freq` is not yearly. I don't totally understand why we use [`cftime`](https://github.com/xCDAT/xcdat/blob/main/xcdat/temporal.py#L1352) for this step for some conditions, but use [pandas](https://github.com/xCDAT/xcdat/blob/main/xcdat/temporal.py#L1358) for others.

Can we use `cftime` for all cases? I'm wondering if we started by using pandas and then used cftime for some conditions (e.g., [see the documentation](https://github.com/xCDAT/xcdat/blob/main/xcdat/temporal.py#L1316)), but in reality we may be able to simply use `cftime`. @tomvothecoder – do you remember these details well enough to know?

If I remove the `if year_is_unused` conditional and re-run the code I don't get an error. But I fail a bunch of unit tests – I'm not sure if this is a unit issue or an actual problem. 

Also note that it is possible that this issue affects other functions/functionality (but using `cftime` might resolve those potential issues). 

### Environment

main branch

Discussion:
codecov-commenter: # [Codecov](https://codecov.io/gh/xCDAT/xcdat/pull/302?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT) Report
> Merging [#302](https://codecov.io/gh/xCDAT/xcdat/pull/302?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT) (0d1352c) into [main](https://codecov.io/gh/xCDAT/xcdat/commit/87790f8b55e70cb39963303f1c2162ce1ca0ea72?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT) (87790f8) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff            @@
##              main      #302   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           14        14           
  Lines         1169      1166    -3     
=========================================
- Hits          1169      1166    -3     
```


| [Impacted Files](https://codecov.io/gh/xCDAT/xcdat/pull/302?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT) | Coverage Δ | |
|---|---|---|
| [xcdat/temporal.py](https://codecov.io/gh/xCDAT/xcdat/pull/302/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT#diff-eGNkYXQvdGVtcG9yYWwucHk=) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT)

tomvothecoder: Thanks for this PR. I already started taking a look, but will be able to review more closely next week.

We should have this merged before #300.
pochedls: 


> 

@tomvothecoder - I had a couple minor questions, but this looks good to me. 

Patch:
diff --git a/xcdat/dataset.py b/xcdat/dataset.py
index 141dec6..b778441 100644
--- a/xcdat/dataset.py
+++ b/xcdat/dataset.py
@@ -321,24 +321,32 @@ def decode_non_cf_time(dataset: xr.Dataset) -> xr.Dataset:
     """
     ds = dataset.copy()
     time = get_axis_coord(ds, "T")
-
-    try:
-        calendar = time.attrs["calendar"]
-    except KeyError:
+    time_attrs = time.attrs
+
+    # NOTE: When opening datasets with `decode_times=False`, the "calendar" and
+    # "units" attributes are stored in `.attrs` (unlike `decode_times=True`
+    # which stores them in `.encoding`). Since xCDAT manually decodes non-CF
+    # compliant time coordinates by first setting `decode_times=False`, the
+    # "calendar" and "units" attrs are popped from the `.attrs` dict and stored
+    # in the `.encoding` dict to mimic xarray's behavior.
+    calendar = time_attrs.pop("calendar", None)
+    units_attr = time_attrs.pop("units", None)
+
+    if calendar is None:
         logger.warning(
             "This dataset's time coordinates do not have a 'calendar' attribute set, "
             "so time coordinates could not be decoded. Set the 'calendar' attribute "
-            "and try decoding the time coordinates again."
+            f"(`ds.{time.name}.attrs['calendar]`) and try decoding the time "
+            "coordinates again."
         )
         return ds
 
-    try:
-        units_attr = time.attrs["units"]
-    except KeyError:
+    if units_attr is None:
         logger.warning(
             "This dataset's time coordinates do not have a 'units' attribute set, "
             "so the time coordinates could not be decoded. Set the 'units' attribute "
-            "and try decoding the time coordinates again."
+            f"(`ds.{time.name}.attrs['units']`) and try decoding the time "
+            "coordinates again."
         )
         return ds
 
@@ -346,9 +354,9 @@ def decode_non_cf_time(dataset: xr.Dataset) -> xr.Dataset:
         units, ref_date = _split_time_units_attr(units_attr)
     except ValueError:
         logger.warning(
-            f"This dataset's 'units' attribute ('{units_attr}') is not in a "
-            "supported format ('months since...' or 'years since...'), so the time "
-            "coordinates could not be decoded."
+            f"This dataset's time coordinates 'units' attribute ('{units_attr}') is "
+            "not in a supported format ('months since...' or 'years since...'), so the "
+            "time coordinates could not be decoded."
         )
         return ds
 
@@ -358,12 +366,16 @@ def decode_non_cf_time(dataset: xr.Dataset) -> xr.Dataset:
         data=data,
         dims=time.dims,
         coords={time.name: data},
-        attrs=time.attrs,
+        # As mentioned in a comment above, the units and calendar attributes are
+        # popped from the `.attrs` dict.
+        attrs=time_attrs,
     )
     decoded_time.encoding = {
         "source": ds.encoding.get("source", "None"),
         "dtype": time.dtype,
         "original_shape": time.shape,
+        # The units and calendar attributes are now saved in the `.encoding`
+        # dict.
         "units": units_attr,
         "calendar": calendar,
     }
diff --git a/xcdat/temporal.py b/xcdat/temporal.py
index 0b4fee7..9e9579b 100644
--- a/xcdat/temporal.py
+++ b/xcdat/temporal.py
@@ -3,11 +3,11 @@ from itertools import chain
 from typing import Dict, List, Literal, Optional, Tuple, TypedDict, get_args
 
 import cf_xarray  # noqa: F401
-import cftime
 import numpy as np
 import pandas as pd
 import xarray as xr
 from dask.array.core import Array
+from xarray.coding.cftime_offsets import get_date_type
 from xarray.core.groupby import DataArrayGroupBy
 
 from xcdat import bounds  # noqa: F401
@@ -150,6 +150,17 @@ class TemporalAccessor:
 
         self._time_bounds = self._dataset.bounds.get_bounds("T").copy()
 
+        try:
+            self.calendar = self._dataset[self._dim].encoding["calendar"]
+            self.date_type = get_date_type(self.calendar)
+        except KeyError:
+            raise KeyError(
+                "This dataset's time coordinates do not have a 'calendar' encoding "
+                "attribute set, which might indicate that the time coordinates were not "
+                "decoded to datetime objects. Ensure that the time coordinates are "
+                "decoded before performing temporal averaging operations."
+            )
+
     def average(self, data_var: str, weighted: bool = True, keep_weights: bool = False):
         """
         Returns a Dataset with the average of a data variable and the time
@@ -1306,17 +1317,15 @@ class TemporalAccessor:
         return df_season
 
     def _convert_df_to_dt(self, df: pd.DataFrame) -> np.ndarray:
-        """Converts a DataFrame of datetime components to datetime objects.
+        """
+        Converts a DataFrame of datetime components to cftime datetime
+        objects.
 
         datetime objects require at least a year, month, and day value. However,
         some modes and time frequencies don't require year, month, and/or day
         for grouping. For these cases, use default values of 1 in order to
         meet this datetime requirement.
 
-        If the default value of 1 is used for the years, datetime objects
-        must be created using `cftime.datetime` because year 1 is outside the
-        Timestamp-valid range.
-
         Parameters
         ----------
         df : pd.DataFrame
@@ -1325,11 +1334,12 @@ class TemporalAccessor:
         Returns
         -------
         np.ndarray
-            A numpy ndarray of datetime.datetime or cftime.datetime objects.
+            A numpy ndarray of cftime.datetime objects.
 
         Notes
         -----
         Refer to [3]_ and [4]_ for more information on Timestamp-valid range.
+        We use cftime.datetime objects to avoid these time range issues.
 
         References
         ----------
@@ -1344,18 +1354,12 @@ class TemporalAccessor:
             if component not in df_new.columns:
                 df_new[component] = default_val
 
-        year_is_unused = self._mode in ["climatology", "departures"] or (
-            self._mode == "average" and self._freq != "year"
-        )
-        if year_is_unused:
-            dates = [
-                cftime.datetime(year, month, day, hour)
-                for year, month, day, hour in zip(
-                    df_new.year, df_new.month, df_new.day, df_new.hour
-                )
-            ]
-        else:
-            dates = pd.to_datetime(df_new)
+        dates = [
+            self.date_type(year, month, day, hour)
+            for year, month, day, hour in zip(
+                df_new.year, df_new.month, df_new.day, df_new.hour
+            )
+        ]
 
         return np.array(dates)
 


Test patch:
diff --git a/tests/fixtures.py b/tests/fixtures.py
index d79fb05..6ac3c51 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -41,17 +41,26 @@ time_cf = xr.DataArray(
         "standard_name": "time",
     },
 )
+# NOTE: With `decode_times=True`, the "calendar" and "units" attributes are
+# stored in `.encoding`.
+time_cf.encoding["calendar"] = "standard"
+time_cf.encoding["units"] = "days since 2000-01-01"
+
+
+# NOTE: With `decode_times=False`, the "calendar" and "units" attributes are
+# stored in `.attrs`.
 time_non_cf = xr.DataArray(
     data=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
     dims=["time"],
     attrs={
-        "units": "months since 2000-01-01",
-        "calendar": "standard",
         "axis": "T",
         "long_name": "time",
         "standard_name": "time",
+        "calendar": "standard",
+        "units": "months since 2000-01-01",
     },
 )
+
 time_non_cf_unsupported = xr.DataArray(
     data=np.arange(1850 + 1 / 24.0, 1851 + 3 / 12.0, 1 / 12.0),
     dims=["time"],
diff --git a/tests/test_dataset.py b/tests/test_dataset.py
index 81d8582..571bb33 100644
--- a/tests/test_dataset.py
+++ b/tests/test_dataset.py
@@ -64,22 +64,53 @@ class TestOpenDataset:
             name="time",
             data=np.array(
                 [
-                    cftime.datetime(2000, 1, 1),
-                    cftime.datetime(2000, 2, 1),
-                    cftime.datetime(2000, 3, 1),
-                    cftime.datetime(2000, 4, 1),
-                    cftime.datetime(2000, 5, 1),
-                    cftime.datetime(2000, 6, 1),
-                    cftime.datetime(2000, 7, 1),
-                    cftime.datetime(2000, 8, 1),
-                    cftime.datetime(2000, 9, 1),
-                    cftime.datetime(2000, 10, 1),
-                    cftime.datetime(2000, 11, 1),
-                    cftime.datetime(2000, 12, 1),
-                    cftime.datetime(2001, 1, 1),
-                    cftime.datetime(2001, 2, 1),
-                    cftime.datetime(2001, 3, 1),
+                    cftime.DatetimeGregorian(
+                        2000, 1, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 2, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 3, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 4, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 5, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 6, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 7, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 8, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 9, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 10, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 11, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 12, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2001, 1, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2001, 2, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2001, 3, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
                 ],
+                dtype="object",
             ),
             dims="time",
         )
@@ -88,53 +119,133 @@ class TestOpenDataset:
             data=np.array(
                 [
                     [
-                        cftime.datetime(1999, 12, 16, 12),
-                        cftime.datetime(2000, 1, 16, 12),
+                        cftime.DatetimeGregorian(
+                            1999, 12, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 1, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 1, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 2, 15, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 2, 15, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 3, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 3, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 4, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 4, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 5, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 5, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 6, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 6, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 7, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 7, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 8, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 1, 16, 12),
-                        cftime.datetime(2000, 2, 15, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 8, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 9, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 2, 15, 12),
-                        cftime.datetime(2000, 3, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 9, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 10, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
-                    [cftime.datetime(2000, 3, 16, 12), cftime.datetime(2000, 4, 16, 0)],
-                    [cftime.datetime(2000, 4, 16, 0), cftime.datetime(2000, 5, 16, 12)],
-                    [cftime.datetime(2000, 5, 16, 12), cftime.datetime(2000, 6, 16, 0)],
-                    [cftime.datetime(2000, 6, 16, 0), cftime.datetime(2000, 7, 16, 12)],
                     [
-                        cftime.datetime(2000, 7, 16, 12),
-                        cftime.datetime(2000, 8, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 10, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 11, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
-                    [cftime.datetime(2000, 8, 16, 12), cftime.datetime(2000, 9, 16, 0)],
                     [
-                        cftime.datetime(2000, 9, 16, 0),
-                        cftime.datetime(2000, 10, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 11, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 12, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 10, 16, 12),
-                        cftime.datetime(2000, 11, 16, 0),
+                        cftime.DatetimeGregorian(
+                            2000, 12, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2001, 1, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 11, 16, 0),
-                        cftime.datetime(2000, 12, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2001, 1, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2001, 2, 15, 0, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 12, 16, 12),
-                        cftime.datetime(2001, 1, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2001, 2, 15, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2001, 3, 15, 0, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
-                    [cftime.datetime(2001, 1, 16, 12), cftime.datetime(2001, 2, 15, 0)],
-                    [cftime.datetime(2001, 2, 15, 0), cftime.datetime(2001, 3, 15, 0)],
-                ]
+                ],
+                dtype="object",
             ),
             dims=["time", "bnds"],
             attrs={"xcdat_bounds": "True"},
         )
 
         expected.time.attrs = {
-            "units": "months since 2000-01-01",
-            "calendar": "standard",
             "axis": "T",
             "long_name": "time",
             "standard_name": "time",
@@ -218,27 +329,57 @@ class TestOpenMfDataset:
 
         # Generate an expected dataset with decoded non-CF compliant time units.
         expected = generate_dataset(cf_compliant=True, has_bounds=True)
-
         expected["time"] = xr.DataArray(
             name="time",
             data=np.array(
                 [
-                    cftime.datetime(2000, 1, 1),
-                    cftime.datetime(2000, 2, 1),
-                    cftime.datetime(2000, 3, 1),
-                    cftime.datetime(2000, 4, 1),
-                    cftime.datetime(2000, 5, 1),
-                    cftime.datetime(2000, 6, 1),
-                    cftime.datetime(2000, 7, 1),
-                    cftime.datetime(2000, 8, 1),
-                    cftime.datetime(2000, 9, 1),
-                    cftime.datetime(2000, 10, 1),
-                    cftime.datetime(2000, 11, 1),
-                    cftime.datetime(2000, 12, 1),
-                    cftime.datetime(2001, 1, 1),
-                    cftime.datetime(2001, 2, 1),
-                    cftime.datetime(2001, 3, 1),
+                    cftime.DatetimeGregorian(
+                        2000, 1, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 2, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 3, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 4, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 5, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 6, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 7, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 8, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 9, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 10, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 11, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2000, 12, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2001, 1, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2001, 2, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
+                    cftime.DatetimeGregorian(
+                        2001, 3, 1, 0, 0, 0, 0, has_year_zero=False
+                    ),
                 ],
+                dtype="object",
             ),
             dims="time",
         )
@@ -247,53 +388,133 @@ class TestOpenMfDataset:
             data=np.array(
                 [
                     [
-                        cftime.datetime(1999, 12, 16, 12),
-                        cftime.datetime(2000, 1, 16, 12),
+                        cftime.DatetimeGregorian(
+                            1999, 12, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 1, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 1, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 2, 15, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 2, 15, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 3, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 1, 16, 12),
-                        cftime.datetime(2000, 2, 15, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 3, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 4, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 2, 15, 12),
-                        cftime.datetime(2000, 3, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 4, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 5, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
-                    [cftime.datetime(2000, 3, 16, 12), cftime.datetime(2000, 4, 16, 0)],
-                    [cftime.datetime(2000, 4, 16, 0), cftime.datetime(2000, 5, 16, 12)],
-                    [cftime.datetime(2000, 5, 16, 12), cftime.datetime(2000, 6, 16, 0)],
-                    [cftime.datetime(2000, 6, 16, 0), cftime.datetime(2000, 7, 16, 12)],
                     [
-                        cftime.datetime(2000, 7, 16, 12),
-                        cftime.datetime(2000, 8, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 5, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 6, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
-                    [cftime.datetime(2000, 8, 16, 12), cftime.datetime(2000, 9, 16, 0)],
                     [
-                        cftime.datetime(2000, 9, 16, 0),
-                        cftime.datetime(2000, 10, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 6, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 7, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 10, 16, 12),
-                        cftime.datetime(2000, 11, 16, 0),
+                        cftime.DatetimeGregorian(
+                            2000, 7, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 8, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 11, 16, 0),
-                        cftime.datetime(2000, 12, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 8, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 9, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
                     [
-                        cftime.datetime(2000, 12, 16, 12),
-                        cftime.datetime(2001, 1, 16, 12),
+                        cftime.DatetimeGregorian(
+                            2000, 9, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 10, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
                     ],
-                    [cftime.datetime(2001, 1, 16, 12), cftime.datetime(2001, 2, 15, 0)],
-                    [cftime.datetime(2001, 2, 15, 0), cftime.datetime(2001, 3, 15, 0)],
-                ]
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 10, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 11, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 11, 16, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2000, 12, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2000, 12, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2001, 1, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2001, 1, 16, 12, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2001, 2, 15, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                    [
+                        cftime.DatetimeGregorian(
+                            2001, 2, 15, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                        cftime.DatetimeGregorian(
+                            2001, 3, 15, 0, 0, 0, 0, has_year_zero=False
+                        ),
+                    ],
+                ],
+                dtype="object",
             ),
             dims=["time", "bnds"],
             attrs={"xcdat_bounds": "True"},
         )
 
         expected.time.attrs = {
-            "units": "months since 2000-01-01",
-            "calendar": "standard",
             "axis": "T",
             "long_name": "time",
             "standard_name": "time",
@@ -439,7 +660,7 @@ class TestDecodeNonCFTimeUnits:
                 "axis": "T",
                 "long_name": "time",
                 "standard_name": "time",
-                # calendar attr is specified by test.
+                # calendar attr and units is specified by test.
             },
         )
         time_bnds = xr.DataArray(
@@ -508,31 +729,56 @@ class TestDecodeNonCFTimeUnits:
                     name="time",
                     data=np.array(
                         [
-                            cftime.datetime(2000, 2, 1, calendar=calendar),
-                            cftime.datetime(2000, 3, 1, calendar=calendar),
-                            cftime.datetime(2000, 4, 1, calendar=calendar),
+                            cftime.DatetimeGregorian(
+                                2000, 2, 1, 0, 0, 0, 0, has_year_zero=False
+                            ),
+                            cftime.DatetimeGregorian(
+                                2000, 3, 1, 0, 0, 0, 0, has_year_zero=False
+                            ),
+                            cftime.DatetimeGregorian(
+                                2000, 4, 1, 0, 0, 0, 0, has_year_zero=False
+                            ),
                         ],
+                        dtype="object",
                     ),
                     dims=["time"],
-                    attrs=ds.time.attrs,
+                    attrs={
+                        "bounds": "time_bnds",
+                        "axis": "T",
+                        "long_name": "time",
+                        "standard_name": "time",
+                    },
                 ),
                 "time_bnds": xr.DataArray(
                     name="time_bnds",
                     data=np.array(
                         [
                             [
-                                cftime.datetime(2000, 1, 1, calendar=calendar),
-                                cftime.datetime(2000, 2, 1, calendar=calendar),
+                                cftime.DatetimeGregorian(
+                                    2000, 1, 1, 0, 0, 0, 0, has_year_zero=False
+                                ),
+                                cftime.DatetimeGregorian(
+                                    2000, 2, 1, 0, 0, 0, 0, has_year_zero=False
+                                ),
                             ],
                             [
-                                cftime.datetime(2000, 2, 1, calendar=calendar),
-                                cftime.datetime(2000, 3, 1, calendar=calendar),
+                                cftime.DatetimeGregorian(
+                                    2000, 2, 1, 0, 0, 0, 0, has_year_zero=False
+                                ),
+                                cftime.DatetimeGregorian(
+                                    2000, 3, 1, 0, 0, 0, 0, has_year_zero=False
+                                ),
                             ],
                             [
-                                cftime.datetime(2000, 3, 1, calendar=calendar),
-                                cftime.datetime(2000, 4, 1, calendar=calendar),
+                                cftime.DatetimeGregorian(
+                                    2000, 3, 1, 0, 0, 0, 0, has_year_zero=False
+                                ),
+                                cftime.DatetimeGregorian(
+                                    2000, 4, 1, 0, 0, 0, 0, has_year_zero=False
+                                ),
                             ],
                         ],
+                        dtype="object",
                     ),
                     dims=["time", "bnds"],
                     attrs=ds.time_bnds.attrs,
@@ -571,7 +817,12 @@ class TestDecodeNonCFTimeUnits:
                         ],
                     ),
                     dims=["time"],
-                    attrs=ds.time.attrs,
+                    attrs={
+                        "bounds": "time_bnds",
+                        "axis": "T",
+                        "long_name": "time",
+                        "standard_name": "time",
+                    },
                 ),
                 "time_bnds": xr.DataArray(
                     name="time_bnds",
@@ -628,7 +879,12 @@ class TestDecodeNonCFTimeUnits:
                         ],
                     ),
                     dims=["time"],
-                    attrs=ds.time.attrs,
+                    attrs={
+                        "bounds": "time_bnds",
+                        "axis": "T",
+                        "long_name": "time",
+                        "standard_name": "time",
+                    },
                 ),
                 "time_bnds": xr.DataArray(
                     name="time_bnds",
@@ -686,7 +942,12 @@ class TestDecodeNonCFTimeUnits:
                         ],
                     ),
                     dims=["time"],
-                    attrs=ds.time.attrs,
+                    attrs={
+                        "bounds": "time_bnds",
+                        "axis": "T",
+                        "long_name": "time",
+                        "standard_name": "time",
+                    },
                 ),
                 "time_bnds": xr.DataArray(
                     name="time_bnds",
@@ -745,7 +1006,12 @@ class TestDecodeNonCFTimeUnits:
                         ],
                     ),
                     dims=["time"],
-                    attrs=ds.time.attrs,
+                    attrs={
+                        "bounds": "time_bnds",
+                        "axis": "T",
+                        "long_name": "time",
+                        "standard_name": "time",
+                    },
                 ),
                 "time_bnds": xr.DataArray(
                     name="time_bnds",
@@ -804,7 +1070,12 @@ class TestDecodeNonCFTimeUnits:
                         ],
                     ),
                     dims=["time"],
-                    attrs=ds.time.attrs,
+                    attrs={
+                        "bounds": "time_bnds",
+                        "axis": "T",
+                        "long_name": "time",
+                        "standard_name": "time",
+                    },
                 ),
                 "time_bnds": xr.DataArray(
                     name="time_bnds",
@@ -1110,8 +1381,6 @@ class Test_PreProcessNonCFDataset:
             dims=["time", "bnds"],
         )
         expected.time.attrs = {
-            "units": "months since 2000-01-01",
-            "calendar": "standard",
             "axis": "T",
             "long_name": "time",
             "standard_name": "time",
diff --git a/tests/test_temporal.py b/tests/test_temporal.py
index af09c47..67a5fce 100644
--- a/tests/test_temporal.py
+++ b/tests/test_temporal.py
@@ -19,6 +19,13 @@ class TestTemporalAccessor:
         obj = ds.temporal
         assert obj._dataset.identical(ds)
 
+    def test_raises_error_if_calendar_encoding_attr_is_not_set(self):
+        ds: xr.Dataset = generate_dataset(cf_compliant=True, has_bounds=True)
+        ds.time.encoding = {}
+
+        with pytest.raises(KeyError):
+            TemporalAccessor(ds)
+
 
 class TestAverage:
     def test_averages_for_yearly_time_series(self):
@@ -47,6 +54,7 @@ class TestAverage:
                 ),
             }
         )
+        ds.time.encoding = {"calendar": "standard"}
         ds["time_bnds"] = xr.DataArray(
             name="time_bnds",
             data=np.array(
@@ -132,6 +140,8 @@ class TestAverage:
                 ),
             }
         )
+        ds.time.encoding = {"calendar": "standard"}
+
         ds["time_bnds"] = xr.DataArray(
             name="time_bnds",
             data=np.array(
@@ -215,6 +225,8 @@ class TestAverage:
                 ),
             }
         )
+        ds.time.encoding = {"calendar": "standard"}
+
         ds["time_bnds"] = xr.DataArray(
             name="time_bnds",
             data=np.array(
@@ -298,6 +310,8 @@ class TestAverage:
                 ),
             }
         )
+        ds.time.encoding = {"calendar": "standard"}
+
         ds["time_bnds"] = xr.DataArray(
             name="time_bnds",
             data=np.array(
@@ -374,6 +388,7 @@ class TestGroupAverage:
             dims=["time"],
             attrs={"axis": "T", "long_name": "time", "standard_name": "time"},
         )
+        time.encoding = {"calendar": "standard"}
         time_bnds = xr.DataArray(
             name="time_bnds",
             data=np.array(
@@ -420,18 +435,16 @@ class TestGroupAverage:
                 "time": xr.DataArray(
                     data=np.array(
                         [
-                            "2000-01-01T00:00:00.000000000",
-                            "2001-01-01T00:00:00.000000000",
+                            cftime.DatetimeGregorian(2000, 1, 1),
+                            cftime.DatetimeGregorian(2001, 1, 1),
                         ],
-                        dtype="datetime64[ns]",
                     ),
                     coords={
                         "time": np.array(
                             [
-                                "2000-01-01T00:00:00.000000000",
-                                "2001-01-01T00:00:00.000000000",
+                                cftime.DatetimeGregorian(2000, 1, 1),
+                                cftime.DatetimeGregorian(2001, 1, 1),
                             ],
-                            dtype="datetime64[ns]",
                         )
                     },
                     dims=["time"],
@@ -471,18 +484,16 @@ class TestGroupAverage:
                 "time": xr.DataArray(
                     data=np.array(
                         [
-                            "2000-01-01T00:00:00.000000000",
-                            "2001-01-01T00:00:00.000000000",
+                            cftime.DatetimeGregorian(2000, 1, 1),
+                            cftime.DatetimeGregorian(2001, 1, 1),
                         ],
-                        dtype="datetime64[ns]",
                     ),
                     coords={
                         "time": np.array(
                             [
-                                "2000-01-01T00:00:00.000000000",
-                                "2001-01-01T00:00:00.000000000",
+                                cftime.DatetimeGregorian(2000, 1, 1),
+                                cftime.DatetimeGregorian(2001, 1, 1),
                             ],
-                            dtype="datetime64[ns]",
                         )
                     },
                     dims=["time"],
@@ -527,12 +538,11 @@ class TestGroupAverage:
                 "time": xr.DataArray(
                     data=np.array(
                         [
-                            "2000-04-01T00:00:00.000000000",
-                            "2000-07-01T00:00:00.000000000",
-                            "2000-10-01T00:00:00.000000000",
-                            "2001-01-01T00:00:00.000000000",
+                            cftime.DatetimeGregorian(2000, 4, 1),
+                            cftime.DatetimeGregorian(2000, 7, 1),
+                            cftime.DatetimeGregorian(2000, 10, 1),
+                            cftime.DatetimeGregorian(2001, 1, 1),
                         ],
-                        dtype="datetime64[ns]",
                     ),
                     dims=["time"],
                     attrs={
@@ -577,13 +587,12 @@ class TestGroupAverage:
                 "time": xr.DataArray(
                     data=np.array(
                         [
-                            "2000-01-01T00:00:00.000000000",
-                            "2000-04-01T00:00:00.000000000",
-                            "2000-07-01T00:00:00.000000000",
-                            "2000-10-01T00:00:00.000000000",
-                            "2001-01-01T00:00:00.000000000",
+                            cftime.DatetimeGregorian(2000, 1, 1),
+                            cftime.DatetimeGregorian(2000, 4, 1),
+                            cftime.DatetimeGregorian(2000, 7, 1),
+                            cftime.DatetimeGregorian(2000, 10, 1),
+                            cftime.DatetimeGregorian(2001, 1, 1),
                         ],
-                        dtype="datetime64[ns]",
                     ),
                     dims=["time"],
                     attrs={
@@ -626,24 +635,22 @@ class TestGroupAverage:
                 "time": xr.DataArray(
                     data=np.array(
                         [
-                            "2000-01-01T00:00:00.000000000",
-                            "2000-04-01T00:00:00.000000000",
-                            "2000-07-01T00:00:00.000000000",
-                            "2000-10-01T00:00:00.000000000",
-                            "2001-01-01T00:00:00.000000000",
+                            cftime.DatetimeGregorian(2000, 1, 1),
+                            cftime.DatetimeGregorian(2000, 4, 1),
+                            cftime.DatetimeGregorian(2000, 7, 1),
+                            cftime.DatetimeGregorian(2000, 10, 1),
+                            cftime.DatetimeGregorian(2001, 1, 1),
                         ],
-                        dtype="datetime64[ns]",
                     ),
                     coords={
                         "time": np.array(
                             [
-                                "2000-01-01T00:00:00.000000000",
-                                "2000-04-01T00:00:00.000000000",
-                                "2000-07-01T00:00:00.000000000",
-                                "2000-10-01T00:00:00.000000000",
-                                "2001-01-01T00:00:00.000000000",
+                                cftime.DatetimeGregorian(2000, 1, 1),
+                                cftime.DatetimeGregorian(2000, 4, 1),
+                                cftime.DatetimeGregorian(2000, 7, 1),
+                                cftime.DatetimeGregorian(2000, 10, 1),
+                                cftime.DatetimeGregorian(2001, 1, 1),
                             ],
-                            dtype="datetime64[ns]",
                         )
                     },
                     dims=["time"],
@@ -692,12 +699,11 @@ class TestGroupAverage:
                 "time": xr.DataArray(
                     data=np.array(
                         [
-                            "2000-02-01T00:00:00.000000000",
-                            "2000-05-01T00:00:00.000000000",
-                            "2000-08-01T00:00:00.000000000",
-                            "2001-02-01T00:00:00.000000000",
+                            cftime.DatetimeGregorian(2000, 2, 1),
+                            cftime.DatetimeGregorian(2000, 5, 1),
+                            cftime.DatetimeGregorian(2000, 8, 1),
+                            cftime.DatetimeGregorian(2001, 2, 1),
                         ],
-                        dtype="datetime64[ns]",
                     ),
                     dims=["time"],
                     attrs={
@@ -783,13 +789,12 @@ class TestGroupAverage:
                 "time": xr.DataArray(
                     data=np.array(
                         [
-                            "2000-01-01T00:00:00.000000000",
-                            "2000-03-01T00:00:00.000000000",
-                            "2000-06-01T00:00:00.000000000",
-                            "2000-09-01T00:00:00.000000000",
-                            "2001-02-01T00:00:00.000000000",
+                            cftime.DatetimeGregorian(2000, 1, 1),
+                            cftime.DatetimeGregorian(2000, 3, 1),
+                            cftime.DatetimeGregorian(2000, 6, 1),
+                            cftime.DatetimeGregorian(2000, 9, 1),
+                            cftime.DatetimeGregorian(2001, 2, 1),
                         ],
-                        dtype="datetime64[ns]",
                     ),
                     dims=["time"],
                     attrs={
@@ -833,13 +838,12 @@ class TestGroupAverage:
                 "time": xr.DataArray(
                     data=np.array(
                         [
-                            "2000-01-01T00:00:00.000000000",
-                            "2000-03-01T00:00:00.000000000",
-                            "2000-06-01T00:00:00.000000000",
-                            "2000-09-01T00:00:00.000000000",
-                            "2001-02-01T00:00:00.000000000",
+                            cftime.DatetimeGregorian(2000, 1, 1),
+                            cftime.DatetimeGregorian(2000, 3, 1),
+                            cftime.DatetimeGregorian(2000, 6, 1),
+                            cftime.DatetimeGregorian(2000, 9, 1),
+                            cftime.DatetimeGregorian(2001, 2, 1),
                         ],
-                        dtype="datetime64[ns]",
                     ),
                     dims=["time"],
                     attrs={
@@ -876,13 +880,12 @@ class TestGroupAverage:
                 "time": xr.DataArray(
                     data=np.array(
                         [
-                            "2000-01-16T00:00:00.000000000",
-                            "2000-03-16T00:00:00.000000000",
-                            "2000-06-16T00:00:00.000000000",
-                            "2000-09-16T00:00:00.000000000",
-                            "2001-02-15T00:00:00.000000000",
+                            cftime.DatetimeGregorian(2000, 1, 16),
+                            cftime.DatetimeGregorian(2000, 3, 16),
+                            cftime.DatetimeGregorian(2000, 6, 16),
+                            cftime.DatetimeGregorian(2000, 9, 16),
+                            cftime.DatetimeGregorian(2001, 2, 15),
                         ],
-                        dtype="datetime64[ns]",
                     ),
                     dims=["time"],
                     attrs={
@@ -917,7 +920,24 @@ class TestGroupAverage:
             coords={
                 "lat": expected.lat,
                 "lon": expected.lon,
-                "time": ds.time,
+                "time": xr.DataArray(
+                    data=np.array(
+                        [
+                            cftime.DatetimeGregorian(2000, 1, 16, 12),
+                            cftime.DatetimeGregorian(2000, 3, 16, 12),
+                            cftime.DatetimeGregorian(2000, 6, 16, 0),
+                            cftime.DatetimeGregorian(2000, 9, 16, 0),
+                            cftime.DatetimeGregorian(2001, 2, 15, 12),
+                        ],
+                    ),
+                    dims=["time"],
+                    attrs={
+                        "axis": "T",
+                        "long_name": "time",
+                        "standard_name": "time",
+                        "bounds": "time_bnds",
+                    },
+                ),
             },
             dims=["time", "lat", "lon"],
             attrs={
@@ -952,19 +972,19 @@ class TestClimatology:
         expected_time = xr.DataArray(
             data=np.array(
                 [
-                    cftime.datetime(1, 1, 1),
-                    cftime.datetime(1, 4, 1),
-                    cftime.datetime(1, 7, 1),
-                    cftime.datetime(1, 10, 1),
+                    cftime.DatetimeGregorian(1, 1, 1),
+                    cftime.DatetimeGregorian(1, 4, 1),
+                    cftime.DatetimeGregorian(1, 7, 1),
+                    cftime.DatetimeGregorian(1, 10, 1),
                 ],
             ),
             coords={
                 "time": np.array(
                     [
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
                     ],
                 ),
             },
@@ -1007,19 +1027,19 @@ class TestClimatology:
         expected_time = xr.DataArray(
             data=np.array(
                 [
-                    cftime.datetime(1, 1, 1),
-                    cftime.datetime(1, 4, 1),
-                    cftime.datetime(1, 7, 1),
-                    cftime.datetime(1, 10, 1),
+                    cftime.DatetimeGregorian(1, 1, 1),
+                    cftime.DatetimeGregorian(1, 4, 1),
+                    cftime.DatetimeGregorian(1, 7, 1),
+                    cftime.DatetimeGregorian(1, 10, 1),
                 ],
             ),
             coords={
                 "time": np.array(
                     [
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
                     ],
                 ),
             },
@@ -1059,19 +1079,19 @@ class TestClimatology:
         expected_time = xr.DataArray(
             data=np.array(
                 [
-                    cftime.datetime(1, 1, 1),
-                    cftime.datetime(1, 4, 1),
-                    cftime.datetime(1, 7, 1),
-                    cftime.datetime(1, 10, 1),
+                    cftime.DatetimeGregorian(1, 1, 1),
+                    cftime.DatetimeGregorian(1, 4, 1),
+                    cftime.DatetimeGregorian(1, 7, 1),
+                    cftime.DatetimeGregorian(1, 10, 1),
                 ],
             ),
             coords={
                 "time": np.array(
                     [
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
                     ],
                 ),
             },
@@ -1116,19 +1136,19 @@ class TestClimatology:
         expected_time = xr.DataArray(
             data=np.array(
                 [
-                    cftime.datetime(1, 2, 1),
-                    cftime.datetime(1, 5, 1),
-                    cftime.datetime(1, 8, 1),
-                    cftime.datetime(1, 11, 1),
+                    cftime.DatetimeGregorian(1, 2, 1),
+                    cftime.DatetimeGregorian(1, 5, 1),
+                    cftime.DatetimeGregorian(1, 8, 1),
+                    cftime.DatetimeGregorian(1, 11, 1),
                 ],
             ),
             coords={
                 "time": np.array(
                     [
-                        cftime.datetime(1, 2, 1),
-                        cftime.datetime(1, 5, 1),
-                        cftime.datetime(1, 8, 1),
-                        cftime.datetime(1, 11, 1),
+                        cftime.DatetimeGregorian(1, 2, 1),
+                        cftime.DatetimeGregorian(1, 5, 1),
+                        cftime.DatetimeGregorian(1, 8, 1),
+                        cftime.DatetimeGregorian(1, 11, 1),
                     ],
                 ),
             },
@@ -1169,35 +1189,35 @@ class TestClimatology:
         expected_time = xr.DataArray(
             data=np.array(
                 [
-                    cftime.datetime(1, 1, 1),
-                    cftime.datetime(1, 2, 1),
-                    cftime.datetime(1, 3, 1),
-                    cftime.datetime(1, 4, 1),
-                    cftime.datetime(1, 5, 1),
-                    cftime.datetime(1, 6, 1),
-                    cftime.datetime(1, 7, 1),
-                    cftime.datetime(1, 8, 1),
-                    cftime.datetime(1, 9, 1),
-                    cftime.datetime(1, 10, 1),
-                    cftime.datetime(1, 11, 1),
-                    cftime.datetime(1, 12, 1),
+                    cftime.DatetimeGregorian(1, 1, 1),
+                    cftime.DatetimeGregorian(1, 2, 1),
+                    cftime.DatetimeGregorian(1, 3, 1),
+                    cftime.DatetimeGregorian(1, 4, 1),
+                    cftime.DatetimeGregorian(1, 5, 1),
+                    cftime.DatetimeGregorian(1, 6, 1),
+                    cftime.DatetimeGregorian(1, 7, 1),
+                    cftime.DatetimeGregorian(1, 8, 1),
+                    cftime.DatetimeGregorian(1, 9, 1),
+                    cftime.DatetimeGregorian(1, 10, 1),
+                    cftime.DatetimeGregorian(1, 11, 1),
+                    cftime.DatetimeGregorian(1, 12, 1),
                 ],
             ),
             coords={
                 "time": np.array(
                     [
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 2, 1),
-                        cftime.datetime(1, 3, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 5, 1),
-                        cftime.datetime(1, 6, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 8, 1),
-                        cftime.datetime(1, 9, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 11, 1),
-                        cftime.datetime(1, 12, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 2, 1),
+                        cftime.DatetimeGregorian(1, 3, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 5, 1),
+                        cftime.DatetimeGregorian(1, 6, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 8, 1),
+                        cftime.DatetimeGregorian(1, 9, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 11, 1),
+                        cftime.DatetimeGregorian(1, 12, 1),
                     ],
                 ),
             },
@@ -1232,35 +1252,35 @@ class TestClimatology:
         expected_time = xr.DataArray(
             data=np.array(
                 [
-                    cftime.datetime(1, 1, 1),
-                    cftime.datetime(1, 2, 1),
-                    cftime.datetime(1, 3, 1),
-                    cftime.datetime(1, 4, 1),
-                    cftime.datetime(1, 5, 1),
-                    cftime.datetime(1, 6, 1),
-                    cftime.datetime(1, 7, 1),
-                    cftime.datetime(1, 8, 1),
-                    cftime.datetime(1, 9, 1),
-                    cftime.datetime(1, 10, 1),
-                    cftime.datetime(1, 11, 1),
-                    cftime.datetime(1, 12, 1),
+                    cftime.DatetimeGregorian(1, 1, 1),
+                    cftime.DatetimeGregorian(1, 2, 1),
+                    cftime.DatetimeGregorian(1, 3, 1),
+                    cftime.DatetimeGregorian(1, 4, 1),
+                    cftime.DatetimeGregorian(1, 5, 1),
+                    cftime.DatetimeGregorian(1, 6, 1),
+                    cftime.DatetimeGregorian(1, 7, 1),
+                    cftime.DatetimeGregorian(1, 8, 1),
+                    cftime.DatetimeGregorian(1, 9, 1),
+                    cftime.DatetimeGregorian(1, 10, 1),
+                    cftime.DatetimeGregorian(1, 11, 1),
+                    cftime.DatetimeGregorian(1, 12, 1),
                 ],
             ),
             coords={
                 "time": np.array(
                     [
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 2, 1),
-                        cftime.datetime(1, 3, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 5, 1),
-                        cftime.datetime(1, 6, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 8, 1),
-                        cftime.datetime(1, 9, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 11, 1),
-                        cftime.datetime(1, 12, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 2, 1),
+                        cftime.DatetimeGregorian(1, 3, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 5, 1),
+                        cftime.DatetimeGregorian(1, 6, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 8, 1),
+                        cftime.DatetimeGregorian(1, 9, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 11, 1),
+                        cftime.DatetimeGregorian(1, 12, 1),
                     ],
                 ),
             },
@@ -1294,35 +1314,35 @@ class TestClimatology:
         expected_time = xr.DataArray(
             data=np.array(
                 [
-                    cftime.datetime(1, 1, 16),
-                    cftime.datetime(1, 2, 15),
-                    cftime.datetime(1, 3, 16),
-                    cftime.datetime(1, 4, 16),
-                    cftime.datetime(1, 5, 16),
-                    cftime.datetime(1, 6, 16),
-                    cftime.datetime(1, 7, 16),
-                    cftime.datetime(1, 8, 16),
-                    cftime.datetime(1, 9, 16),
-                    cftime.datetime(1, 10, 16),
-                    cftime.datetime(1, 11, 16),
-                    cftime.datetime(1, 12, 16),
+                    cftime.DatetimeGregorian(1, 1, 16),
+                    cftime.DatetimeGregorian(1, 2, 15),
+                    cftime.DatetimeGregorian(1, 3, 16),
+                    cftime.DatetimeGregorian(1, 4, 16),
+                    cftime.DatetimeGregorian(1, 5, 16),
+                    cftime.DatetimeGregorian(1, 6, 16),
+                    cftime.DatetimeGregorian(1, 7, 16),
+                    cftime.DatetimeGregorian(1, 8, 16),
+                    cftime.DatetimeGregorian(1, 9, 16),
+                    cftime.DatetimeGregorian(1, 10, 16),
+                    cftime.DatetimeGregorian(1, 11, 16),
+                    cftime.DatetimeGregorian(1, 12, 16),
                 ],
             ),
             coords={
                 "time": np.array(
                     [
-                        cftime.datetime(1, 1, 16),
-                        cftime.datetime(1, 2, 15),
-                        cftime.datetime(1, 3, 16),
-                        cftime.datetime(1, 4, 16),
-                        cftime.datetime(1, 5, 16),
-                        cftime.datetime(1, 6, 16),
-                        cftime.datetime(1, 7, 16),
-                        cftime.datetime(1, 8, 16),
-                        cftime.datetime(1, 9, 16),
-                        cftime.datetime(1, 10, 16),
-                        cftime.datetime(1, 11, 16),
-                        cftime.datetime(1, 12, 16),
+                        cftime.DatetimeGregorian(1, 1, 16),
+                        cftime.DatetimeGregorian(1, 2, 15),
+                        cftime.DatetimeGregorian(1, 3, 16),
+                        cftime.DatetimeGregorian(1, 4, 16),
+                        cftime.DatetimeGregorian(1, 5, 16),
+                        cftime.DatetimeGregorian(1, 6, 16),
+                        cftime.DatetimeGregorian(1, 7, 16),
+                        cftime.DatetimeGregorian(1, 8, 16),
+                        cftime.DatetimeGregorian(1, 9, 16),
+                        cftime.DatetimeGregorian(1, 10, 16),
+                        cftime.DatetimeGregorian(1, 11, 16),
+                        cftime.DatetimeGregorian(1, 12, 16),
                     ],
                 ),
             },
@@ -1356,35 +1376,35 @@ class TestClimatology:
         expected_time = xr.DataArray(
             data=np.array(
                 [
-                    cftime.datetime(1, 1, 16),
-                    cftime.datetime(1, 2, 15),
-                    cftime.datetime(1, 3, 16),
-                    cftime.datetime(1, 4, 16),
-                    cftime.datetime(1, 5, 16),
-                    cftime.datetime(1, 6, 16),
-                    cftime.datetime(1, 7, 16),
-                    cftime.datetime(1, 8, 16),
-                    cftime.datetime(1, 9, 16),
-                    cftime.datetime(1, 10, 16),
-                    cftime.datetime(1, 11, 16),
-                    cftime.datetime(1, 12, 16),
+                    cftime.DatetimeGregorian(1, 1, 16),
+                    cftime.DatetimeGregorian(1, 2, 15),
+                    cftime.DatetimeGregorian(1, 3, 16),
+                    cftime.DatetimeGregorian(1, 4, 16),
+                    cftime.DatetimeGregorian(1, 5, 16),
+                    cftime.DatetimeGregorian(1, 6, 16),
+                    cftime.DatetimeGregorian(1, 7, 16),
+                    cftime.DatetimeGregorian(1, 8, 16),
+                    cftime.DatetimeGregorian(1, 9, 16),
+                    cftime.DatetimeGregorian(1, 10, 16),
+                    cftime.DatetimeGregorian(1, 11, 16),
+                    cftime.DatetimeGregorian(1, 12, 16),
                 ],
             ),
             coords={
                 "time": np.array(
                     [
-                        cftime.datetime(1, 1, 16),
-                        cftime.datetime(1, 2, 15),
-                        cftime.datetime(1, 3, 16),
-                        cftime.datetime(1, 4, 16),
-                        cftime.datetime(1, 5, 16),
-                        cftime.datetime(1, 6, 16),
-                        cftime.datetime(1, 7, 16),
-                        cftime.datetime(1, 8, 16),
-                        cftime.datetime(1, 9, 16),
-                        cftime.datetime(1, 10, 16),
-                        cftime.datetime(1, 11, 16),
-                        cftime.datetime(1, 12, 16),
+                        cftime.DatetimeGregorian(1, 1, 16),
+                        cftime.DatetimeGregorian(1, 2, 15),
+                        cftime.DatetimeGregorian(1, 3, 16),
+                        cftime.DatetimeGregorian(1, 4, 16),
+                        cftime.DatetimeGregorian(1, 5, 16),
+                        cftime.DatetimeGregorian(1, 6, 16),
+                        cftime.DatetimeGregorian(1, 7, 16),
+                        cftime.DatetimeGregorian(1, 8, 16),
+                        cftime.DatetimeGregorian(1, 9, 16),
+                        cftime.DatetimeGregorian(1, 10, 16),
+                        cftime.DatetimeGregorian(1, 11, 16),
+                        cftime.DatetimeGregorian(1, 12, 16),
                     ],
                 ),
             },
@@ -1652,21 +1672,21 @@ class Test_GetWeights:
                 name="month",
                 data=np.array(
                     [
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 2, 1),
-                        cftime.datetime(1, 3, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 5, 1),
-                        cftime.datetime(1, 6, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 8, 1),
-                        cftime.datetime(1, 9, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 11, 1),
-                        cftime.datetime(1, 12, 1),
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 2, 1),
-                        cftime.datetime(1, 12, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 2, 1),
+                        cftime.DatetimeGregorian(1, 3, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 5, 1),
+                        cftime.DatetimeGregorian(1, 6, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 8, 1),
+                        cftime.DatetimeGregorian(1, 9, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 11, 1),
+                        cftime.DatetimeGregorian(1, 12, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 2, 1),
+                        cftime.DatetimeGregorian(1, 12, 1),
                     ],
                 ),
                 coords={"time": ds.time},
@@ -1846,6 +1866,7 @@ class Test_GetWeights:
                     "bounds": "time_bnds",
                 },
             )
+            ds.time.encoding = {"calendar": "standard"}
             ds["ts"] = xr.DataArray(
                 name="ts",
                 data=np.ones((12, 4, 4)),
@@ -2242,6 +2263,7 @@ class Test_GetWeights:
                     "bounds": "time_bnds",
                 },
             )
+            ds.time.encoding = {"calendar": "standard"}
             ds["ts"] = xr.DataArray(
                 name="ts",
                 data=np.ones((12, 4, 4)),
@@ -2319,18 +2341,18 @@ class Test_GetWeights:
                 name="season",
                 data=np.array(
                     [
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
                     ],
                 ),
                 coords={"time": ds.time},
@@ -2375,21 +2397,21 @@ class Test_GetWeights:
                 name="season",
                 data=np.array(
                     [
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
                     ],
                 ),
                 coords={"time": ds.time},
@@ -2438,21 +2460,21 @@ class Test_GetWeights:
                 name="month",
                 data=np.array(
                     [
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 2, 1),
-                        cftime.datetime(1, 3, 1),
-                        cftime.datetime(1, 4, 1),
-                        cftime.datetime(1, 5, 1),
-                        cftime.datetime(1, 6, 1),
-                        cftime.datetime(1, 7, 1),
-                        cftime.datetime(1, 8, 1),
-                        cftime.datetime(1, 9, 1),
-                        cftime.datetime(1, 10, 1),
-                        cftime.datetime(1, 11, 1),
-                        cftime.datetime(1, 12, 1),
-                        cftime.datetime(1, 1, 1),
-                        cftime.datetime(1, 2, 1),
-                        cftime.datetime(1, 12, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 2, 1),
+                        cftime.DatetimeGregorian(1, 3, 1),
+                        cftime.DatetimeGregorian(1, 4, 1),
+                        cftime.DatetimeGregorian(1, 5, 1),
+                        cftime.DatetimeGregorian(1, 6, 1),
+                        cftime.DatetimeGregorian(1, 7, 1),
+                        cftime.DatetimeGregorian(1, 8, 1),
+                        cftime.DatetimeGregorian(1, 9, 1),
+                        cftime.DatetimeGregorian(1, 10, 1),
+                        cftime.DatetimeGregorian(1, 11, 1),
+                        cftime.DatetimeGregorian(1, 12, 1),
+                        cftime.DatetimeGregorian(1, 1, 1),
+                        cftime.DatetimeGregorian(1, 2, 1),
+                        cftime.DatetimeGregorian(1, 12, 1),
                     ],
                 ),
                 coords={"time": ds.time},
@@ -2500,21 +2522,21 @@ class Test_GetWeights:
                 name="month_day",
                 data=np.array(
                     [
-                        cftime.datetime(1, 1, 16),
-                        cftime.datetime(1, 2, 15),
-                        cftime.datetime(1, 3, 16),
-                        cftime.datetime(1, 4, 16),
-                        cftime.datetime(1, 5, 6),
-                        cftime.datetime(1, 6, 16),
-                        cftime.datetime(1, 7, 16),
-                        cftime.datetime(1, 8, 16),
-                        cftime.datetime(1, 9, 16),
-                        cftime.datetime(1, 10, 16),
-                        cftime.datetime(1, 11, 16),
-                        cftime.datetime(1, 12, 16),
-                        cftime.datetime(1, 1, 16),
-                        cftime.datetime(1, 2, 15),
-                        cftime.datetime(1, 12, 16),
+                        cftime.DatetimeGregorian(1, 1, 16),
+                        cftime.DatetimeGregorian(1, 2, 15),
+                        cftime.DatetimeGregorian(1, 3, 16),
+                        cftime.DatetimeGregorian(1, 4, 16),
+                        cftime.DatetimeGregorian(1, 5, 6),
+                        cftime.DatetimeGregorian(1, 6, 16),
+                        cftime.DatetimeGregorian(1, 7, 16),
+                        cftime.DatetimeGregorian(1, 8, 16),
+                        cftime.DatetimeGregorian(1, 9, 16),
+                        cftime.DatetimeGregorian(1, 10, 16),
+                        cftime.DatetimeGregorian(1, 11, 16),
+                        cftime.DatetimeGregorian(1, 12, 16),
+                        cftime.DatetimeGregorian(1, 1, 16),
+                        cftime.DatetimeGregorian(1, 2, 15),
+                        cftime.DatetimeGregorian(1, 12, 16),
                     ],
                 ),
                 coords={"time": ds.time},