Skip to content

Commit d54c6a7

Browse files
committed
BUG: fixes GH3425, raising on passed invalid dtypes for datetimelike
fixes GH2423, astyping is now checked when using datetimelike and timedeltalike for valid astype to dtypes BUG: PY3 compat for timedelta64[ns] in astype BUG: more py3 compat PTF
1 parent 6c3a89d commit d54c6a7

File tree

6 files changed

+45
-31
lines changed

6 files changed

+45
-31
lines changed

RELEASE.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ pandas 0.11.1
5656
Note: The default value will change in 0.12 to the "no mangle" behaviour,
5757
If your code relies on this behaviour, explicitly specify mangle_dupe_cols=True
5858
in your calls.
59+
- Do not allow astypes on ``datetime64[ns]`` except to ``object``, and
60+
``timedelta64[ns]`` to ``object/int`` (GH3425_)
61+
- Do not allow datetimelike/timedeltalike creation except with valid types
62+
(e.g. cannot pass ``datetime64[ms]``) (GH3423_)
5963

6064
**Bug Fixes**
6165

@@ -93,8 +97,9 @@ pandas 0.11.1
9397
.. _GH2786: https://github.com/pydata/pandas/issues/2786
9498
.. _GH2194: https://github.com/pydata/pandas/issues/2194
9599
.. _GH3230: https://github.com/pydata/pandas/issues/3230
96-
.. _GH3164: https://github.com/pydata/pandas/issues/3164
100+
.. _GH3425: https://github.com/pydata/pandas/issues/3425
97101
.. _GH3416: https://github.com/pydata/pandas/issues/3416
102+
.. _GH3423: https://github.com/pydata/pandas/issues/3423
98103
.. _GH3251: https://github.com/pydata/pandas/issues/3251
99104
.. _GH3379: https://github.com/pydata/pandas/issues/3379
100105
.. _GH3480: https://github.com/pydata/pandas/issues/3480

pandas/core/common.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class AmbiguousIndexError(PandasError, KeyError):
4343

4444

4545
_POSSIBLY_CAST_DTYPES = set([ np.dtype(t) for t in ['M8[ns]','m8[ns]','O','int8','uint8','int16','uint16','int32','uint32','int64','uint64'] ])
46+
_NS_DTYPE = np.dtype('M8[ns]')
47+
_TD_DTYPE = np.dtype('m8[ns]')
48+
_INT64_DTYPE = np.dtype(np.int64)
4649

4750
def isnull(obj):
4851
'''
@@ -1085,10 +1088,10 @@ def _possibly_cast_to_datetime(value, dtype, coerce = False):
10851088
if is_datetime64 or is_timedelta64:
10861089

10871090
# force the dtype if needed
1088-
#if is_datetime64 and dtype != 'datetime64[ns]':
1089-
# dtype = np.dtype('datetime64[ns]')
1090-
#elif is_timedelta64 and dtype != 'timedelta64[ns]':
1091-
# dtype = np.dtype('timedelta64[ns]')
1091+
if is_datetime64 and dtype != _NS_DTYPE:
1092+
raise TypeError("cannot convert datetimelike to dtype [%s]" % dtype)
1093+
elif is_timedelta64 and dtype != _TD_DTYPE:
1094+
raise TypeError("cannot convert timedeltalike to dtype [%s]" % dtype)
10921095

10931096
if np.isscalar(value):
10941097
if value == tslib.iNaT or isnull(value):
@@ -1106,7 +1109,6 @@ def _possibly_cast_to_datetime(value, dtype, coerce = False):
11061109
if is_datetime64:
11071110
from pandas.tseries.tools import to_datetime
11081111
value = to_datetime(value, coerce=coerce).values
1109-
#value = tslib.array_to_datetime(value, coerce = coerce)
11101112
elif is_timedelta64:
11111113
value = _possibly_cast_to_timedelta(value)
11121114
except:
@@ -1523,9 +1525,24 @@ def _astype_nansafe(arr, dtype, copy = True):
15231525
if not isinstance(dtype, np.dtype):
15241526
dtype = np.dtype(dtype)
15251527

1526-
if issubclass(arr.dtype.type, np.datetime64):
1528+
if is_datetime64_dtype(arr):
15271529
if dtype == object:
15281530
return tslib.ints_to_pydatetime(arr.view(np.int64))
1531+
elif issubclass(dtype.type, np.int):
1532+
return arr.view(dtype)
1533+
elif dtype != _NS_DTYPE:
1534+
raise TypeError("cannot astype a datetimelike from [%s] to [%s]" % (arr.dtype,dtype))
1535+
return arr.astype(_NS_DTYPE)
1536+
elif is_timedelta64_dtype(arr):
1537+
if issubclass(dtype.type, np.int):
1538+
return arr.view(dtype)
1539+
elif dtype == object:
1540+
return arr.astype(object)
1541+
1542+
# in py3, timedelta64[ns] are int64
1543+
elif (py3compat.PY3 and dtype not in [_INT64_DTYPE,_TD_DTYPE]) or (not py3compat.PY3 and dtype != _TD_DTYPE):
1544+
raise TypeError("cannot astype a timedelta from [%s] to [%s]" % (arr.dtype,dtype))
1545+
return arr.astype(_TD_DTYPE)
15291546
elif (np.issubdtype(arr.dtype, np.floating) and
15301547
np.issubdtype(dtype, np.integer)):
15311548

@@ -1729,9 +1746,6 @@ def _check_as_is(x):
17291746
self.queue.truncate(0)
17301747

17311748

1732-
_NS_DTYPE = np.dtype('M8[ns]')
1733-
1734-
17351749
def _concat_compat(to_concat, axis=0):
17361750
# filter empty arrays
17371751
to_concat = [x for x in to_concat if x.shape[axis] > 0]
@@ -1759,7 +1773,6 @@ def _to_pydatetime(x):
17591773

17601774
return x
17611775

1762-
17631776
def _where_compat(mask, arr1, arr2):
17641777
if arr1.dtype == _NS_DTYPE and arr2.dtype == _NS_DTYPE:
17651778
new_vals = np.where(mask, arr1.view(np.int64), arr2.view(np.int64))

pandas/core/internals.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from numpy import nan
55
import numpy as np
66

7-
from pandas.core.common import _possibly_downcast_to_dtype, isnull
7+
from pandas.core.common import _possibly_downcast_to_dtype, isnull, _NS_DTYPE, _TD_DTYPE
88
from pandas.core.index import Index, MultiIndex, _ensure_index, _handle_legacy_indexes
99
from pandas.core.indexing import _check_slice_bounds, _maybe_convert_indices
1010
import pandas.core.common as com
@@ -740,10 +740,6 @@ def should_store(self, value):
740740
(np.integer, np.floating, np.complexfloating,
741741
np.datetime64, np.bool_))
742742

743-
_NS_DTYPE = np.dtype('M8[ns]')
744-
_TD_DTYPE = np.dtype('m8[ns]')
745-
746-
747743
class DatetimeBlock(Block):
748744
_can_hold_na = True
749745

pandas/tests/test_series.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -482,13 +482,13 @@ def test_constructor_dtype_datetime64(self):
482482
s.ix[0] = np.nan
483483
self.assert_(s.dtype == 'M8[ns]')
484484

485-
# GH3414 related
486-
#import pdb; pdb.set_trace()
487-
#result = Series(Series(dates).astype('int')/1e6,dtype='M8[ms]')
488-
#self.assert_(result.dtype == 'M8[ns]')
485+
# invalid astypes
486+
for t in ['s','D','us','ms']:
487+
self.assertRaises(TypeError, s.astype, 'M8[%s]' % t)
489488

490-
#s = Series(dates, dtype='datetime64')
491-
#self.assert_(s.dtype == 'M8[ns]')
489+
# GH3414 related
490+
self.assertRaises(TypeError, lambda x: Series(Series(dates).astype('int')/1000000,dtype='M8[ms]'))
491+
self.assertRaises(TypeError, lambda x: Series(dates, dtype='datetime64'))
492492

493493
def test_constructor_dict(self):
494494
d = {'a': 0., 'b': 1., 'c': 2.}
@@ -1830,6 +1830,13 @@ def test_constructor_dtype_timedelta64(self):
18301830
td = Series([ timedelta(days=i) for i in range(3) ] + [ np.nan ], dtype='m8[ns]' )
18311831
self.assert_(td.dtype=='timedelta64[ns]')
18321832

1833+
# invalid astypes
1834+
for t in ['s','D','us','ms']:
1835+
self.assertRaises(TypeError, td.astype, 'm8[%s]' % t)
1836+
1837+
# valid astype
1838+
td.astype('int')
1839+
18331840
# this is an invalid casting
18341841
self.assertRaises(Exception, Series, [ timedelta(days=i) for i in range(3) ] + [ 'foo' ], dtype='m8[ns]' )
18351842

pandas/tseries/index.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import numpy as np
88

9-
from pandas.core.common import isnull
9+
from pandas.core.common import isnull, _NS_DTYPE, _INT64_DTYPE
1010
from pandas.core.index import Index, Int64Index
1111
from pandas.tseries.frequencies import (
1212
infer_freq, to_offset, get_period_alias,
@@ -92,9 +92,6 @@ class TimeSeriesError(Exception):
9292

9393

9494
_midnight = time(0, 0)
95-
_NS_DTYPE = np.dtype('M8[ns]')
96-
_INT64_DTYPE = np.dtype(np.int64)
97-
9895

9996
class DatetimeIndex(Int64Index):
10097
"""

pandas/tseries/period.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import pandas.tseries.frequencies as _freq_mod
1313

1414
import pandas.core.common as com
15-
from pandas.core.common import isnull
15+
from pandas.core.common import isnull, _NS_DTYPE, _INT64_DTYPE
1616
from pandas.util import py3compat
1717

1818
from pandas.lib import Timestamp
@@ -516,10 +516,6 @@ def wrapper(self, other):
516516
return result
517517
return wrapper
518518

519-
_INT64_DTYPE = np.dtype(np.int64)
520-
_NS_DTYPE = np.dtype('M8[ns]')
521-
522-
523519
class PeriodIndex(Int64Index):
524520
"""
525521
Immutable ndarray holding ordinal values indicating regular periods in

0 commit comments

Comments
 (0)