Repository: pydata/xarray

Issue:
Expose use_cftime option in open_zarr
`use_cftime` was recently added as an option to `decode_cf` and `open_dataset` to give users a little more control over how times are decoded (#2759).  It would be good if it was also available for `open_zarr`.  This perhaps doesn't have quite the importance, because `open_zarr` only works for single data stores, so there is no risk of decoding times to different types (e.g. as there was for `open_mfdataset`, #1263); however, it would still be nice to be able to silence serialization warnings that result from decoding times to cftime objects in some instances, e.g. #2754. 

Discussion:
andersy005: @Geektrovert, thank you for working on this! I added rebase your fork to upstream master and added some tests locally. Do you mind giving me permissions so that I can push to your folk so as to bring this PR to completion?
Geektrovert: @andersy005 sure! I will add you as a collaborator to my fork. Thank you for working on this!
andersy005: > LGTM. This just needs a `whats-new` entry

Done! I don't know why the readthedocs build failed though
keewis: this seems to be a general issue with RTD (there are quite a few open issues about this already) and has to be fixed by them.
dcherian: Thanks @Geektrovert & @andersy005. This is a great improvement.

@Geektrovert I see this is your first PR. Thanks! and welcome to xarray.

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 1f15ad99..478db357 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -62,6 +62,8 @@ New Features
   now accept a ``dim_order`` parameter allowing to specify the resulting dataframe's
   dimensions order (:issue:`4331`, :pull:`4333`).
   By `Thomas Zilio <https://github.com/thomas-z>`_.
+- Expose ``use_cftime`` option in :py:func:`~xarray.open_zarr` (:issue:`2886`, :pull:`3229`)
+  By `Samnan Rahee <https://github.com/Geektrovert>`_ and `Anderson Banihirwe <https://github.com/andersy005>`_.
 
 
 Bug fixes
diff --git a/xarray/backends/zarr.py b/xarray/backends/zarr.py
index 89ba97b3..f74fddb6 100644
--- a/xarray/backends/zarr.py
+++ b/xarray/backends/zarr.py
@@ -510,6 +510,7 @@ def open_zarr(
     overwrite_encoded_chunks=False,
     chunk_store=None,
     decode_timedelta=None,
+    use_cftime=None,
     **kwargs,
 ):
     """Load and decode a dataset from a Zarr store.
@@ -576,6 +577,16 @@ def open_zarr(
         {'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds'}
         into timedelta objects. If False, leave them encoded as numbers.
         If None (default), assume the same value of decode_time.
+    use_cftime: bool, optional
+        Only relevant if encoded dates come from a standard calendar
+        (e.g. "gregorian", "proleptic_gregorian", "standard", or not
+        specified).  If None (default), attempt to decode times to
+        ``np.datetime64[ns]`` objects; if this is not possible, decode times to
+        ``cftime.datetime`` objects. If True, always decode times to
+        ``cftime.datetime`` objects, regardless of whether or not they can be
+        represented using ``np.datetime64[ns]`` objects.  If False, always
+        decode times to ``np.datetime64[ns]`` objects; if this is not possible
+        raise an error.
 
     Returns
     -------
@@ -637,6 +648,7 @@ def open_zarr(
             decode_coords=decode_coords,
             drop_variables=drop_variables,
             decode_timedelta=decode_timedelta,
+            use_cftime=use_cftime,
         )
 
         # TODO: this is where we would apply caching


Test patch:
diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py
index 1173fed0..fe93f5a9 100644
--- a/xarray/tests/test_backends.py
+++ b/xarray/tests/test_backends.py
@@ -2025,6 +2025,16 @@ class ZarrBase(CFEncodedBase):
         ) as ds1:
             assert_equal(ds1, original)
 
+    @requires_cftime
+    def test_open_zarr_use_cftime(self):
+        ds = create_test_data()
+        with self.create_zarr_target() as store_target:
+            ds.to_zarr(store_target, consolidated=True)
+            ds_a = xr.open_zarr(store_target, consolidated=True)
+            assert_identical(ds, ds_a)
+            ds_b = xr.open_zarr(store_target, consolidated=True, use_cftime=True)
+            assert xr.coding.times.contains_cftime_datetimes(ds_b.time)
+
 
 @requires_zarr
 class TestZarrDictStore(ZarrBase):