diff options
author | Mark Wiebe <mwiebe@enthought.com> | 2011-06-07 15:37:26 -0500 |
---|---|---|
committer | Mark Wiebe <mwiebe@enthought.com> | 2011-06-09 12:03:34 -0500 |
commit | 6395979c7bb17dc79b2037884c383b46b75bb67f (patch) | |
tree | 50d96b92ccec24073d9a8cd5722af0bdb4dbb122 | |
parent | e64b6f1c8275f06c94c0b83f7b5469d7a33cd639 (diff) | |
download | numpy-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.h | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/datetime.c | 23 | ||||
-rw-r--r-- | numpy/core/tests/test_datetime.py | 14 |
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 |