Repository: pandas-dev/pandas

Issue:
BUG: to_datetime drops UTC offset when parsing datetime strings and box=False
xref https://github.com/pandas-dev/pandas/pull/22380#issuecomment-413617891 and post PR #21822

```
In [1]: data = ['2018-01-04 09:01:00+09:00', '2018-01-04 09:02:00+09:00']

# This output should behave like Out[5]
In [3]: pd.to_datetime(data, box=False)
Out[3]:
array(['2018-01-04T00:01:00.000000000', '2018-01-04T00:02:00.000000000'],
      dtype='datetime64[ns]')

In [4]: data = ['2018-01-04 09:01:00+09:00', '2018-01-04 09:02:00+08:00']

In [5]: pd.to_datetime(data, box=False)
Out[5]:
array([datetime.datetime(2018, 1, 4, 9, 1, tzinfo=tzoffset(None, 32400)),
       datetime.datetime(2018, 1, 4, 9, 2, tzinfo=tzoffset(None, 28800))],
      dtype=object)
```


Discussion:

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
@@ -275,14 +275,25 @@ def _convert_listlike_datetimes(arg, box, format, name=None, tz=None,
                 yearfirst=yearfirst,
                 require_iso8601=require_iso8601
             )
-            if tz_parsed is not None and box:
-                return DatetimeIndex._simple_new(result, name=name,
-                                                 tz=tz_parsed)
+            if tz_parsed is not None:
+                if box:
+                    # We can take a shortcut since the datetime64 numpy array
+                    # is in UTC
+                    return DatetimeIndex._simple_new(result, name=name,
+                                                     tz=tz_parsed)
+                else:
+                    # Convert the datetime64 numpy array to an numpy array
+                    # of datetime objects
+                    result = [Timestamp(ts, tz=tz_parsed).to_pydatetime()
+                              for ts in result]
+                    return np.array(result, dtype=object)
 
         if box:
+            # Ensure we return an Index in all cases where box=True
             if is_datetime64_dtype(result):
                 return DatetimeIndex(result, tz=tz, name=name)
             elif is_object_dtype(result):
+                # e.g. an Index of datetime objects
                 from pandas import Index
                 return Index(result, name=name)
         return result


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
@@ -592,6 +592,17 @@ def test_iso_8601_strings_with_same_offset(self):
         result = DatetimeIndex([ts_str] * 2)
         tm.assert_index_equal(result, expected)
 
+    def test_iso_8601_strings_same_offset_no_box(self):
+        # GH 22446
+        data = ['2018-01-04 09:01:00+09:00', '2018-01-04 09:02:00+09:00']
+        result = pd.to_datetime(data, box=False)
+        expected = np.array([
+            datetime(2018, 1, 4, 9, 1, tzinfo=pytz.FixedOffset(540)),
+            datetime(2018, 1, 4, 9, 2, tzinfo=pytz.FixedOffset(540))
+        ],
+            dtype=object)
+        tm.assert_numpy_array_equal(result, expected)
+
     def test_iso_8601_strings_with_different_offsets(self):
         # GH 17697, 11736
         ts_strings = ["2015-11-18 15:30:00+05:30",
diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py
--- a/pandas/tests/test_algos.py
+++ b/pandas/tests/test_algos.py
@@ -330,10 +330,9 @@ def test_datetime64_dtype_array_returned(self):
              '2015-01-01T00:00:00.000000000+0000'],
             dtype='M8[ns]')
 
-        dt_index = pd.to_datetime(['2015-01-03T00:00:00.000000000+0000',
-                                   '2015-01-01T00:00:00.000000000+0000',
-                                   '2015-01-01T00:00:00.000000000+0000'],
-                                  box=False)
+        dt_index = pd.to_datetime(['2015-01-03T00:00:00.000000000',
+                                   '2015-01-01T00:00:00.000000000',
+                                   '2015-01-01T00:00:00.000000000'])
         result = algos.unique(dt_index)
         tm.assert_numpy_array_equal(result, expected)
         assert result.dtype == expected.dtype