Repository: pydata/xarray

Issue:
Cannot plot datetime.date dimension
### What happened?

I noticed that xarray doesnt support plotting when the x-axis is a `datetime.date`. In my case, I would like to plot hourly data aggregated by date. I know that in this particular case, I could just use `.resample('1D')` to achieve the same result and be able to plot it but I am wondering whether xarray shouldn't just also support plotting dates. 

I am pretty sure that matplotlib supports date on the x-axis so maybe adding it to an acceptable type in *plot/utils.py* L675 in `_ensure_plottable` would already do the trick?

I am happy to look into this if this is a wanted feature.

### What did you expect to happen?

_No response_

### Minimal Complete Verifiable Example

```Python
import xarray as xr
import numpy as np
import datetime 
start = datetime.datetime(2024, 1,1)
time = [start + datetime.timedelta(hours=x) for x in range(720)]

data = xr.DataArray(np.random.randn(len(time)), coords=dict(time=('time', time)))
data.groupby('time.date').mean().plot()
```


### MVCE confirmation

- [X] Minimal example — the example is as focused as reasonably possible to demonstrate the underlying issue in xarray.
- [X] Complete example — the example is self-contained, including all data and the text of any traceback.
- [X] Verifiable example — the example copy & pastes into an IPython prompt or [Binder notebook](https://mybinder.org/v2/gh/pydata/xarray/main?urlpath=lab/tree/doc/examples/blank_template.ipynb), returning the result.
- [X] New issue — a search of GitHub Issues suggests this is not a duplicate.
- [ ] Recent environment — the issue occurs with the latest version of xarray and its dependencies.

### Relevant log output

```Python
TypeError: Plotting requires coordinates to be numeric, boolean, or dates of type numpy.datetime64, datetime.datetime, cftime.datetime or pandas.Interval. Received data of type object instead.
```


### Anything else we need to know?

_No response_

### Environment

<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.10.13 (main, Aug 24 2023, 12:59:26) [Clang 15.0.0 (clang-1500.1.0.2.5)]
python-bits: 64
OS: Darwin
OS-release: 22.1.0
machine: arm64
processor: arm
byteorder: little
LC_ALL: None
LANG: None
LOCALE: (None, 'UTF-8')
libhdf5: 1.12.2
libnetcdf: 4.9.3-development

xarray: 2023.12.0
pandas: 2.1.4
numpy: 1.26.3
scipy: 1.12.0
netCDF4: 1.6.5
pydap: None
h5netcdf: None
h5py: None
Nio: None
zarr: 2.16.1
cftime: 1.6.3
nc_time_axis: 1.4.1
iris: None
bottleneck: 1.3.7
dask: 2024.1.1
distributed: None
matplotlib: 3.8.2
cartopy: None
seaborn: None
numbagg: None
fsspec: 2023.12.2
cupy: None
pint: None
sparse: None
flox: None
numpy_groupies: None
setuptools: 69.1.0
pip: 24.0
conda: None
pytest: None
mypy: None
IPython: 8.21.0
sphinx: None


</details>


Discussion:
welcome[bot]: Thank you for opening this pull request! It may take us a few days to respond here, so thank you for being patient.
If you have questions, some answers may be found in our [contributing guidelines](http://docs.xarray.dev/en/stable/contributing.html).

Illviljan: Could you add a test in https://github.com/pydata/xarray/blob/main/xarray/tests/test_plot.py so we don't regress?
saschahofmann: Sure! Shall I just check whether it plots ? Something like this `test_datetime_dimension` in L612 just with a date dimension?

saschahofmann: I added the `test_date_dimension` plot, I saw that there is a whole `TestDatetimePlot` class. Should I add an equivalent or even refactor it so I only need to change the self.darray? Or is it fine with a single test?
saschahofmann: Quick question: Would it be worth it ungitignore your .vscode folder?

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index e8ce0cff..ca9a2b37 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -37,6 +37,8 @@ New Features
 - Allow creating :py:class:`xr.Coordinates` objects with no indexes (:pull:`8711`)
   By `Benoit Bovy <https://github.com/benbovy>`_ and `Tom Nicholas
   <https://github.com/TomNicholas>`_.
+- Enable plotting of ``datetime.dates``. (:issue:`8866`, :pull:`8873`)
+  By `Sascha Hofmann <https://github.com/saschahofmann>`_.
 
 Breaking changes
 ~~~~~~~~~~~~~~~~
@@ -73,6 +75,9 @@ Bug fixes
 - Warn and return bytes undecoded in case of UnicodeDecodeError in h5netcdf-backend
   (:issue:`5563`, :pull:`8874`).
   By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
+- Fix bug incorrectly disallowing creation of a dataset with a multidimensional coordinate variable with the same name as one of its dims.
+  (:issue:`8884`, :pull:`8886`)
+  By `Tom Nicholas <https://github.com/TomNicholas>`_.
 
 
 Documentation
diff --git a/xarray/backends/zarr.py b/xarray/backends/zarr.py
index 13b1819f..b4369fa7 100644
--- a/xarray/backends/zarr.py
+++ b/xarray/backends/zarr.py
@@ -623,7 +623,12 @@ class ZarrStore(AbstractWritableDataStore):
             # avoid needing to load index variables into memory.
             # TODO: consider making loading indexes lazy again?
             existing_vars, _, _ = conventions.decode_cf_variables(
-                self.get_variables(), self.get_attrs()
+                {
+                    k: v
+                    for k, v in self.get_variables().items()
+                    if k in existing_variable_names
+                },
+                self.get_attrs(),
             )
             # Modified variables must use the same encoding as the store.
             vars_with_encoding = {}
diff --git a/xarray/core/merge.py b/xarray/core/merge.py
index cbd06c8f..a90e59e7 100644
--- a/xarray/core/merge.py
+++ b/xarray/core/merge.py
@@ -562,25 +562,6 @@ def merge_coords(
     return variables, out_indexes
 
 
-def assert_valid_explicit_coords(
-    variables: Mapping[Any, Any],
-    dims: Mapping[Any, int],
-    explicit_coords: Iterable[Hashable],
-) -> None:
-    """Validate explicit coordinate names/dims.
-
-    Raise a MergeError if an explicit coord shares a name with a dimension
-    but is comprised of arbitrary dimensions.
-    """
-    for coord_name in explicit_coords:
-        if coord_name in dims and variables[coord_name].dims != (coord_name,):
-            raise MergeError(
-                f"coordinate {coord_name} shares a name with a dataset dimension, but is "
-                "not a 1D variable along that dimension. This is disallowed "
-                "by the xarray data model."
-            )
-
-
 def merge_attrs(variable_attrs, combine_attrs, context=None):
     """Combine attributes from different variables according to combine_attrs"""
     if not variable_attrs:
@@ -728,7 +709,6 @@ def merge_core(
         # coordinates may be dropped in merged results
         coord_names.intersection_update(variables)
     if explicit_coords is not None:
-        assert_valid_explicit_coords(variables, dims, explicit_coords)
         coord_names.update(explicit_coords)
     for dim, size in dims.items():
         if dim in variables:
diff --git a/xarray/plot/utils.py b/xarray/plot/utils.py
index 804e1cfd..8789bc2f 100644
--- a/xarray/plot/utils.py
+++ b/xarray/plot/utils.py
@@ -4,7 +4,7 @@ import itertools
 import textwrap
 import warnings
 from collections.abc import Hashable, Iterable, Mapping, MutableMapping, Sequence
-from datetime import datetime
+from datetime import date, datetime
 from inspect import getfullargspec
 from typing import TYPE_CHECKING, Any, Callable, Literal, overload
 
@@ -672,7 +672,7 @@ def _ensure_plottable(*args) -> None:
         np.bool_,
         np.str_,
     )
-    other_types: tuple[type[object], ...] = (datetime,)
+    other_types: tuple[type[object], ...] = (datetime, date)
     cftime_datetime_types: tuple[type[object], ...] = (
         () if cftime is None else (cftime.datetime,)
     )


Test patch:
diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py
index 07573066..248c873d 100644
--- a/xarray/tests/test_backends.py
+++ b/xarray/tests/test_backends.py
@@ -2261,7 +2261,6 @@ class ZarrBase(CFEncodedBase):
         original = create_test_data().chunk({"dim1": 3, "dim2": 4, "dim3": 3})
         with self.roundtrip(original, open_kwargs={"chunks": {}}) as actual:
             for k, v in actual.data_vars.items():
-                print(k)
                 assert v.chunks == actual[k].chunks
 
     def test_chunk_encoding(self) -> None:
@@ -2468,6 +2467,27 @@ class ZarrBase(CFEncodedBase):
         ) as actual:
             assert_identical(original, actual)
 
+    def test_zarr_mode_w_overwrites_encoding(self) -> None:
+        import zarr
+
+        data = Dataset({"foo": ("x", [1.0, 1.0, 1.0])})
+        with self.create_zarr_target() as store:
+            data.to_zarr(
+                store, **self.version_kwargs, encoding={"foo": {"add_offset": 1}}
+            )
+            np.testing.assert_equal(
+                zarr.open_group(store, **self.version_kwargs)["foo"], data.foo.data - 1
+            )
+            data.to_zarr(
+                store,
+                **self.version_kwargs,
+                encoding={"foo": {"add_offset": 0}},
+                mode="w",
+            )
+            np.testing.assert_equal(
+                zarr.open_group(store, **self.version_kwargs)["foo"], data.foo.data
+            )
+
     def test_encoding_kwarg_fixed_width_string(self) -> None:
         # not relevant for zarr, since we don't use EncodedStringCoder
         pass
diff --git a/xarray/tests/test_coordinates.py b/xarray/tests/test_coordinates.py
index 40743194..f88e554d 100644
--- a/xarray/tests/test_coordinates.py
+++ b/xarray/tests/test_coordinates.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import numpy as np
 import pandas as pd
 import pytest
 
@@ -8,7 +9,7 @@ from xarray.core.coordinates import Coordinates
 from xarray.core.dataarray import DataArray
 from xarray.core.dataset import Dataset
 from xarray.core.indexes import PandasIndex, PandasMultiIndex
-from xarray.core.variable import IndexVariable
+from xarray.core.variable import IndexVariable, Variable
 from xarray.tests import assert_identical, source_ndarray
 
 
@@ -174,3 +175,10 @@ class TestCoordinates:
         left2, right2 = align(left, right, join="override")
         assert_identical(left2, left)
         assert_identical(left2, right2)
+
+    def test_dataset_from_coords_with_multidim_var_same_name(self):
+        # regression test for GH #8883
+        var = Variable(data=np.arange(6).reshape(2, 3), dims=["x", "y"])
+        coords = Coordinates(coords={"x": var}, indexes={})
+        ds = Dataset(coords=coords)
+        assert ds.coords["x"].dims == ("x", "y")
diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py
index 39c404d0..e2a64964 100644
--- a/xarray/tests/test_dataset.py
+++ b/xarray/tests/test_dataset.py
@@ -486,14 +486,6 @@ class TestDataset:
         actual = Dataset({"z": expected["z"]})
         assert_identical(expected, actual)
 
-    def test_constructor_invalid_dims(self) -> None:
-        # regression for GH1120
-        with pytest.raises(MergeError):
-            Dataset(
-                data_vars=dict(v=("y", [1, 2, 3, 4])),
-                coords=dict(y=DataArray([0.1, 0.2, 0.3, 0.4], dims="x")),
-            )
-
     def test_constructor_1d(self) -> None:
         expected = Dataset({"x": (["x"], 5.0 + np.arange(5))})
         actual = Dataset({"x": 5.0 + np.arange(5)})
diff --git a/xarray/tests/test_plot.py b/xarray/tests/test_plot.py
index 6f983a12..8da1bba6 100644
--- a/xarray/tests/test_plot.py
+++ b/xarray/tests/test_plot.py
@@ -5,7 +5,7 @@ import inspect
 import math
 from collections.abc import Hashable
 from copy import copy
-from datetime import datetime
+from datetime import date, datetime, timedelta
 from typing import Any, Callable, Literal
 
 import numpy as np
@@ -620,6 +620,18 @@ class TestPlot(PlotTestCase):
         ax = plt.gca()
         assert ax.has_data()
 
+    def test_date_dimension(self) -> None:
+        nrow = 3
+        ncol = 4
+        start = date(2000, 1, 1)
+        time = [start + timedelta(days=i) for i in range(nrow)]
+        a = DataArray(
+            easy_array((nrow, ncol)), coords=[("time", time), ("y", range(ncol))]
+        )
+        a.plot()
+        ax = plt.gca()
+        assert ax.has_data()
+
     @pytest.mark.slow
     @pytest.mark.filterwarnings("ignore:tight_layout cannot")
     def test_convenient_facetgrid(self) -> None: