summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wiebe <mwiebe@enthought.com>2011-06-07 15:37:26 -0500
committerMark Wiebe <mwiebe@enthought.com>2011-06-09 12:03:34 -0500
commit6395979c7bb17dc79b2037884c383b46b75bb67f (patch)
tree50d96b92ccec24073d9a8cd5722af0bdb4dbb122
parente64b6f1c8275f06c94c0b83f7b5469d7a33cd639 (diff)
downloadnumpy-6395979c7bb17dc79b2037884c383b46b75bb67f.tar.gz
ENH: datetime-autounit: Make 'now' and 'today' only parse with appropriate units
In particular, 'now' needs time-like units, and 'today' needs date-like units.
-rw-r--r--numpy/core/src/multiarray/_datetime.h4
-rw-r--r--numpy/core/src/multiarray/datetime.c23
-rw-r--r--numpy/core/tests/test_datetime.py14
3 files changed, 34 insertions, 7 deletions
diff --git a/numpy/core/src/multiarray/_datetime.h b/numpy/core/src/multiarray/_datetime.h
index b07fe2f51..643c9b106 100644
--- a/numpy/core/src/multiarray/_datetime.h
+++ b/numpy/core/src/multiarray/_datetime.h
@@ -192,6 +192,8 @@ get_datetime_iso_8601_strlen(int local, NPY_DATETIMEUNIT base);
* day according to local time) and "Now" (current time in UTC).
*
* 'str' must be a NULL-terminated string, and 'len' must be its length.
+ * 'unit' should contain -1 if the unit is unknown, or the unit
+ * which will be used if it is.
*
* 'out' gets filled with the parsed date-time.
* 'out_local' gets set to 1 if the parsed time was in local time,
@@ -210,12 +212,12 @@ get_datetime_iso_8601_strlen(int local, NPY_DATETIMEUNIT base);
*/
NPY_NO_EXPORT int
parse_iso_8601_date(char *str, int len,
+ NPY_DATETIMEUNIT unit,
npy_datetimestruct *out,
npy_bool *out_local,
NPY_DATETIMEUNIT *out_bestunit,
npy_bool *out_special);
-
/*
* Converts an npy_datetimestruct to an (almost) ISO 8601
* NULL-terminated string.
diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c
index 8e57961eb..642341570 100644
--- a/numpy/core/src/multiarray/datetime.c
+++ b/numpy/core/src/multiarray/datetime.c
@@ -2278,6 +2278,8 @@ add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes)
* day according to local time) and "Now" (current time in UTC).
*
* 'str' must be a NULL-terminated string, and 'len' must be its length.
+ * 'unit' should contain -1 if the unit is unknown, or the unit
+ * which will be used if it is.
*
* 'out' gets filled with the parsed date-time.
* 'out_local' gets set to 1 if the parsed time was in local time,
@@ -2296,6 +2298,7 @@ add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes)
*/
NPY_NO_EXPORT int
parse_iso_8601_date(char *str, int len,
+ NPY_DATETIMEUNIT unit,
npy_datetimestruct *out,
npy_bool *out_local,
NPY_DATETIMEUNIT *out_bestunit,
@@ -2350,6 +2353,14 @@ parse_iso_8601_date(char *str, int len,
time_t rawtime = 0;
struct tm tm_;
+ /* 'today' only works for units of days or larger */
+ if (unit != -1 && unit > NPY_FR_D) {
+ PyErr_SetString(PyExc_ValueError,
+ "Special value 'today' can only be converted "
+ "to a NumPy datetime with 'D' or larger units");
+ return -1;
+ }
+
time(&rawtime);
#if defined(_WIN32)
if (localtime_s(&tm_, &rawtime) != 0) {
@@ -2392,6 +2403,15 @@ parse_iso_8601_date(char *str, int len,
tolower(str[2]) == 'w') {
time_t rawtime = 0;
PyArray_DatetimeMetaData meta;
+
+ /* 'now' only works for units of hours or smaller */
+ if (unit != -1 && unit < NPY_FR_h) {
+ PyErr_SetString(PyExc_ValueError,
+ "Special value 'now' can only be converted "
+ "to a NumPy datetime with 'h' or smaller units");
+ return -1;
+ }
+
time(&rawtime);
/* Set up a dummy metadata for the conversion */
@@ -3658,7 +3678,8 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj,
}
/* Parse the ISO date */
- if (parse_iso_8601_date(str, len, &dts, NULL, &bestunit, NULL) < 0) {
+ if (parse_iso_8601_date(str, len, meta->base, &dts,
+ NULL, &bestunit, NULL) < 0) {
Py_DECREF(bytes);
return -1;
}
diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py
index 9fb249340..316d03ecc 100644
--- a/numpy/core/tests/test_datetime.py
+++ b/numpy/core/tests/test_datetime.py
@@ -239,10 +239,18 @@ class TestDateTime(TestCase):
assert_equal(np.datetime64('today').dtype,
np.dtype('M8[D]'))
+ assert_raises(ValueError, np.datetime64, 'today', 'h')
+ assert_raises(ValueError, np.datetime64, 'today', 's')
+ assert_raises(ValueError, np.datetime64, 'today', 'as')
+
# 'now' special value
assert_equal(np.datetime64('now').dtype,
np.dtype('M8[s]'))
+ assert_raises(ValueError, np.datetime64, 'now', 'Y')
+ assert_raises(ValueError, np.datetime64, 'now', 'M')
+ assert_raises(ValueError, np.datetime64, 'now', 'D')
+
def test_datetime_nat_casting(self):
a = np.array('NaT', dtype='M8[D]')
b = np.datetime64('NaT', '[D]')
@@ -352,7 +360,7 @@ class TestDateTime(TestCase):
a = np.array(['2000-01-01', datetime.date(2000, 1, 1)], dtype='M8[s]')
assert_equal(a[0], a[1])
# Will fail if the date changes during the exact right moment
- a = np.array(['today', datetime.date.today()], dtype='M8[s]')
+ a = np.array(['today', datetime.date.today()], dtype='M8[D]')
assert_equal(a[0], a[1])
# datetime.datetime.now() returns local time, not UTC
#a = np.array(['now', datetime.datetime.now()], dtype='M8[s]')
@@ -470,14 +478,10 @@ class TestDateTime(TestCase):
np.array('1932-02-17T00:00:00Z', dtype=dt2))
assert_equal(np.array('10000-04-27', dtype=dt1),
np.array('10000-04-27T00:00:00Z', dtype=dt2))
- assert_equal(np.array('today', dtype=dt1),
- np.array('today', dtype=dt2))
assert_equal(np.datetime64('1932-02-17', unit1),
np.datetime64('1932-02-17T00:00:00Z', unit2))
assert_equal(np.datetime64('10000-04-27', unit1),
np.datetime64('10000-04-27T00:00:00Z', unit2))
- assert_equal(np.datetime64('today', unit1),
- np.datetime64('today', unit2))
# Shouldn't be able to compare datetime and timedelta
# TODO: Changing to 'same_kind' or 'safe' casting in the ufuncs by