Repository: pandas-dev/pandas

Issue:
Assignment of column via .loc
```python
# This code is failing
df = pd.DataFrame({'timestamp': [np.datetime64('2017-01-01 01:00:00'), np.datetime64('2017-01-01 02:00:00')],
                   'value': ['a', 'b']})
df.loc[:, 'day'] = df.loc[:, 'timestamp'].values.astype('datetime64[D]')
```
#### Error
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-91-7e5c52b6ed28> in <module>()
----> 1 df.loc[:, 'day'] = df.loc[:, 'timestamp'].values.astype('datetime64[D]')

/home/homola/env/venv/local/lib/python2.7/site-packages/pandas/core/indexing.pyc in __setitem__(self, key, value)
    188             key = com.apply_if_callable(key, self.obj)
    189         indexer = self._get_setitem_indexer(key)
--> 190         self._setitem_with_indexer(indexer, value)
    191 
    192     def _validate_key(self, key, axis):

/home/homola/env/venv/local/lib/python2.7/site-packages/pandas/core/indexing.pyc in _setitem_with_indexer(self, indexer, value)
    360 
    361                         # add a new item with the dtype setup
--> 362                         self.obj[key] = _infer_fill_value(value)
    363 
    364                         new_indexer = convert_from_missing_indexer_tuple(

/home/homola/env/venv/local/lib/python2.7/site-packages/pandas/core/frame.pyc in __setitem__(self, key, value)
   3368         else:
   3369             # set column
-> 3370             self._set_item(key, value)
   3371 
   3372     def _setitem_slice(self, key, value):

/home/homola/env/venv/local/lib/python2.7/site-packages/pandas/core/frame.pyc in _set_item(self, key, value)
   3443 
   3444         self._ensure_valid_index(value)
-> 3445         value = self._sanitize_column(key, value)
   3446         NDFrame._set_item(self, key, value)
   3447 

/home/homola/env/venv/local/lib/python2.7/site-packages/pandas/core/frame.pyc in _sanitize_column(self, key, value, broadcast)
   3652             # upcast
   3653             value = cast_scalar_to_array(len(self.index), value)
-> 3654             value = maybe_cast_to_datetime(value, infer_dtype)
   3655 
   3656         # return internal types directly

/home/homola/env/venv/local/lib/python2.7/site-packages/pandas/core/dtypes/cast.pyc in maybe_cast_to_datetime(value, dtype, errors)
    979                 else:
    980                     raise TypeError("cannot convert datetimelike to "
--> 981                                     "dtype [{dtype}]".format(dtype=dtype))
    982             elif is_datetime64tz:
    983 

TypeError: cannot convert datetimelike to dtype [datetime64[D]]
```

```python
# This code is ok
df = pd.DataFrame({'timestamp': [np.datetime64('2017-01-01 01:00:00'), np.datetime64('2017-01-01 02:00:00')],
                   'value': ['a', 'b']})
df['day'] = df.loc[:, 'timestamp'].values.astype('datetime64[D]')
```

#### Problem description

When assigning to a column and using pandas.DataFrame.loc, it fails due to 'TypeError: cannot convert datetimelike to dtype [datetime64[D]]'. When using simple assignment, it doesn't fail. 
If it is a bad practice what is done above in the first example, the error message should be different.

#### Expected Outcome
Assignment is successful for both below.
```python
df.loc[:, 'day'] = df.loc[:, 'timestamp'].values.astype('datetime64[D]')
df['day'] = df.loc[:, 'timestamp'].values.astype('datetime64[D]')
```

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 2.7.12.final.0
python-bits: 64
OS: Linux
OS-release: 4.15.0-51-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: en_US.utf8
LANG: en_US.UTF-8
LOCALE: None.None

pandas: 0.24.2
numpy: 1.16.4
...
</details>



Discussion:
Looks like a bug. Interested in debugging where things go wrong?
May I take a look?

I'm on sprint at PyCon KR 2019

Resolving patch:
diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py
--- a/pandas/core/dtypes/cast.py
+++ b/pandas/core/dtypes/cast.py
@@ -1029,7 +1029,10 @@ def maybe_cast_to_datetime(value, dtype, errors="raise"):
             )
 
             if is_datetime64 and not is_dtype_equal(dtype, _NS_DTYPE):
-                if dtype.name in ("datetime64", "datetime64[ns]"):
+
+                # pandas supports dtype whose granularity is less than [ns]
+                # e.g., [ps], [fs], [as]
+                if dtype <= np.dtype("M8[ns]"):
                     if dtype.name == "datetime64":
                         raise ValueError(msg.format(dtype=dtype.name))
                     dtype = _NS_DTYPE
@@ -1047,7 +1050,10 @@ def maybe_cast_to_datetime(value, dtype, errors="raise"):
                     value = [value]
 
             elif is_timedelta64 and not is_dtype_equal(dtype, _TD_DTYPE):
-                if dtype.name in ("timedelta64", "timedelta64[ns]"):
+
+                # pandas supports dtype whose granularity is less than [ns]
+                # e.g., [ps], [fs], [as]
+                if dtype <= np.dtype("m8[ns]"):
                     if dtype.name == "timedelta64":
                         raise ValueError(msg.format(dtype=dtype.name))
                     dtype = _TD_DTYPE


Test patch:
diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py
--- a/pandas/tests/indexing/test_loc.py
+++ b/pandas/tests/indexing/test_loc.py
@@ -690,6 +690,28 @@ def test_loc_setitem_consistency_slice_column_len(self):
         )
         tm.assert_series_equal(df[("Respondent", "Duration")], expected)
 
+    @pytest.mark.parametrize("unit", ["Y", "M", "D", "h", "m", "s", "ms", "us"])
+    def test_loc_assign_non_ns_datetime(self, unit):
+        # GH 27395, non-ns dtype assignment via .loc should work
+        # and return the same result when using simple assignment
+        df = DataFrame(
+            {
+                "timestamp": [
+                    np.datetime64("2017-02-11 12:41:29"),
+                    np.datetime64("1991-11-07 04:22:37"),
+                ]
+            }
+        )
+
+        df.loc[:, unit] = df.loc[:, "timestamp"].values.astype(
+            "datetime64[{unit}]".format(unit=unit)
+        )
+        df["expected"] = df.loc[:, "timestamp"].values.astype(
+            "datetime64[{unit}]".format(unit=unit)
+        )
+        expected = Series(df.loc[:, "expected"], name=unit)
+        tm.assert_series_equal(df.loc[:, unit], expected)
+
     def test_loc_setitem_frame(self):
         df = self.frame_labels