Repository: pandas-dev/pandas

Issue:
to_datetime %Y%m%d does not coerce correctly
Hi,

see this example:

```
# imagine a dataframe with a from/to 
x_df = DataFrame([[20120101, 20121231], [20130101, 20131231], [20140101, 20141231], [20150101, 99991231]])
x_df.columns = ['date_from', 'date_to']
date_def = '%Y%m%d'
# so everything is ok & peachy for the from dates
x_df['date_from_2'] = pd.to_datetime(x_df['date_from'], format=date_def, coerce=True)
x_df['date_from_2'].dtype
list(x_df['date_from_2'])
# but with out of bound dates it goes horribly wrong
x_df['date_to_2'] = pd.to_datetime(x_df['date_to'], format=date_def, coerce=True)
x_df['date_to_2'].dtype 
list(x_df['date_to_2']) # note the lack of NATs and conversion to datetime.datetime instead of np.datetime64
# now we can do 
x_df['date_to_3'] = [np.datetime64(date_val, unit='s') for date_val in x_df['date_to_2']] # works great but unfortunately pandas chose to aim for nanoseconds as a standard for date detail...
x_df['date_to_3'] = [pd.Timestamp(date_val) for date_val in x_df['date_to_2']] # which breaks on the 99991231 example
```

This is a bug, but also related to the discussion in #7307. Probably has to do with:
"Note Specifying a format argument will potentially speed up the conversion considerably and on versions later then 0.13.0 explicitly specifying a format string of ‘%Y%m%d’ takes a faster path still."



Discussion:

Resolving patch:
diff --git a/pandas/tseries/tools.py b/pandas/tseries/tools.py
--- a/pandas/tseries/tools.py
+++ b/pandas/tseries/tools.py
@@ -262,7 +262,7 @@ def _convert_listlike(arg, box, format):
                 # shortcut formatting here
                 if format == '%Y%m%d':
                     try:
-                        result = _attempt_YYYYMMDD(arg)
+                        result = _attempt_YYYYMMDD(arg, coerce=coerce)
                     except:
                         raise ValueError("cannot convert the input to '%Y%m%d' date format")
 
@@ -313,14 +313,14 @@ def _convert_listlike(arg, box, format):
 class DateParseError(ValueError):
     pass
 
-def _attempt_YYYYMMDD(arg):
+def _attempt_YYYYMMDD(arg, coerce):
     """ try to parse the YYYYMMDD/%Y%m%d format, try to deal with NaT-like,
         arg is a passed in as an object dtype, but could really be ints/strings with nan-like/or floats (e.g. with nan) """
 
     def calc(carg):
         # calculate the actual result
         carg = carg.astype(object)
-        return lib.try_parse_year_month_day(carg/10000,carg/100 % 100, carg % 100)
+        return tslib.array_to_datetime(lib.try_parse_year_month_day(carg/10000,carg/100 % 100, carg % 100), coerce=coerce)
 
     def calc_with_mask(carg,mask):
         result = np.empty(carg.shape, dtype='M8[ns]')


Test patch:
diff --git a/pandas/tseries/tests/test_timeseries.py b/pandas/tseries/tests/test_timeseries.py
--- a/pandas/tseries/tests/test_timeseries.py
+++ b/pandas/tseries/tests/test_timeseries.py
@@ -3934,6 +3934,16 @@ def test_to_datetime_format_YYYYMMDD(self):
         result = to_datetime(s,format='%Y%m%d')
         assert_series_equal(result, expected)
 
+        # coercion
+        # GH 7930
+        s = Series([20121231, 20141231, 99991231])
+        result = pd.to_datetime(s,format='%Y%m%d')
+        expected = np.array([ datetime(2012,12,31), datetime(2014,12,31), datetime(9999,12,31) ], dtype=object)
+        self.assert_numpy_array_equal(result, expected)
+
+        result = pd.to_datetime(s,format='%Y%m%d', coerce=True)
+        expected = Series(['20121231','20141231','NaT'],dtype='M8[ns]')
+        assert_series_equal(result, expected)
 
     def test_to_datetime_format_microsecond(self):
         val = '01-Apr-2011 00:00:01.978'