Repository: numpy/numpy

Issue:
NaT and extended sort order
https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.sort.html#numpy.sort describes how `np.nan` is handled for floating point data. `NaT` and datetime data are not mentioned.

### Reproducing code example:

In the following, `NaN` is sorted to the end, while `NaT` is sorted to the start.

```python
In [32]: arr = np.array([np.nan, 1, 2, 3])

In [33]: arr.sort(); arr
Out[33]: array([ 1.,  2.,  3., nan])

In [34]: dtarr = np.array(['NaT', 1, 2, 3], dtype='datetime64[ns]')

In [35]: dtarr.sort(); dtarr
Out[35]:
array([                          'NaT', '1970-01-01T00:00:00.000000001',
       '1970-01-01T00:00:00.000000002', '1970-01-01T00:00:00.000000003'],
      dtype='datetime64[ns]')
```

So my question: is NaT sorting to the start a bug or deliberate? If it's deliberate, I could add a section to the docs noting that.

### Numpy/Python version information:

1.16.0.dev0+9cc9f01 3.7.1 (default, Nov  6 2018, 18:45:35)
[Clang 10.0.0 (clang-1000.11.45.5)]


Discussion:
Sounds like a bug, I expect that datetimes simply reuse the integer sorting code at the moment, it is too bad but sounds like we have to specialize it. Then again, it is likely the code is already a `….c.src` template for the different integer types, so should be easy to add a datetime special check for `INT_MIN` (if that is correct).

Resolving patch:
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -944,6 +944,10 @@ def sort(a, axis=-1, kind=None, order=None):
     'mergesort' and 'stable' are mapped to radix sort for integer data types. Radix sort is an
     O(n) sort instead of O(n log n).
 
+    .. versionchanged:: 1.17.0
+
+    NaT now sorts to the end of arrays for consistency with NaN.
+
     Examples
     --------
     >>> a = np.array([[1,4],[3,1]])


Test patch:
diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py
--- a/numpy/core/tests/test_datetime.py
+++ b/numpy/core/tests/test_datetime.py
@@ -136,6 +136,38 @@ def test_compare_generic_nat(self):
         assert_(np.datetime64('NaT') != np.datetime64('NaT', 'us'))
         assert_(np.datetime64('NaT', 'us') != np.datetime64('NaT'))
 
+
+
+    @pytest.mark.parametrize("size", [
+        3, 21, 217, 1000])
+    def test_nat_argsort_stability(self, size):
+        # NaT < NaT should be False internally for
+        # sort stability
+        expected = np.arange(size)
+        arr = np.tile(np.datetime64('NaT'), size)
+        assert_equal(np.argsort(arr, kind='mergesort'), expected)
+
+    @pytest.mark.parametrize("arr, expected", [
+        # the example provided in gh-12629
+        (np.array(['NaT', 1, 2, 3], dtype='M8[ns]'),
+         np.array([1, 2, 3, 'NaT'], dtype='M8[ns]')),
+        # multiple NaTs
+        (np.array(['NaT', 9, 'NaT', -707], dtype='M8[s]'),
+         np.array([-707, 9, 'NaT', 'NaT'], dtype='M8[s]')),
+        # this sort explores another code path for NaT
+        (np.array([1, -2, 3, 'NaT'], dtype='M8[ns]'),
+         np.array([-2, 1, 3, 'NaT'], dtype='M8[ns]')),
+        # 2-D array
+        (np.array([[51, -220, 'NaT'],
+                   [-17, 'NaT', -90]], dtype='M8[us]'),
+         np.array([[-220, 51, 'NaT'],
+                   [-90, -17, 'NaT']], dtype='M8[us]')),
+        ])
+    def test_sort_nat(self, arr, expected):
+        # fix for gh-12629; NaT sorting to end of array
+        arr.sort()
+        assert_equal(arr, expected)
+
     def test_datetime_scalar_construction(self):
         # Construct with different units
         assert_equal(np.datetime64('1950-03-12', 'D'),