diff options
author | Mark Wiebe <mwiebe@enthought.com> | 2011-06-02 12:33:49 -0500 |
---|---|---|
committer | Mark Wiebe <mwiebe@enthought.com> | 2011-06-02 12:33:49 -0500 |
commit | b0a6e3908da85ff3e83eafbd50dfd1c440e4755d (patch) | |
tree | 90ed014fbf5f033dec45dd9cc88163bef80c8244 | |
parent | 7eb97b56efac9d1a64100e46ed5000e12ecb3399 (diff) | |
download | numpy-b0a6e3908da85ff3e83eafbd50dfd1c440e4755d.tar.gz |
ENH: datetime: Fix up creation of datetime scalar
-rw-r--r-- | numpy/core/src/multiarray/_datetime.h | 13 | ||||
-rw-r--r-- | numpy/core/src/multiarray/datetime.c | 65 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.c.src | 32 | ||||
-rw-r--r-- | numpy/core/tests/test_datetime.py | 44 |
4 files changed, 150 insertions, 4 deletions
diff --git a/numpy/core/src/multiarray/_datetime.h b/numpy/core/src/multiarray/_datetime.h index 4c28c7840..e4bc2eaf0 100644 --- a/numpy/core/src/multiarray/_datetime.h +++ b/numpy/core/src/multiarray/_datetime.h @@ -268,4 +268,17 @@ convert_datetimestruct_to_datetime(PyArray_DatetimeMetaData *meta, NPY_NO_EXPORT npy_bool has_equivalent_datetime_metadata(PyArray_Descr *type1, PyArray_Descr *type2); +/* + * Casts a single datetime from having src_meta metadata into + * dst_meta metadata. + * + * Returns 0 on success, -1 on failure. + */ +NPY_NO_EXPORT int +cast_datetime_to_datetime(PyArray_DatetimeMetaData *src_meta, + PyArray_DatetimeMetaData *dst_meta, + npy_datetime src_dt, + npy_datetime *dst_dt); + + #endif diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c index 9dd1e0a38..88c764ace 100644 --- a/numpy/core/src/multiarray/datetime.c +++ b/numpy/core/src/multiarray/datetime.c @@ -11,6 +11,7 @@ #include "numpy/npy_3kcompat.h" +#include "numpy/arrayscalars.h" #include "_datetime.h" /* @@ -3022,10 +3023,30 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, return 0; } - /* TODO - else if (PyArray_IsScalar(op, Datetime)) { + /* Datetime scalar */ + else if (PyArray_IsScalar(obj, Datetime)) { + PyDatetimeScalarObject *dts = (PyDatetimeScalarObject *)obj; + + return cast_datetime_to_datetime(&dts->obmeta, meta, dts->obval, out); + } + /* Datetime zero-dimensional array */ + else if (PyArray_Check(obj) && + PyArray_NDIM(obj) == 0 && + PyArray_DESCR(obj)->type_num == NPY_DATETIME) { + PyArray_DatetimeMetaData *obj_meta; + npy_datetime dt = 0; + + obj_meta = get_datetime_metadata_from_dtype(PyArray_DESCR(obj)); + if (obj_meta == NULL) { + return -1; + } + PyArray_DESCR(obj)->f->copyswap(&dt, + PyArray_DATA(obj), + !PyArray_ISNOTSWAPPED(obj), + obj); + + return cast_datetime_to_datetime(obj_meta, meta, dt, out); } - */ /* Convert from a Python date or datetime object */ else { int code; @@ -3179,3 +3200,41 @@ has_equivalent_datetime_metadata(PyArray_Descr *type1, PyArray_Descr *type2) meta1->events == meta2->events; } +/* + * Casts a single datetime from having src_meta metadata into + * dst_meta metadata. + * + * Returns 0 on success, -1 on failure. + */ +NPY_NO_EXPORT int +cast_datetime_to_datetime(PyArray_DatetimeMetaData *src_meta, + PyArray_DatetimeMetaData *dst_meta, + npy_datetime src_dt, + npy_datetime *dst_dt) +{ + npy_datetimestruct dts; + + /* If the metadata is the same, short-circuit the conversion */ + if (src_meta->base == dst_meta->base && + src_meta->num == dst_meta->num && + src_meta->events == dst_meta->events) { + *dst_dt = src_dt; + return 0; + } + + /* Otherwise convert through a datetimestruct */ + if (convert_datetime_to_datetimestruct(src_meta, src_dt, &dts) < 0) { + *dst_dt = NPY_DATETIME_NAT; + return -1; + } + if (dts.event >= dst_meta->events) { + dts.event = dts.event % dst_meta->events; + } + if (convert_datetimestruct_to_datetime(dst_meta, &dts, dst_dt) < 0) { + *dst_dt = NPY_DATETIME_NAT; + return -1; + } + + return 0; +} + diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index b753609ba..66969fbb2 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -2350,7 +2350,7 @@ finish: /**begin repeat * #name = datetime, timedelta# * #Name = Datetime, Timedelta# - * #TYPE = DATETIME, TIMEDELTA# + * #NAME = DATETIME, TIMEDELTA# */ static PyObject * @@ -2388,6 +2388,36 @@ static PyObject * if (obj == NULL) { ret->obval = 0; } + /* + * If the metadata was unspecified and the input is a datetime/timedelta + * scalar, then copy the input's value and metadata exactly. + */ + else if (meta_obj == NULL && PyArray_IsScalar(obj, @Name@)) { + ret->obval = ((Py@Name@ScalarObject *)obj)->obval; + ret->obmeta = ((Py@Name@ScalarObject *)obj)->obmeta; + } + /* + * If the metadata was unspecified and the input is a datetime/timedelta + * zero-dimensional array, then copy the input's value and metadata + * directly. + */ + else if (meta_obj == NULL && + PyArray_Check(obj) && + PyArray_NDIM(obj) == 0 && + PyArray_DESCR(obj)->type_num == NPY_@NAME@) { + PyArray_DatetimeMetaData *meta; + + meta = get_datetime_metadata_from_dtype(PyArray_DESCR(obj)); + if (meta == NULL) { + Py_DECREF(ret); + return NULL; + } + PyArray_DESCR(obj)->f->copyswap(&ret->obval, + PyArray_DATA(obj), + !PyArray_ISNOTSWAPPED(obj), + obj); + ret->obmeta = *meta; + } else { if (convert_pyobject_to_@name@(&ret->obmeta, obj, &ret->obval) < 0) { diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index cd0c4b3df..cea44d19f 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -72,14 +72,58 @@ class TestDateTime(TestCase): # Can cast safely if the integer multiplier does divide assert_(np.can_cast('M8[6h]', 'M8[3h]', casting='safe')) + def test_datetime_scalar_construction(self): + # Construct with different units + assert_equal(np.datetime64('1950-03-12', 'D'), + np.datetime64('1950-03-12')) + assert_equal(np.datetime64('1950-03-12', 's'), + np.datetime64('1950-03-12', 'm')) + + # When constructing from a scalar or zero-dimensional array, + # it either keeps the units or you can override them. + a = np.datetime64('2000-03-18T16Z', 'h') + b = np.array('2000-03-18T16Z', dtype='M8[h]') + + assert_equal(a.dtype, np.dtype('M8[h]')) + assert_equal(b.dtype, np.dtype('M8[h]')) + + assert_equal(np.datetime64(a), a); + assert_equal(np.datetime64(a).dtype, np.dtype('M8[h]')) + + assert_equal(np.datetime64(b), a) + assert_equal(np.datetime64(b).dtype, np.dtype('M8[h]')) + + assert_equal(np.datetime64(a, 's'), a) + assert_equal(np.datetime64(a, 's').dtype, np.dtype('M8[s]')) + + assert_equal(np.datetime64(b, 's'), a) + assert_equal(np.datetime64(b, 's').dtype, np.dtype('M8[s]')) + def test_datetime_nat_casting(self): a = np.array('NaT', dtype='M8[D]') + b = np.datetime64('NaT', '[D]') + + # Arrays assert_equal(a.astype('M8[s]'), np.array('NaT', dtype='M8[s]')) assert_equal(a.astype('M8[ms]'), np.array('NaT', dtype='M8[ms]')) assert_equal(a.astype('M8[M]'), np.array('NaT', dtype='M8[M]')) assert_equal(a.astype('M8[Y]'), np.array('NaT', dtype='M8[Y]')) assert_equal(a.astype('M8[W]'), np.array('NaT', dtype='M8[W]')) + # Scalars -> Scalars + assert_equal(np.datetime64(b, '[s]'), np.datetime64('NaT', '[s]')) + assert_equal(np.datetime64(b, '[ms]'), np.datetime64('NaT', '[ms]')) + assert_equal(np.datetime64(b, '[M]'), np.datetime64('NaT', '[M]')) + assert_equal(np.datetime64(b, '[Y]'), np.datetime64('NaT', '[Y]')) + assert_equal(np.datetime64(b, '[W]'), np.datetime64('NaT', '[W]')) + + # Arrays -> Scalars + assert_equal(np.datetime64(a, '[s]'), np.datetime64('NaT', '[s]')) + assert_equal(np.datetime64(a, '[ms]'), np.datetime64('NaT', '[ms]')) + assert_equal(np.datetime64(a, '[M]'), np.datetime64('NaT', '[M]')) + assert_equal(np.datetime64(a, '[Y]'), np.datetime64('NaT', '[Y]')) + assert_equal(np.datetime64(a, '[W]'), np.datetime64('NaT', '[W]')) + def test_days_creation(self): assert_equal(np.array('1599', dtype='M8[D]').astype('i8'), (1600-1970)*365 - (1972-1600)/4 + 3 - 365) |