Repository: pandas-dev/pandas

Issue:
BUG: to_datetime with unit, utc=True and errors='ignore' does not coerce to UTC
```
In [3]: pd.__version__
Out[3]: '0.24.0.dev0+1040.g24bce1a5f'

In [4]: pd.to_datetime([1], unit='s', box=True, utc=True, errors='ignore')
Out[4]: DatetimeIndex(['1970-01-01 00:00:01'], dtype='datetime64[ns]', freq=None)
```

It appears the tz argument is missing from the output here:

https://github.com/pandas-dev/pandas/blob/24bce1a5fdd70a66b9fb5e2f9f51631d1df6add3/pandas/core/tools/datetimes.py#L215

#### Expected Output
```
In [4]: pd.to_datetime([1], unit='s', box=True, utc=True, errors='ignore')
Out[4]: DatetimeIndex(['1970-01-01 00:00:01+00:00'], dtype='datetime64[ns, UTC]', freq=None)
```




Discussion:
@mroeschke let me fix it
Looks like @srinivasreddy is already working on a PR for this issue. Feel free to coordinate with him on this issue.

Resolving patch:
diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py
--- a/pandas/core/tools/datetimes.py
+++ b/pandas/core/tools/datetimes.py
@@ -204,7 +204,12 @@ def _convert_listlike_datetimes(arg, box, format, name=None, tz=None,
         if box:
             if errors == 'ignore':
                 from pandas import Index
-                return Index(result, name=name)
+                result = Index(result, name=name)
+                # GH 23758: We may still need to localize the result with tz
+                try:
+                    return result.tz_localize(tz)
+                except AttributeError:
+                    return result
 
             return DatetimeIndex(result, tz=tz, name=name)
         return result
@@ -572,7 +577,7 @@ def to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False,
             values = convert_listlike(arg._values, True, format)
             result = Series(values, index=arg.index, name=arg.name)
     elif isinstance(arg, (ABCDataFrame, compat.MutableMapping)):
-        result = _assemble_from_unit_mappings(arg, errors=errors)
+        result = _assemble_from_unit_mappings(arg, errors, box, tz)
     elif isinstance(arg, ABCIndexClass):
         cache_array = _maybe_cache(arg, format, cache, convert_listlike)
         if not cache_array.empty:
@@ -618,7 +623,7 @@ def to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False,
              }
 
 
-def _assemble_from_unit_mappings(arg, errors):
+def _assemble_from_unit_mappings(arg, errors, box, tz):
     """
     assemble the unit specified fields from the arg (DataFrame)
     Return a Series for actual parsing
@@ -631,6 +636,11 @@ def _assemble_from_unit_mappings(arg, errors):
         - If 'raise', then invalid parsing will raise an exception
         - If 'coerce', then invalid parsing will be set as NaT
         - If 'ignore', then invalid parsing will return the input
+    box : boolean
+
+        - If True, return a DatetimeIndex
+        - If False, return an array
+    tz : None or 'utc'
 
     Returns
     -------
@@ -683,7 +693,7 @@ def coerce(values):
               coerce(arg[unit_rev['month']]) * 100 +
               coerce(arg[unit_rev['day']]))
     try:
-        values = to_datetime(values, format='%Y%m%d', errors=errors)
+        values = to_datetime(values, format='%Y%m%d', errors=errors, utc=tz)
     except (TypeError, ValueError) as e:
         raise ValueError("cannot assemble the "
                          "datetimes: {error}".format(error=e))
@@ -698,7 +708,8 @@ def coerce(values):
             except (TypeError, ValueError) as e:
                 raise ValueError("cannot assemble the datetimes [{value}]: "
                                  "{error}".format(value=value, error=e))
-
+    if not box:
+        return values.values
     return values
 
 


Test patch:
diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py
--- a/pandas/tests/indexes/datetimes/test_tools.py
+++ b/pandas/tests/indexes/datetimes/test_tools.py
@@ -949,6 +949,33 @@ def test_dataframe_dtypes(self, cache):
         with pytest.raises(ValueError):
             to_datetime(df, cache=cache)
 
+    def test_dataframe_box_false(self):
+        # GH 23760
+        df = pd.DataFrame({'year': [2015, 2016],
+                           'month': [2, 3],
+                           'day': [4, 5]})
+        result = pd.to_datetime(df, box=False)
+        expected = np.array(['2015-02-04', '2016-03-05'],
+                            dtype='datetime64[ns]')
+        tm.assert_numpy_array_equal(result, expected)
+
+    def test_dataframe_utc_true(self):
+        # GH 23760
+        df = pd.DataFrame({'year': [2015, 2016],
+                           'month': [2, 3],
+                           'day': [4, 5]})
+        result = pd.to_datetime(df, utc=True)
+        expected = pd.Series(np.array(['2015-02-04', '2016-03-05'],
+                             dtype='datetime64[ns]')).dt.tz_localize('UTC')
+        tm.assert_series_equal(result, expected)
+
+    def test_to_datetime_errors_ignore_utc_true(self):
+        # GH 23758
+        result = pd.to_datetime([1], unit='s', box=True, utc=True,
+                                errors='ignore')
+        expected = DatetimeIndex(['1970-01-01 00:00:01'], tz='UTC')
+        tm.assert_index_equal(result, expected)
+
 
 class TestToDatetimeMisc(object):
     def test_to_datetime_barely_out_of_bounds(self):