summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wiebe <mwiebe@enthought.com>2011-06-03 10:52:13 -0500
committerMark Wiebe <mwiebe@enthought.com>2011-06-03 10:52:13 -0500
commit681adf030eda8e9097cf53856dda6426b2351485 (patch)
tree451e28ff6c2ca6de869cd9c6bdd3800bfc8a00f3
parent8f35cd72500420512915f75cbefe7dd9fc12a009 (diff)
downloadnumpy-681adf030eda8e9097cf53856dda6426b2351485.tar.gz
ENH: datetime: Got repr and str for datetime scalars working
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h15
-rw-r--r--numpy/core/src/multiarray/_datetime.h24
-rw-r--r--numpy/core/src/multiarray/arrayobject.c5
-rw-r--r--numpy/core/src/multiarray/datetime.c512
-rw-r--r--numpy/core/src/multiarray/descriptor.c20
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src168
-rw-r--r--numpy/core/tests/test_datetime.py19
7 files changed, 714 insertions, 49 deletions
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 13a4fb7a5..e69ff38b9 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -215,6 +215,19 @@ typedef enum {
/* The special not-a-time (NaT) value */
#define NPY_DATETIME_NAT NPY_MIN_INT64
+/*
+ * Theoretical maximum length of a DATETIME ISO 8601 string
+ * YEAR: 21 (64-bit year)
+ * MONTH: 3
+ * DAY: 3
+ * HOURS: 3
+ * MINUTES: 3
+ * SECONDS: 3
+ * ATTOSECONDS: 1 + 3*6
+ * TIMEZONE: 6
+ * NULL TERMINATOR: 1
+ */
+#define NPY_DATETIME_MAX_ISO8601_STRLEN (21+3*5+1+3*6+6+1)
typedef enum {
NPY_FR_Y, /* Years */
@@ -689,7 +702,7 @@ typedef struct {
/*
* This structure contains an exploded view of a date-time value.
- * NaT is represented by year == NPY_MIN_INT64.
+ * NaT is represented by year == NPY_DATETIME_NAT.
*/
typedef struct {
npy_int64 year;
diff --git a/numpy/core/src/multiarray/_datetime.h b/numpy/core/src/multiarray/_datetime.h
index 820b4f7e9..41c6e4ca1 100644
--- a/numpy/core/src/multiarray/_datetime.h
+++ b/numpy/core/src/multiarray/_datetime.h
@@ -166,10 +166,15 @@ convert_pyobject_to_datetime_metadata(PyObject *obj,
* 'ret' is a PyUString containing the datetime string, and this
* function appends the metadata string to it.
*
+ * If 'skip_brackets' is true, skips the '[]' when events == 1.
+ *
* This function steals the reference 'ret'
*/
NPY_NO_EXPORT PyObject *
-append_metastr_to_datetime_typestr(PyArray_Descr *self, PyObject *ret);
+append_metastr_to_string(PyArray_DatetimeMetaData *meta,
+ int skip_brackets,
+ PyObject *ret);
+
/*
* Parses (almost) standard ISO 8601 date strings. The differences are:
@@ -189,6 +194,23 @@ NPY_NO_EXPORT int
parse_iso_8601_date(char *str, int len, npy_datetimestruct *out);
/*
+ * Converts an npy_datetimestruct to an (almost) ISO 8601
+ * NULL-terminated string.
+ *
+ * If 'local' is non-zero, it produces a string in local time with
+ * a +-#### timezone offset, otherwise it uses timezone Z (UTC).
+ *
+ * 'base' restricts the output to that unit. Set 'base' to
+ * -1 to auto-detect a base after which all the values are zero.
+ *
+ * Returns 0 on success, -1 on failure (for example if the output
+ * string was too short).
+ */
+NPY_NO_EXPORT int
+make_iso_8601_date(npy_datetimestruct *dts, char *outstr, int outlen,
+ int local, NPY_DATETIMEUNIT base);
+
+/*
* Tests for and converts a Python datetime.datetime or datetime.date
* object into a NumPy npy_datetimestruct.
*
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c
index f75759593..ba4b2fc59 100644
--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -340,8 +340,7 @@ array_repr_builtin(PyArrayObject *self, int repr)
max_n = PyArray_NBYTES(self)*4*sizeof(char) + 7;
if ((string = (char *)_pya_malloc(max_n)) == NULL) {
- PyErr_SetString(PyExc_MemoryError, "out of memory");
- return NULL;
+ return PyErr_NoMemory();
}
if (repr) {
@@ -407,6 +406,8 @@ PyArray_SetStringFunction(PyObject *op, int repr)
/*NUMPY_API
* This function is scheduled to be removed
+ *
+ * TO BE REMOVED - NOT USED INTERNALLY.
*/
NPY_NO_EXPORT void
PyArray_SetDatetimeParseFunction(PyObject *op)
diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c
index 7923bac66..d20dac925 100644
--- a/numpy/core/src/multiarray/datetime.c
+++ b/numpy/core/src/multiarray/datetime.c
@@ -2226,40 +2226,54 @@ convert_pyobject_to_datetime_metadata(PyObject *obj,
* 'ret' is a PyUString containing the datetime string, and this
* function appends the metadata string to it.
*
+ * If 'skip_brackets' is true, skips the '[]' when events == 1.
+ *
* This function steals the reference 'ret'
*/
NPY_NO_EXPORT PyObject *
-append_metastr_to_datetime_typestr(PyArray_Descr *self, PyObject *ret)
+append_metastr_to_string(PyArray_DatetimeMetaData *meta,
+ int skip_brackets,
+ PyObject *ret)
{
- PyObject *tmp;
PyObject *res;
int num, events;
char *basestr;
- PyArray_DatetimeMetaData *dt_data;
- dt_data = get_datetime_metadata_from_dtype(self);
- if (dt_data == NULL) {
- Py_DECREF(ret);
+ if (ret == NULL) {
return NULL;
}
- num = dt_data->num;
- events = dt_data->events;
- basestr = _datetime_strings[dt_data->base];
+ num = meta->num;
+ events = meta->events;
+ if (meta->base >= 0 && meta->base < NPY_DATETIME_NUMUNITS) {
+ basestr = _datetime_strings[meta->base];
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "NumPy datetime metadata is corrupted");
+ return NULL;
+ }
if (num == 1) {
- tmp = PyUString_FromString(basestr);
+ if (skip_brackets && events == 1) {
+ res = PyUString_FromFormat("%s", basestr);
+ }
+ else {
+ res = PyUString_FromFormat("[%s]", basestr);
+ }
}
else {
- tmp = PyUString_FromFormat("%d%s", num, basestr);
+ if (skip_brackets && events == 1) {
+ res = PyUString_FromFormat("%d%s", num, basestr);
+ }
+ else {
+ res = PyUString_FromFormat("[%d%s]", num, basestr);
+ }
}
- res = PyUString_FromString("[");
- PyUString_ConcatAndDel(&res, tmp);
- PyUString_ConcatAndDel(&res, PyUString_FromString("]"));
if (events != 1) {
- tmp = PyUString_FromFormat("//%d", events);
- PyUString_ConcatAndDel(&res, tmp);
+ PyUString_ConcatAndDel(&res,
+ PyUString_FromFormat("//%d", events));
}
PyUString_ConcatAndDel(&ret, res);
return ret;
@@ -2275,7 +2289,7 @@ datetimestruct_timezone_offset(npy_datetimestruct *dts, int minutes)
int isleap;
/* MINUTES */
- dts->min += minutes;
+ dts->min -= minutes;
while (dts->min < 0) {
dts->min += 60;
dts->hour--;
@@ -2812,6 +2826,470 @@ error:
}
/*
+ * Converts an npy_datetimestruct to an (almost) ISO 8601
+ * NULL-terminated string.
+ *
+ * If 'local' is non-zero, it produces a string in local time with
+ * a +-#### timezone offset, otherwise it uses timezone Z (UTC).
+ *
+ * 'base' restricts the output to that unit. Set 'base' to
+ * -1 to auto-detect a base after which all the values are zero.
+ *
+ * Returns 0 on success, -1 on failure (for example if the output
+ * string was too short).
+ */
+NPY_NO_EXPORT int
+make_iso_8601_date(npy_datetimestruct *dts, char *outstr, int outlen,
+ int local, NPY_DATETIMEUNIT base)
+{
+ npy_datetimestruct dts_local;
+ int timezone_offset = 0;
+
+ char *substr = outstr, sublen = outlen;
+ int tmplen;
+
+ /* Handle NaT */
+ if (dts->year == NPY_DATETIME_NAT) {
+ if (outlen < 4) {
+ goto string_too_short;
+ }
+ outstr[0] = 'N';
+ outstr[0] = 'a';
+ outstr[0] = 'T';
+ outstr[0] = '\0';
+
+ return 0;
+ }
+
+ /* Only do local time within a reasonable year range */
+ if (dts->year <= 1900 || dts->year >= 10000) {
+ local = 0;
+ }
+
+ /* Automatically detect a good unit */
+ if (base == -1) {
+ if (dts->as % 1000 != 0) {
+ base = NPY_FR_as;
+ }
+ else if (dts->as != 0) {
+ base = NPY_FR_fs;
+ }
+ else if (dts->ps % 1000 != 0) {
+ base = NPY_FR_ps;
+ }
+ else if (dts->ps != 0) {
+ base = NPY_FR_ns;
+ }
+ else if (dts->us % 1000 != 0) {
+ base = NPY_FR_us;
+ }
+ else if (dts->us != 0) {
+ base = NPY_FR_ms;
+ }
+ else if (dts->sec != 0) {
+ base = NPY_FR_s;
+ }
+ /* If 'local' is enabled, always include minutes with hours */
+ else if (dts->min != 0 || (local && dts->hour != 0)) {
+ base = NPY_FR_m;
+ }
+ else if (dts->hour != 0) {
+ base = NPY_FR_h;
+ }
+ /* 'local' has no effect on date-only printing */
+ else if (dts->day != 1) {
+ base = NPY_FR_D;
+ }
+ else if (dts->month != 1) {
+ base = NPY_FR_M;
+ }
+ else {
+ base = NPY_FR_Y;
+ }
+ }
+ /*
+ * Print business days and weeks with the same precision as days.
+ *
+ * TODO: Could print weeks with YYYY-Www format if the week
+ * epoch is a Monday.
+ */
+ else if (base == NPY_FR_B || base == NPY_FR_W) {
+ base = NPY_FR_D;
+ }
+
+ /* Printed dates have no time zone */
+ if (base < NPY_FR_h) {
+ local = 0;
+ }
+
+ /* Use the C API to convert from UTC to local time */
+ if (local) {
+ time_t rawtime = 0, localrawtime;
+ struct tm tm_;
+
+ /*
+ * Convert everything in 'dts' to a time_t, to minutes precision.
+ * This is POSIX time, which skips leap-seconds, but because
+ * we drop the seconds value from the npy_datetimestruct, everything
+ * is ok for this operation.
+ */
+ rawtime = (time_t)get_datetimestruct_days(dts) * 24 * 60 * 60;
+ rawtime += dts->hour * 60 * 60;
+ rawtime += dts->min * 60;
+
+ /* localtime converts a 'time_t' into a local 'struct tm' */
+#if defined(_WIN32)
+ if (localtime_s(&tm_, &rawtime) != 0) {
+ PyErr_SetString(PyExc_OSError, "Failed to use localtime_s to "
+ "get a local time");
+ return -1;
+ }
+#else
+ /* Other platforms may require something else */
+ if (localtime_r(&rawtime, &tm_) == NULL) {
+ PyErr_SetString(PyExc_OSError, "Failed to use localtime_r to "
+ "get a local time");
+ return -1;
+ }
+#endif
+ /* Make a copy of the npy_datetimestruct we can modify */
+ dts_local = *dts;
+
+ /* Copy back all the values except seconds */
+ dts_local.min = tm_.tm_min;
+ dts_local.hour = tm_.tm_hour;
+ dts_local.day = tm_.tm_mday;
+ dts_local.month = tm_.tm_mon + 1;
+ dts_local.year = tm_.tm_year + 1900;
+
+ /* Extract the timezone offset that was applied */
+ rawtime /= 60;
+ localrawtime = (time_t)get_datetimestruct_days(&dts_local) * 24 * 60;
+ localrawtime += dts_local.hour * 60;
+ localrawtime += dts_local.min;
+
+ timezone_offset = localrawtime - rawtime;
+
+ /* Set dts to point to our local time instead of the UTC time */
+ dts = &dts_local;
+ }
+
+ /* YEAR */
+#ifdef _WIN32
+ tmplen = _snprintf(substr, sublen, "%04" NPY_INT64_FMT, dts->year);
+#else
+ tmplen = snprintf(substr, sublen, "%04" NPY_INT64_FMT, dts->year);
+#endif
+ /* If it ran out of space or there isn't space for the NULL terminator */
+ if (tmplen < 0 || tmplen >= sublen) {
+ goto string_too_short;
+ }
+ substr += tmplen;
+ sublen -= tmplen;
+
+ /* Stop if the unit is years */
+ if (base == NPY_FR_Y) {
+ *substr = '\0';
+ return 0;
+ }
+
+ /* MONTH */
+ substr[0] = '-';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->month / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->month % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is months */
+ if (base == NPY_FR_M) {
+ *substr = '\0';
+ return 0;
+ }
+
+ /* DAY */
+ substr[0] = '-';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->day / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->day % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is days */
+ if (base == NPY_FR_D) {
+ *substr = '\0';
+ return 0;
+ }
+
+ /* HOUR */
+ substr[0] = 'T';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->hour / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->hour % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is hours */
+ if (base == NPY_FR_h) {
+ goto add_time_zone;
+ }
+
+ /* MINUTE */
+ substr[0] = ':';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->min / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->min % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is minutes */
+ if (base == NPY_FR_m) {
+ goto add_time_zone;
+ }
+
+ /* SECOND */
+ substr[0] = ':';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->sec / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->sec % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is seconds */
+ if (base == NPY_FR_s) {
+ goto add_time_zone;
+ }
+
+ /* MILLISECOND */
+ substr[0] = '.';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->us / 100000) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->us / 10000) % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr[3] = (char)((dts->us / 1000) % 10 + '0');
+ if (sublen <= 4 ) {
+ goto string_too_short;
+ }
+ substr += 4;
+ sublen -= 4;
+
+ /* Stop if the unit is milliseconds */
+ if (base == NPY_FR_ms) {
+ goto add_time_zone;
+ }
+
+ /* MICROSECOND */
+ substr[0] = (char)((dts->us / 100) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->us / 10) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)(dts->us % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is microseconds */
+ if (base == NPY_FR_us) {
+ goto add_time_zone;
+ }
+
+ /* NANOSECOND */
+ substr[0] = (char)((dts->ps / 100000) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->ps / 10000) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->ps / 1000) % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is nanoseconds */
+ if (base == NPY_FR_ns) {
+ goto add_time_zone;
+ }
+
+ /* PICOSECOND */
+ substr[0] = (char)((dts->ps / 100) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->ps / 10) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)(dts->ps % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is picoseconds */
+ if (base == NPY_FR_ps) {
+ goto add_time_zone;
+ }
+
+ /* FEMTOSECOND */
+ substr[0] = (char)((dts->as / 100000) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->as / 10000) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->as / 1000) % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is femtoseconds */
+ if (base == NPY_FR_fs) {
+ goto add_time_zone;
+ }
+
+ /* ATTOSECOND */
+ substr[0] = (char)((dts->as / 100) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->as / 10) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)(dts->as % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+add_time_zone:
+ if (local) {
+ /* Add the +/- sign */
+ if (timezone_offset < 0) {
+ substr[0] = '-';
+ timezone_offset = -timezone_offset;
+ }
+ else {
+ substr[0] = '+';
+ }
+ if (sublen <= 1) {
+ goto string_too_short;
+ }
+ substr += 1;
+ sublen -= 1;
+
+ /* Add the timezone offset */
+ substr[0] = (char)((timezone_offset / (10*60)) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((timezone_offset / 60) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)(((timezone_offset % 60) / 10) % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr[3] = (char)((timezone_offset % 60) % 10 + '0');
+ if (sublen <= 4 ) {
+ goto string_too_short;
+ }
+ substr += 4;
+ sublen -= 4;
+ }
+ /* UTC "Zulu" time */
+ else {
+ substr[0] = 'Z';
+ if (sublen <= 1) {
+ goto string_too_short;
+ }
+ substr += 1;
+ sublen -= 1;
+ }
+
+ /* Add a NULL terminator, and return */
+ substr[0] = '\0';
+
+ return 0;
+
+string_too_short:
+ /* Put a NULL terminator on anyway */
+ if (outlen > 0) {
+ outstr[outlen-1] = '\0';
+ }
+
+ PyErr_Format(PyExc_RuntimeError,
+ "The string provided for NumPy ISO datetime formatting "
+ "was too short, with length %d",
+ outlen);
+ return -1;
+}
+
+/*
* Tests for and converts a Python datetime.datetime or datetime.date
* object into a NumPy npy_datetimestruct.
*
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c
index d42624f68..1fe8f748d 100644
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -1319,7 +1319,15 @@ arraydescr_protocol_typestr_get(PyArray_Descr *self)
ret = PyUString_FromFormat("%c%c%d", endian, basic_, size);
if (PyDataType_ISDATETIME(self)) {
- ret = append_metastr_to_datetime_typestr(self, ret);
+ PyArray_DatetimeMetaData *meta;
+
+ meta = get_datetime_metadata_from_dtype(self);
+ if (meta == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ ret = append_metastr_to_string(meta, 0, ret);
}
return ret;
@@ -1362,7 +1370,15 @@ arraydescr_typename_get(PyArray_Descr *self)
PyUString_ConcatAndDel(&res, p);
}
if (PyDataType_ISDATETIME(self)) {
- res = append_metastr_to_datetime_typestr(self, res);
+ PyArray_DatetimeMetaData *meta;
+
+ meta = get_datetime_metadata_from_dtype(self);
+ if (meta == NULL) {
+ Py_DECREF(res);
+ return NULL;
+ }
+
+ res = append_metastr_to_string(meta, 0, res);
}
return res;
diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src
index 66969fbb2..4203ca37b 100644
--- a/numpy/core/src/multiarray/scalartypes.c.src
+++ b/numpy/core/src/multiarray/scalartypes.c.src
@@ -334,15 +334,13 @@ gentype_nonzero_number(PyObject *m1)
static PyObject *
gentype_str(PyObject *self)
{
- PyArrayObject *arr;
- PyObject *ret;
+ PyObject *arr, *ret = NULL;
- arr = (PyArrayObject *)PyArray_FromScalar(self, NULL);
- if (arr == NULL) {
- return NULL;
+ arr = PyArray_FromScalar(self, NULL);
+ if (arr != NULL) {
+ ret = PyObject_Str((PyObject *)arr);
+ Py_DECREF(arr);
}
- ret = PyObject_Str((PyObject *)arr);
- Py_DECREF(arr);
return ret;
}
@@ -350,15 +348,14 @@ gentype_str(PyObject *self)
static PyObject *
gentype_repr(PyObject *self)
{
- PyArrayObject *arr;
- PyObject *ret;
+ PyObject *arr, *ret = NULL;
- arr = (PyArrayObject *)PyArray_FromScalar(self, NULL);
- if (arr == NULL) {
- return NULL;
+ arr = PyArray_FromScalar(self, NULL);
+ if (arr != NULL) {
+ /* XXX: Why are we using str here? */
+ ret = PyObject_Str((PyObject *)arr);
+ Py_DECREF(arr);
}
- ret = PyObject_Str((PyObject *)arr);
- Py_DECREF(arr);
return ret;
}
@@ -597,6 +594,145 @@ static PyObject *
}
/**end repeat**/
+static PyObject *
+datetimetype_repr(PyObject *self)
+{
+ PyDatetimeScalarObject *scal;
+ npy_datetimestruct dts;
+ PyObject *ret;
+ char iso[NPY_DATETIME_MAX_ISO8601_STRLEN];
+
+ if (!PyArray_IsScalar(self, Datetime)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Called NumPy datetime repr on a non-datetime type");
+ return NULL;
+ }
+
+ scal = (PyDatetimeScalarObject *)self;
+
+ if (convert_datetime_to_datetimestruct(&scal->obmeta, scal->obval,
+ &dts) < 0) {
+ return NULL;
+ }
+
+ if (make_iso_8601_date(&dts, iso, sizeof(iso), 1, scal->obmeta.base) < 0) {
+ return NULL;
+ }
+
+ ret = PyUString_FromString("numpy.datetime64('");
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString(iso));
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString("','"));
+ ret = append_metastr_to_string(&scal->obmeta, 1, ret);
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString("')"));
+
+ return ret;
+}
+
+static PyObject *
+timedeltatype_repr(PyObject *self)
+{
+ PyTimedeltaScalarObject *scal;
+ PyObject *ret;
+
+ if (!PyArray_IsScalar(self, Timedelta)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Called NumPy timedelta repr on a non-datetime type");
+ return NULL;
+ }
+
+ scal = (PyTimedeltaScalarObject *)self;
+
+ ret = PyUString_FromFormat("numpy.timedelta64(%lld", scal->obval);
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString(",'"));
+ ret = append_metastr_to_string(&scal->obmeta, 1, ret);
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString("')"));
+
+ return ret;
+}
+
+static PyObject *
+datetimetype_str(PyObject *self)
+{
+ PyDatetimeScalarObject *scal;
+ npy_datetimestruct dts;
+ char iso[NPY_DATETIME_MAX_ISO8601_STRLEN];
+
+ if (!PyArray_IsScalar(self, Datetime)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Called NumPy datetime str on a non-datetime type");
+ return NULL;
+ }
+
+ scal = (PyDatetimeScalarObject *)self;
+
+ if (convert_datetime_to_datetimestruct(&scal->obmeta, scal->obval,
+ &dts) < 0) {
+ return NULL;
+ }
+
+ if (make_iso_8601_date(&dts, iso, sizeof(iso), 1, scal->obmeta.base) < 0) {
+ return NULL;
+ }
+
+ return PyUString_FromString(iso);
+}
+
+static char *_datetime_strings[] = {
+ "years",
+ "months",
+ "weeks",
+ "business days",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "picoseconds",
+ "femtoseconds",
+ "attoseconds"
+};
+
+static PyObject *
+timedeltatype_str(PyObject *self)
+{
+ PyTimedeltaScalarObject *scal;
+ PyObject *ret;
+ char *basestr = "invalid";
+
+ if (!PyArray_IsScalar(self, Timedelta)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Called NumPy timedelta str on a non-datetime type");
+ return NULL;
+ }
+
+ scal = (PyTimedeltaScalarObject *)self;
+
+ /* TODO: Account for events, etc */
+
+ if (scal->obmeta.base >= 0 && scal->obmeta.base < NPY_DATETIME_NUMUNITS) {
+ basestr = _datetime_strings[scal->obmeta.base];
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "NumPy datetime metadata is corrupted");
+ return NULL;
+ }
+
+ ret = PyUString_FromFormat("%lld ",
+ (long long)(scal->obval * scal->obmeta.num));
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString(basestr));
+
+ return ret;
+}
+
/* The REPR values are finfo.precision + 2 */
#define HALFPREC_REPR 5
#define HALFPREC_STR 5
@@ -3872,6 +4008,9 @@ initialize_numeric_types(void)
PyDoubleArrType_Type.tp_@name@ = doubletype_@name@;
PyCDoubleArrType_Type.tp_@name@ = cdoubletype_@name@;
+
+ PyDatetimeArrType_Type.tp_@name@ = datetimetype_@name@;
+ PyTimedeltaArrType_Type.tp_@name@ = timedeltatype_@name@;
/**end repeat**/
PyHalfArrType_Type.tp_print = halftype_print;
@@ -3898,6 +4037,7 @@ initialize_numeric_types(void)
PyCLongDoubleArrType_Type.@kind@_@name@ = clongdoubletype_@name@;
/**end repeat**/
+
#if !defined(NPY_PY3K)
/**begin repeat
* #name = long, hex, oct#
diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py
index 1059aeaa1..d2a25c1ff 100644
--- a/numpy/core/tests/test_datetime.py
+++ b/numpy/core/tests/test_datetime.py
@@ -1,4 +1,5 @@
import os, pickle
+import numpy
import numpy as np
from numpy.testing import *
from numpy.compat import asbytes
@@ -767,15 +768,6 @@ class TestDateTime(TestCase):
self.assertRaises(ValueError, lambda : np.dtype('M8[as/10]'))
def test_string_parser_variants(self):
- """
- # Different month formats
- assert_equal(np.array(['1980-02-29'], np.dtype('M8')),
- np.array(['1980-Feb-29'], np.dtype('M8')))
- assert_equal(np.array(['1980-02-29'], np.dtype('M8')),
- np.array(['1980-feb-29'], np.dtype('M8')))
- assert_equal(np.array(['1980-02-29'], np.dtype('M8')),
- np.array(['1980-FEB-29'], np.dtype('M8')))
- """
# Allow space instead of 'T' between date and time
assert_equal(np.array(['1980-02-29T01:02:03'], np.dtype('M8')),
np.array(['1980-02-29 01:02:03'], np.dtype('M8')))
@@ -787,11 +779,14 @@ class TestDateTime(TestCase):
np.array(['-1980-02-29 01:02:03Z'], np.dtype('M8')))
# Time zone offset
assert_equal(np.array(['1980-02-29T02:02:03Z'], np.dtype('M8')),
- np.array(['1980-02-29 00:32:03+0130'], np.dtype('M8')))
+ np.array(['1980-02-29 00:32:03-0130'], np.dtype('M8')))
assert_equal(np.array(['1980-02-28T22:32:03Z'], np.dtype('M8')),
- np.array(['1980-02-29 00:02:03-01:30'], np.dtype('M8')))
+ np.array(['1980-02-29 00:02:03+01:30'], np.dtype('M8')))
assert_equal(np.array(['1980-02-29T02:32:03.506Z'], np.dtype('M8')),
- np.array(['1980-02-29 00:32:03.506+02'], np.dtype('M8')))
+ np.array(['1980-02-29 00:32:03.506-02'], np.dtype('M8')))
+ assert_equal(np.datetime64('1977-03-02T12:30-0230'),
+ np.datetime64('1977-03-02T15:00Z'))
+
def test_string_parser_error_check(self):
# Arbitrary bad string