summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wiebe <mwiebe@enthought.com>2011-06-02 12:33:49 -0500
committerMark Wiebe <mwiebe@enthought.com>2011-06-02 12:33:49 -0500
commitb0a6e3908da85ff3e83eafbd50dfd1c440e4755d (patch)
tree90ed014fbf5f033dec45dd9cc88163bef80c8244
parent7eb97b56efac9d1a64100e46ed5000e12ecb3399 (diff)
downloadnumpy-b0a6e3908da85ff3e83eafbd50dfd1c440e4755d.tar.gz
ENH: datetime: Fix up creation of datetime scalar
-rw-r--r--numpy/core/src/multiarray/_datetime.h13
-rw-r--r--numpy/core/src/multiarray/datetime.c65
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src32
-rw-r--r--numpy/core/tests/test_datetime.py44
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)