diff options
author | Mark Wiebe <mwiebe@enthought.com> | 2011-06-07 15:22:38 -0500 |
---|---|---|
committer | Mark Wiebe <mwiebe@enthought.com> | 2011-06-09 12:03:19 -0500 |
commit | e64b6f1c8275f06c94c0b83f7b5469d7a33cd639 (patch) | |
tree | 3f44d876d80c761013b7f76661bf6a407637eaaa | |
parent | a1e7be3cb49577d77831e2246f39bb80acd5a22f (diff) | |
download | numpy-e64b6f1c8275f06c94c0b83f7b5469d7a33cd639.tar.gz |
ENH: datetime-autounit: Automatically detect the unit for scalar construction
-rw-r--r-- | numpy/core/src/multiarray/_datetime.h | 44 | ||||
-rw-r--r-- | numpy/core/src/multiarray/datetime.c | 323 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.c.src | 52 | ||||
-rw-r--r-- | numpy/core/tests/test_datetime.py | 87 |
4 files changed, 423 insertions, 83 deletions
diff --git a/numpy/core/src/multiarray/_datetime.h b/numpy/core/src/multiarray/_datetime.h index 519a1c545..b07fe2f51 100644 --- a/numpy/core/src/multiarray/_datetime.h +++ b/numpy/core/src/multiarray/_datetime.h @@ -175,23 +175,46 @@ append_metastr_to_string(PyArray_DatetimeMetaData *meta, NPY_NO_EXPORT int get_datetime_iso_8601_strlen(int local, NPY_DATETIMEUNIT base); - /* * Parses (almost) standard ISO 8601 date strings. The differences are: * + * + After the date and time, may place a ' ' followed by an event number. * + The date "20100312" is parsed as the year 20100312, not as * equivalent to "2010-03-12". The '-' in the dates are not optional. * + Only seconds may have a decimal point, with up to 18 digits after it * (maximum attoseconds precision). * + Either a 'T' as in ISO 8601 or a ' ' may be used to separate * the date and the time. Both are treated equivalently. + * + Doesn't (yet) handle the "YYYY-DDD" or "YYYY-Www" formats. + * + Doesn't handle leap seconds (seconds value has 60 in these cases). + * + Doesn't handle 24:00:00 as synonym for midnight (00:00:00) tomorrow + * + Accepts special values "NaT" (not a time), "Today", (current + * day according to local time) and "Now" (current time in UTC). * * 'str' must be a NULL-terminated string, and 'len' must be its length. * + * 'out' gets filled with the parsed date-time. + * 'out_local' gets set to 1 if the parsed time was in local time, + * to 0 otherwise. The values 'now' and 'today' don't get counted + * as local, and neither do UTC +/-#### timezone offsets, because + * they aren't using the computer's local timezone offset. + * 'out_bestunit' gives a suggested unit based on the amount of + * resolution provided in the string, or -1 for NaT. + * 'out_special' gets set to 1 if the parsed time was 'today', + * 'now', or ''/'NaT'. For 'today', the unit recommended is + * 'D', for 'now', the unit recommended is 's', and for 'NaT' + * the unit recommended is 'Y'. + * + * * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -parse_iso_8601_date(char *str, int len, npy_datetimestruct *out); +parse_iso_8601_date(char *str, int len, + npy_datetimestruct *out, + npy_bool *out_local, + NPY_DATETIMEUNIT *out_bestunit, + npy_bool *out_special); + /* * Converts an npy_datetimestruct to an (almost) ISO 8601 @@ -214,19 +237,26 @@ NPY_NO_EXPORT int make_iso_8601_date(npy_datetimestruct *dts, char *outstr, int outlen, int local, NPY_DATETIMEUNIT base, int tzoffset); - /* * Tests for and converts a Python datetime.datetime or datetime.date * object into a NumPy npy_datetimestruct. * + * 'out_bestunit' gives a suggested unit based on whether the object + * was a datetime.date or datetime.datetime object. + * * Returns -1 on error, 0 on success, and 1 (with no error set) * if obj doesn't have the neeeded date or datetime attributes. */ NPY_NO_EXPORT int -convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out); +convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, + NPY_DATETIMEUNIT *out_bestunit); /* - * Converts a PyObject * into a datetime, in any of the input forms supported. + * Converts a PyObject * into a datetime, in any of the forms supported. + * + * If the units metadata isn't known ahead of time, set meta->base + * to -1, and this function will populate meta with either default + * values or values from the input object. * * Returns -1 on error, 0 on success. */ @@ -237,6 +267,10 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, /* * Converts a PyObject * into a timedelta, in any of the forms supported * + * If the units metadata isn't known ahead of time, set meta->base + * to -1, and this function will populate meta with either default + * values or values from the input object. + * * Returns -1 on error, 0 on success. */ NPY_NO_EXPORT int diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c index a1fcd54fa..8e57961eb 100644 --- a/numpy/core/src/multiarray/datetime.c +++ b/numpy/core/src/multiarray/datetime.c @@ -1168,8 +1168,8 @@ parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len) /* If there's no metastr, use the default */ if (len == 0) { - dt_data->num = 1; dt_data->base = NPY_DATETIME_DEFAULTUNIT; + dt_data->num = 1; dt_data->events = 1; } else { @@ -2279,13 +2279,30 @@ add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes) * * 'str' must be a NULL-terminated string, and 'len' must be its length. * + * 'out' gets filled with the parsed date-time. + * 'out_local' gets set to 1 if the parsed time was in local time, + * to 0 otherwise. The values 'now' and 'today' don't get counted + * as local, and neither do UTC +/-#### timezone offsets, because + * they aren't using the computer's local timezone offset. + * 'out_bestunit' gives a suggested unit based on the amount of + * resolution provided in the string, or -1 for NaT. + * 'out_special' gets set to 1 if the parsed time was 'today', + * 'now', or ''/'NaT'. For 'today', the unit recommended is + * 'D', for 'now', the unit recommended is 's', and for 'NaT' + * the unit recommended is 'Y'. + * + * * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) +parse_iso_8601_date(char *str, int len, + npy_datetimestruct *out, + npy_bool *out_local, + NPY_DATETIMEUNIT *out_bestunit, + npy_bool *out_special) { int year_leap = 0; - int i; + int i, numdigits; char *substr, sublen; /* Initialize the output to all zeros */ @@ -2299,6 +2316,22 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) tolower(str[1]) == 'a' && tolower(str[2]) == 't')) { out->year = NPY_DATETIME_NAT; + + /* + * Indicate that this was a special value, and + * recommend 'Y' as the unit because when promoted + * with any other unit, will produce that unit. + */ + if (out_local != NULL) { + *out_local = 0; + } + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_Y; + } + if (out_special != NULL) { + *out_special = 1; + } + return 0; } @@ -2335,6 +2368,21 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) out->year = tm_.tm_year + 1900; out->month = tm_.tm_mon + 1; out->day = tm_.tm_mday; + + /* + * Indicate that this was a special value, and + * is a date (unit 'D'). + */ + if (out_local != NULL) { + *out_local = 0; + } + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_D; + } + if (out_special != NULL) { + *out_special = 1; + } + return 0; } @@ -2351,9 +2399,29 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) meta.num = 1; meta.events = 1; + /* + * Indicate that this was a special value, and + * use 's' because the time() function has resolution + * seconds. + */ + if (out_local != NULL) { + *out_local = 0; + } + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_s; + } + if (out_special != NULL) { + *out_special = 1; + } + return convert_datetime_to_datetimestruct(&meta, rawtime, out); } + /* Anything else isn't a special value */ + if (out_special != NULL) { + *out_special = 0; + } + substr = str; sublen = len; @@ -2390,6 +2458,12 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) /* Next character must be a '-' or the end of the string */ if (sublen == 0) { + if (out_local != NULL) { + *out_local = 0; + } + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_Y; + } goto finish; } else if (*substr == '-') { @@ -2423,6 +2497,12 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) /* Next character must be a '-' or the end of the string */ if (sublen == 0) { + if (out_local != NULL) { + *out_local = 0; + } + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_M; + } goto finish; } else if (*substr == '-') { @@ -2457,6 +2537,12 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) /* Next character must be a 'T', ' ', or end of string */ if (sublen == 0) { + if (out_local != NULL) { + *out_local = 0; + } + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_D; + } goto finish; } else if (*substr != 'T' && *substr != ' ') { @@ -2489,6 +2575,9 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) --sublen; } else { + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_h; + } goto parse_timezone; } @@ -2519,6 +2608,9 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) --sublen; } else { + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_m; + } goto parse_timezone; } @@ -2549,44 +2641,78 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out) --sublen; } else { + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_s; + } goto parse_timezone; } /* PARSE THE MICROSECONDS (0 to 6 digits) */ + numdigits = 0; for (i = 0; i < 6; ++i) { out->us *= 10; if (sublen > 0 && isdigit(*substr)) { out->us += (*substr - '0'); ++substr; --sublen; + ++numdigits; } } if (sublen == 0 || !isdigit(*substr)) { + if (out_bestunit != NULL) { + if (numdigits > 3) { + *out_bestunit = NPY_FR_us; + } + else { + *out_bestunit = NPY_FR_ms; + } + } goto parse_timezone; } /* PARSE THE PICOSECONDS (0 to 6 digits) */ + numdigits = 0; for (i = 0; i < 6; ++i) { out->ps *= 10; if (sublen > 0 && isdigit(*substr)) { out->ps += (*substr - '0'); ++substr; --sublen; + ++numdigits; } } if (sublen == 0 || !isdigit(*substr)) { + if (out_bestunit != NULL) { + if (numdigits > 3) { + *out_bestunit = NPY_FR_ps; + } + else { + *out_bestunit = NPY_FR_ns; + } + } goto parse_timezone; } /* PARSE THE ATTOSECONDS (0 to 6 digits) */ + numdigits = 0; for (i = 0; i < 6; ++i) { out->as *= 10; if (sublen > 0 && isdigit(*substr)) { out->as += (*substr - '0'); ++substr; --sublen; + ++numdigits; + } + } + + if (out_bestunit != NULL) { + if (numdigits > 3) { + *out_bestunit = NPY_FR_as; + } + else { + *out_bestunit = NPY_FR_fs; } } @@ -2642,11 +2768,21 @@ parse_timezone: out->year = tm_.tm_year + 1900; } + /* Since neither "Z" nor a time-zone was specified, it's local */ + if (out_local != NULL) { + *out_local = 1; + } + goto finish; } /* UTC specifier */ if (*substr == 'Z') { + /* "Z" means not local */ + if (out_local != NULL) { + *out_local = 0; + } + if (sublen == 1) { goto finish; } @@ -2658,6 +2794,15 @@ parse_timezone: /* Time zone offset */ else if (*substr == '-' || *substr == '+') { int offset_neg = 0, offset_hour = 0, offset_minute = 0; + + /* + * Since "local" means local with respect to the current + * machine, we say this is non-local. + */ + if (out_local != NULL) { + *out_local = 0; + } + if (*substr == '-') { offset_neg = 1; } @@ -2713,24 +2858,6 @@ parse_timezone: add_minutes_to_datetimestruct(out, -60 * offset_hour - offset_minute); } - /* May have a ' ' followed by an event number */ - if (sublen == 0) { - goto finish; - } - else if (sublen > 0 && *substr == ' ') { - ++substr; - --sublen; - - while (sublen > 0 && isdigit(*substr)) { - out->event = 10 * out->event + (*substr - '0'); - ++substr; - --sublen; - } - } - else { - goto parse_error; - } - /* Skip trailing whitespace */ while (sublen > 0 && isspace(*substr)) { ++substr; @@ -3293,11 +3420,15 @@ string_too_short: * datetime duck typing. The tzinfo time zone conversion would require * this style of access anyway. * + * 'out_bestunit' gives a suggested unit based on whether the object + * was a datetime.date or datetime.datetime object. + * * Returns -1 on error, 0 on success, and 1 (with no error set) * if obj doesn't have the neeeded date or datetime attributes. */ NPY_NO_EXPORT int -convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out) +convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, + NPY_DATETIMEUNIT *out_bestunit) { PyObject *tmp; int isleap; @@ -3364,6 +3495,10 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out) !PyObject_HasAttrString(obj, "minute") || !PyObject_HasAttrString(obj, "second") || !PyObject_HasAttrString(obj, "microsecond")) { + /* The best unit for date is 'D' */ + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_D; + } return 0; } @@ -3465,6 +3600,11 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out) } } + /* The resolution of Python's datetime is 'us' */ + if (out_bestunit != NULL) { + *out_bestunit = NPY_FR_us; + } + return 0; invalid_date: @@ -3482,7 +3622,11 @@ invalid_time: } /* - * Converts a PyObject * into a datetime, in any of the forms supported + * Converts a PyObject * into a datetime, in any of the forms supported. + * + * If the units metadata isn't known ahead of time, set meta->base + * to -1, and this function will populate meta with either default + * values or values from the input object. * * Returns -1 on error, 0 on success. */ @@ -3495,6 +3639,8 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, char *str = NULL; int len = 0; npy_datetimestruct dts; + NPY_DATETIMEUNIT bestunit = -1; + /* Convert to an ASCII string for the date parser */ if (PyUnicode_Check(obj)) { bytes = PyUnicode_AsASCIIString(obj); @@ -3512,12 +3658,19 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, } /* Parse the ISO date */ - if (parse_iso_8601_date(str, len, &dts) < 0) { + if (parse_iso_8601_date(str, len, &dts, NULL, &bestunit, NULL) < 0) { Py_DECREF(bytes); return -1; } Py_DECREF(bytes); + /* Use the detected unit if none was specified */ + if (meta->base == -1) { + meta->base = bestunit; + meta->num = 1; + meta->events = 1; + } + if (convert_datetimestruct_to_datetime(meta, &dts, out) < 0) { return -1; } @@ -3526,10 +3679,22 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, } /* Do no conversion on raw integers */ else if (PyInt_Check(obj)) { + /* Use the default unit if none was specified */ + if (meta->base == -1) { + meta->base = NPY_DATETIME_DEFAULTUNIT; + meta->num = 1; + meta->events = 1; + } *out = PyInt_AS_LONG(obj); return 0; } else if (PyLong_Check(obj)) { + /* Use the default unit if none was specified */ + if (meta->base == -1) { + meta->base = NPY_DATETIME_DEFAULTUNIT; + meta->num = 1; + meta->events = 1; + } *out = PyLong_AsLongLong(obj); return 0; } @@ -3562,7 +3727,18 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, else if (PyArray_IsScalar(obj, Datetime)) { PyDatetimeScalarObject *dts = (PyDatetimeScalarObject *)obj; - return cast_datetime_to_datetime(&dts->obmeta, meta, dts->obval, out); + /* Copy the scalar directly if units weren't specified */ + if (meta->base == -1) { + *meta = dts->obmeta; + *out = dts->obval; + + return 0; + } + /* Otherwise do a casting transformation */ + else { + return cast_datetime_to_datetime(&dts->obmeta, meta, + dts->obval, out); + } } /* Datetime zero-dimensional array */ else if (PyArray_Check(obj) && @@ -3580,18 +3756,36 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, !PyArray_ISNOTSWAPPED(obj), obj); - return cast_datetime_to_datetime(obj_meta, meta, dt, out); + /* Copy the value directly if units weren't specified */ + if (meta->base == -1) { + *meta = *obj_meta; + *out = dt; + + return 0; + } + /* Otherwise do a casting transformation */ + else { + return cast_datetime_to_datetime(obj_meta, meta, dt, out); + } } /* Convert from a Python date or datetime object */ else { int code; npy_datetimestruct dts; + NPY_DATETIMEUNIT bestunit = -1; - code = convert_pydatetime_to_datetimestruct(obj, &dts); + code = convert_pydatetime_to_datetimestruct(obj, &dts, &bestunit); if (code == -1) { return -1; } else if (code == 0) { + /* Use the detected unit if none was specified */ + if (meta->base == -1) { + meta->base = bestunit; + meta->num = 1; + meta->events = 1; + } + if (convert_datetimestruct_to_datetime(meta, &dts, out) < 0) { return -1; } @@ -3608,6 +3802,10 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, /* * Converts a PyObject * into a timedelta, in any of the forms supported * + * If the units metadata isn't known ahead of time, set meta->base + * to -1, and this function will populate meta with either default + * values or values from the input object. + * * Returns -1 on error, 0 on success. */ NPY_NO_EXPORT int @@ -3616,10 +3814,24 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, { /* Do no conversion on raw integers */ if (PyInt_Check(obj)) { + /* Use the default unit if none was specified */ + if (meta->base == -1) { + meta->base = NPY_DATETIME_DEFAULTUNIT; + meta->num = 1; + meta->events = 1; + } + *out = PyInt_AS_LONG(obj); return 0; } else if (PyLong_Check(obj)) { + /* Use the default unit if none was specified */ + if (meta->base == -1) { + meta->base = NPY_DATETIME_DEFAULTUNIT; + meta->num = 1; + meta->events = 1; + } + *out = PyLong_AsLongLong(obj); return 0; } @@ -3627,8 +3839,18 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, else if (PyArray_IsScalar(obj, Timedelta)) { PyTimedeltaScalarObject *dts = (PyTimedeltaScalarObject *)obj; - return cast_timedelta_to_timedelta(&dts->obmeta, meta, - dts->obval, out); + /* Copy the scalar directly if units weren't specified */ + if (meta->base == -1) { + *meta = dts->obmeta; + *out = dts->obval; + + return 0; + } + /* Otherwise do a casting transformation */ + else { + return cast_timedelta_to_timedelta(&dts->obmeta, meta, + dts->obval, out); + } } /* Timedelta zero-dimensional array */ else if (PyArray_Check(obj) && @@ -3646,7 +3868,17 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, !PyArray_ISNOTSWAPPED(obj), obj); - return cast_timedelta_to_timedelta(obj_meta, meta, dt, out); + /* Copy the value directly if units weren't specified */ + if (meta->base == -1) { + *meta = *obj_meta; + *out = dt; + + return 0; + } + /* Otherwise do a casting transformation */ + else { + return cast_timedelta_to_timedelta(obj_meta, meta, dt, out); + } } /* Convert from a Python timedelta object */ else if (PyObject_HasAttrString(obj, "days") && @@ -3694,16 +3926,29 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, } Py_DECREF(tmp); - /* - * Convert to a microseconds timedelta, then cast to the - * desired units. - */ td = days*(24*60*60*1000000LL) + seconds*1000000LL + useconds; - us_meta.base = NPY_FR_us; - us_meta.num = 1; - us_meta.events = 1; - - return cast_timedelta_to_timedelta(&us_meta, meta, td, out); + + /* Use microseconds if none was specified */ + if (meta->base == -1) { + meta->base = NPY_FR_us; + meta->num = 1; + meta->events = 1; + + *out = td; + + return 0; + } + else { + /* + * Convert to a microseconds timedelta, then cast to the + * desired units. + */ + us_meta.base = NPY_FR_us; + us_meta.num = 1; + us_meta.events = 1; + + return cast_timedelta_to_timedelta(&us_meta, meta, td, out); + } } PyErr_SetString(PyExc_ValueError, diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index c024dcccc..50e51e591 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -2521,50 +2521,26 @@ static PyObject * } } else { - ret->obmeta.base = NPY_DATETIME_DEFAULTUNIT; - ret->obmeta.num = 1; - ret->obmeta.events = 1; + /* + * A unit of -1 signals that convert_pyobject_to_datetime + * should populate. + */ + ret->obmeta.base = -1; } 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; + if (ret->obmeta.base == -1) { + ret->obmeta.base = NPY_DATETIME_DEFAULTUNIT; + ret->obmeta.num = 1; + ret->obmeta.events = 1; } - PyArray_DESCR(obj)->f->copyswap(&ret->obval, - PyArray_DATA(obj), - !PyArray_ISNOTSWAPPED(obj), - obj); - ret->obmeta = *meta; + + ret->obval = 0; } - else { - if (convert_pyobject_to_@name@(&ret->obmeta, obj, &ret->obval) + else if (convert_pyobject_to_@name@(&ret->obmeta, obj, &ret->obval) < 0) { - Py_DECREF(ret); - return NULL; - } + Py_DECREF(ret); + return NULL; } return (PyObject *)ret; diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index bb6594a9a..9fb249340 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -80,6 +80,9 @@ class TestDateTime(TestCase): assert_equal(np.datetime64('1950-03-12', 's'), np.datetime64('1950-03-12', 'm')) + # Default construction means 0 + assert_equal(np.datetime64(), np.datetime64(0)) + # 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') @@ -110,7 +113,6 @@ class TestDateTime(TestCase): np.datetime64(datetime.datetime(1980,1,25, 14,36,22,500000))) - def test_timedelta_scalar_construction(self): # Construct with different units assert_equal(np.timedelta64(7, 'D'), @@ -118,6 +120,9 @@ class TestDateTime(TestCase): assert_equal(np.timedelta64(120, 's'), np.timedelta64(2, 'm')) + # Default construction means 0 + assert_equal(np.timedelta64(), np.timedelta64(0)) + # When constructing from a scalar or zero-dimensional array, # it either keeps the units or you can override them. a = np.timedelta64(2, 'h') @@ -158,6 +163,86 @@ class TestDateTime(TestCase): assert_equal(np.timedelta64(28, 'W'), np.timedelta64(datetime.timedelta(weeks=28))) + def test_timedelta_scalar_construction_units(self): + # String construction detecting units + assert_equal(np.datetime64('2010').dtype, + np.dtype('M8[Y]')) + assert_equal(np.datetime64('2010-03').dtype, + np.dtype('M8[M]')) + assert_equal(np.datetime64('2010-03-12').dtype, + np.dtype('M8[D]')) + assert_equal(np.datetime64('2010-03-12T17').dtype, + np.dtype('M8[h]')) + assert_equal(np.datetime64('2010-03-12T17:15Z').dtype, + np.dtype('M8[m]')) + assert_equal(np.datetime64('2010-03-12T17:15:08Z').dtype, + np.dtype('M8[s]')) + + assert_equal(np.datetime64('2010-03-12T17:15:08.1Z').dtype, + np.dtype('M8[ms]')) + assert_equal(np.datetime64('2010-03-12T17:15:08.12Z').dtype, + np.dtype('M8[ms]')) + assert_equal(np.datetime64('2010-03-12T17:15:08.123Z').dtype, + np.dtype('M8[ms]')) + + assert_equal(np.datetime64('2010-03-12T17:15:08.1234Z').dtype, + np.dtype('M8[us]')) + assert_equal(np.datetime64('2010-03-12T17:15:08.12345Z').dtype, + np.dtype('M8[us]')) + assert_equal(np.datetime64('2010-03-12T17:15:08.123456Z').dtype, + np.dtype('M8[us]')) + + assert_equal(np.datetime64('1970-01-01T00:00:02.1234567Z').dtype, + np.dtype('M8[ns]')) + assert_equal(np.datetime64('1970-01-01T00:00:02.12345678Z').dtype, + np.dtype('M8[ns]')) + assert_equal(np.datetime64('1970-01-01T00:00:02.123456789Z').dtype, + np.dtype('M8[ns]')) + + assert_equal(np.datetime64('1970-01-01T00:00:02.1234567890Z').dtype, + np.dtype('M8[ps]')) + assert_equal(np.datetime64('1970-01-01T00:00:02.12345678901Z').dtype, + np.dtype('M8[ps]')) + assert_equal(np.datetime64('1970-01-01T00:00:02.123456789012Z').dtype, + np.dtype('M8[ps]')) + + assert_equal(np.datetime64( + '1970-01-01T00:00:02.1234567890123Z').dtype, + np.dtype('M8[fs]')) + assert_equal(np.datetime64( + '1970-01-01T00:00:02.12345678901234Z').dtype, + np.dtype('M8[fs]')) + assert_equal(np.datetime64( + '1970-01-01T00:00:02.123456789012345Z').dtype, + np.dtype('M8[fs]')) + + assert_equal(np.datetime64( + '1970-01-01T00:00:02.1234567890123456Z').dtype, + np.dtype('M8[as]')) + assert_equal(np.datetime64( + '1970-01-01T00:00:02.12345678901234567Z').dtype, + np.dtype('M8[as]')) + assert_equal(np.datetime64( + '1970-01-01T00:00:02.123456789012345678Z').dtype, + np.dtype('M8[as]')) + + # Python date object + assert_equal(np.datetime64(datetime.date(2010,4,16)).dtype, + np.dtype('M8[D]')) + + # Python datetime object + assert_equal(np.datetime64( + datetime.datetime(2010,4,16,13,45,18)).dtype, + np.dtype('M8[us]')) + + # 'today' special value + assert_equal(np.datetime64('today').dtype, + np.dtype('M8[D]')) + + # 'now' special value + assert_equal(np.datetime64('now').dtype, + np.dtype('M8[s]')) + def test_datetime_nat_casting(self): a = np.array('NaT', dtype='M8[D]') b = np.datetime64('NaT', '[D]') |