summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/core/src/multiarray/_datetime.h44
-rw-r--r--numpy/core/src/multiarray/datetime.c323
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src52
-rw-r--r--numpy/core/tests/test_datetime.py87
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]')