Repository: pydata/xarray

Issue:
CFTimeIndex improperly handles string slice for length-1 indexes
#### Code Sample, a copy-pastable example if possible

```
In [1]: import xarray as xr

In [2]: import cftime

In [3]: index = xr.CFTimeIndex([cftime.DatetimeNoLeap(1, 1, 1)])

In [4]: da = xr.DataArray([1], coords=[index], dims=['time'])

In [5]: da.sel(time=slice('0001', '0001'))
Out[5]:
<xarray.DataArray (time: 0)>
array([], dtype=int64)
Coordinates:
  * time     (time) object
```
#### Problem description

When a `CFTimeIndex` is created with a single element, slicing using strings does not work; the example above should behave analogously to as it does when using a `DatetimeIndex`:
```
In [9]: import pandas as pd

In [10]: index = pd.DatetimeIndex(['2000-01-01'])

In [11]: da = xr.DataArray([1], coords=[index], dims=['time'])

In [12]: da.sel(time=slice('2000', '2000'))
Out[12]:
<xarray.DataArray (time: 1)>
array([1])
Coordinates:
  * time     (time) datetime64[ns] 2000-01-01
```
I have a fix for this, which I will push shortly.

#### Expected Output

```
In [5]: da.sel(time=slice('0001', '0001'))
Out[5]:
<xarray.DataArray (time: 1)>
array([1])
Coordinates:
  * time     (time) object 0001-01-01 00:00:00
```
#### Output of ``xr.show_versions()``

<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.6.1.final.0
python-bits: 64
OS: Darwin
OS-release: 17.4.0
machine: x86_64
processor: i386
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

xarray: 0.10.4
pandas: 0.20.2
numpy: 1.13.1
scipy: 0.19.1
netCDF4: 1.4.0
h5netcdf: 0.5.1
h5py: 2.8.0
Nio: None
zarr: None
bottleneck: 1.2.0
cyordereddict: None
dask: 0.15.0
distributed: 1.17.1
matplotlib: 2.0.2
cartopy: None
seaborn: None
setuptools: 33.1.1.post20170320
pip: 9.0.1
conda: None
pytest: 3.1.2
IPython: 6.1.0
sphinx: None
</details>


Discussion:

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 4c9a1415..d9f43fa1 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -59,6 +59,12 @@ Bug fixes
   dimension were improperly skipped.
   By `Stephan Hoyer <https://github.com/shoyer>`_
 
+- Selecting data indexed by a length-1 ``CFTimeIndex`` with a slice of strings
+  now behaves as it does when using a length-1 ``DatetimeIndex`` (i.e. it no
+  longer falsely returns an empty array when the slice includes the value in
+  the index) (:issue:`2165`).
+  By `Spencer Clark <https://github.com/spencerkclark>`_.
+  
 .. _whats-new.0.10.4:
 
 v0.10.4 (May 16, 2018)
diff --git a/xarray/coding/cftimeindex.py b/xarray/coding/cftimeindex.py
index 5fca14dd..eb8cae2f 100644
--- a/xarray/coding/cftimeindex.py
+++ b/xarray/coding/cftimeindex.py
@@ -225,7 +225,7 @@ class CFTimeIndex(pd.Index):
                                                           label)
             start, end = _parsed_string_to_bounds(self.date_type, resolution,
                                                   parsed)
-            if self.is_monotonic_decreasing and len(self):
+            if self.is_monotonic_decreasing and len(self) > 1:
                 return end if side == 'left' else start
             return start if side == 'left' else end
         else:


Test patch:
diff --git a/xarray/tests/test_cftimeindex.py b/xarray/tests/test_cftimeindex.py
index c78ac038..6f102b60 100644
--- a/xarray/tests/test_cftimeindex.py
+++ b/xarray/tests/test_cftimeindex.py
@@ -79,6 +79,12 @@ def monotonic_decreasing_index(date_type):
     return CFTimeIndex(dates)
 
 
+@pytest.fixture
+def length_one_index(date_type):
+    dates = [date_type(1, 1, 1)]
+    return CFTimeIndex(dates)
+
+
 @pytest.fixture
 def da(index):
     return xr.DataArray([1, 2, 3, 4], coords=[index],
@@ -280,6 +286,36 @@ def test_get_slice_bound_decreasing_index(
     assert result == expected
 
 
+@pytest.mark.skipif(not has_cftime, reason='cftime not installed')
+@pytest.mark.parametrize('kind', ['loc', 'getitem'])
+def test_get_slice_bound_length_one_index(
+        date_type, length_one_index, kind):
+    result = length_one_index.get_slice_bound('0001', 'left', kind)
+    expected = 0
+    assert result == expected
+
+    result = length_one_index.get_slice_bound('0001', 'right', kind)
+    expected = 1
+    assert result == expected
+
+    result = length_one_index.get_slice_bound(
+        date_type(1, 3, 1), 'left', kind)
+    expected = 1
+    assert result == expected
+
+    result = length_one_index.get_slice_bound(
+        date_type(1, 3, 1), 'right', kind)
+    expected = 1
+    assert result == expected
+
+
+@pytest.mark.skipif(not has_cftime, reason='cftime not installed')
+def test_string_slice_length_one_index(length_one_index):
+    da = xr.DataArray([1], coords=[length_one_index], dims=['time'])
+    result = da.sel(time=slice('0001', '0001'))
+    assert_identical(result, da)
+
+
 @pytest.mark.skipif(not has_cftime, reason='cftime not installed')
 def test_date_type_property(date_type, index):
     assert index.date_type is date_type