Skip to content

Datetime dev 2 #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 22, 2011
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions numpy/core/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ if ENABLE_SEPARATE_COMPILATION:
pjoin('src', 'multiarray', 'hashdescr.c'),
pjoin('src', 'multiarray', 'arrayobject.c'),
pjoin('src', 'multiarray', 'datetime.c'),
pjoin('src', 'multiarray', 'datetime_strings.c'),
pjoin('src', 'multiarray', 'datetime_busday.c'),
pjoin('src', 'multiarray', 'datetime_busdaycal.c'),
pjoin('src', 'multiarray', 'numpyos.c'),
Expand Down
13 changes: 7 additions & 6 deletions numpy/core/arrayprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def _array2string(a, max_line_width, precision, suppress_small, separator=' ',
'complexfloat' : ComplexFormat(data, precision,
suppress_small),
'longcomplexfloat' : LongComplexFormat(precision),
'datetime' : DatetimeFormat(True, None, -1),
'datetime' : DatetimeFormat(),
'timedelta' : TimedeltaFormat(data),
'numpystr' : repr,
'str' : str}
Expand Down Expand Up @@ -698,16 +698,17 @@ def __call__(self, x):
return r + i

class DatetimeFormat(object):
def __init__(self, uselocaltime=True, overrideunit=None, tzoffset=-1):
self.local = uselocaltime
def __init__(self, overrideunit=None,
timezone='local', casting='same_kind'):
self.timezone = timezone
self.unit = overrideunit
self.tzoffset = -1
self.casting = casting

def __call__(self, x):
return "'%s'" % datetime_as_string(x,
local=self.local,
unit=self.unit,
tzoffset=self.tzoffset)
timezone=self.timezone,
casting=self.casting)

class TimedeltaFormat(object):
def __init__(self, data):
Expand Down
1 change: 1 addition & 0 deletions numpy/core/code_generators/genapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
join('multiarray', 'conversion_utils.c'),
join('multiarray', 'buffer.c'),
join('multiarray', 'datetime.c'),
join('multiarray', 'datetime_strings.c'),
join('multiarray', 'datetime_busday.c'),
join('multiarray', 'datetime_busdaycal.c'),
join('multiarray', 'nditer.c.src'),
Expand Down
8 changes: 4 additions & 4 deletions numpy/core/include/numpy/ndarraytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ enum NPY_TYPES { NPY_BOOL=0,
NPY_NTYPES_ABI_COMPATIBLE=21
};

#define NPY_METADATA_DTSTR "__frequency__"
#define NPY_METADATA_DTSTR "__timeunit__"

/* basetype array priority */
#define NPY_PRIORITY 0.0
Expand Down Expand Up @@ -718,7 +718,9 @@ typedef struct {
NPY_DATETIMEUNIT base;
int num;
/*
* 'den' is unused, kept here for ABI compatibility with 1.6.
* 'den' and 'events are unused, kept here for ABI
* compatibility with 1.6.
*
* TODO: Remove for 2.0.
*/
int den;
Expand All @@ -732,14 +734,12 @@ typedef struct {
typedef struct {
npy_int64 year;
npy_int32 month, day, hour, min, sec, us, ps, as;
npy_int32 event;
} npy_datetimestruct;

/* TO BE REMOVED - NOT USED INTERNALLY. */
typedef struct {
npy_int64 day;
npy_int32 sec, us, ps, as;
npy_int32 event;
} npy_timedeltastruct;

/* TO BE REMOVED - NOT USED INTERNALLY. */
Expand Down
1 change: 1 addition & 0 deletions numpy/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,7 @@ def get_mathlib_info(*args):
join('src', 'multiarray', 'numpymemoryview.c'),
join('src', 'multiarray', 'buffer.c'),
join('src', 'multiarray', 'datetime.c'),
join('src', 'multiarray', 'datetime_strings.c'),
join('src', 'multiarray', 'datetime_busday.c'),
join('src', 'multiarray', 'datetime_busdaycal.c'),
join('src', 'multiarray', 'numpyos.c'),
Expand Down
167 changes: 84 additions & 83 deletions numpy/core/src/multiarray/_datetime.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
#ifndef _NPY_PRIVATE__DATETIME_H_
#define _NPY_PRIVATE__DATETIME_H_

NPY_NO_EXPORT char *_datetime_strings[NPY_DATETIME_NUMUNITS];

NPY_NO_EXPORT int _days_per_month_table[2][12];

NPY_NO_EXPORT void
numpy_pydatetime_import();

/*
* Returns 1 if the given year is a leap year, 0 otherwise.
*/
NPY_NO_EXPORT int
is_leapyear(npy_int64 year);

/*
* Calculates the days offset from the 1970 epoch.
*/
NPY_NO_EXPORT npy_int64
get_datetimestruct_days(const npy_datetimestruct *dts);

/*
* Creates a datetime or timedelta dtype using a copy of the provided metadata.
*/
Expand Down Expand Up @@ -99,10 +115,48 @@ convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta,
*/
NPY_NO_EXPORT npy_bool
datetime_metadata_divides(
PyArray_Descr *dividend,
PyArray_Descr *divisor,
PyArray_DatetimeMetaData *dividend,
PyArray_DatetimeMetaData *divisor,
int strict_with_nonlinear_units);

/*
* This provides the casting rules for the DATETIME data type units.
*
* Notably, there is a barrier between 'date units' and 'time units'
* for all but 'unsafe' casting.
*/
NPY_NO_EXPORT npy_bool
can_cast_datetime64_units(NPY_DATETIMEUNIT src_unit,
NPY_DATETIMEUNIT dst_unit,
NPY_CASTING casting);

/*
* This provides the casting rules for the DATETIME data type metadata.
*/
NPY_NO_EXPORT npy_bool
can_cast_datetime64_metadata(PyArray_DatetimeMetaData *src_meta,
PyArray_DatetimeMetaData *dst_meta,
NPY_CASTING casting);

/*
* This provides the casting rules for the TIMEDELTA data type units.
*
* Notably, there is a barrier between the nonlinear years and
* months units, and all the other units.
*/
NPY_NO_EXPORT npy_bool
can_cast_timedelta64_units(NPY_DATETIMEUNIT src_unit,
NPY_DATETIMEUNIT dst_unit,
NPY_CASTING casting);

/*
* This provides the casting rules for the TIMEDELTA data type metadata.
*/
NPY_NO_EXPORT npy_bool
can_cast_timedelta64_metadata(PyArray_DatetimeMetaData *src_meta,
PyArray_DatetimeMetaData *dst_meta,
NPY_CASTING casting);

/*
* Computes the GCD of the two date-time metadata values. Raises
* an exception if there is no reasonable GCD, such as with
Expand All @@ -119,11 +173,7 @@ compute_datetime_metadata_greatest_common_divisor_capsule(

/*
* Computes the conversion factor to convert data with 'src_meta' metadata
* into data with 'dst_meta' metadata, not taking into account the events.
*
* To convert a npy_datetime or npy_timedelta, first the event number needs
* to be divided away, then it needs to be scaled by num/denom, and
* finally the event number can be added back in.
* into data with 'dst_meta' metadata.
*
* If overflow occurs, both out_num and out_denom are set to 0, but
* no error is set.
Expand Down Expand Up @@ -156,6 +206,13 @@ convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple,
NPY_NO_EXPORT PyObject *
convert_datetime_metadata_tuple_to_metacobj(PyObject *tuple);

/*
* Gets a tzoffset in minutes by calling the fromutc() function on
* the Python datetime.tzinfo object.
*/
NPY_NO_EXPORT int
get_tzoffset_from_pytzinfo(PyObject *timezone, npy_datetimestruct *dts);

/*
* Converts an input object into datetime metadata. The input
* may be either a string or a tuple.
Expand All @@ -170,7 +227,7 @@ 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.
* If 'skip_brackets' is true, skips the '[]'.
*
* This function steals the reference 'ret'
*/
Expand All @@ -179,90 +236,23 @@ append_metastr_to_string(PyArray_DatetimeMetaData *meta,
int skip_brackets,
PyObject *ret);

/*
* Provides a string length to use for converting datetime
* objects with the given local and unit settings.
*/
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.
* '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,
* 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_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.
*
* 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.
*
* 'tzoffset' is used if 'local' is enabled, and 'tzoffset' is
* set to a value other than -1. This is a manual override for
* the local time zone to use, as an offset in minutes.
*
* 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, 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.
*
* If 'apply_tzinfo' is 1, this function uses the tzinfo to convert
* to UTC time, otherwise it returns the struct with the local time.
*
* 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,
NPY_DATETIMEUNIT *out_bestunit);
NPY_DATETIMEUNIT *out_bestunit,
int apply_tzinfo);

/*
* Converts a PyObject * into a datetime, in any of the forms supported.
Expand All @@ -271,11 +261,17 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out,
* to -1, and this function will populate meta with either default
* values or values from the input object.
*
* The 'casting' parameter is used to control what kinds of inputs
* are accepted, and what happens. For example, with 'unsafe' casting,
* unrecognized inputs are converted to 'NaT' instead of throwing an error,
* while with 'safe' casting an error will be thrown if any precision
* from the input will be thrown away.
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the casting rules described in more detail somewhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at the moment, but it should go in the documentation, yes.

* Returns -1 on error, 0 on success.
*/
NPY_NO_EXPORT int
convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj,
npy_datetime *out);
NPY_CASTING casting, npy_datetime *out);

/*
* Converts a PyObject * into a timedelta, in any of the forms supported
Expand All @@ -284,12 +280,17 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj,
* to -1, and this function will populate meta with either default
* values or values from the input object.
*
* The 'casting' parameter is used to control what kinds of inputs
* are accepted, and what happens. For example, with 'unsafe' casting,
* unrecognized inputs are converted to 'NaT' instead of throwing an error,
* while with 'safe' casting an error will be thrown if any precision
* from the input will be thrown away.
*
* Returns -1 on error, 0 on success.
*/
NPY_NO_EXPORT int
convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj,
npy_timedelta *out);

NPY_CASTING casting, npy_timedelta *out);

/*
* Converts a datetime into a PyObject *.
Expand Down
7 changes: 4 additions & 3 deletions numpy/core/src/multiarray/arraytypes.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,8 @@ DATETIME_setitem(PyObject *op, char *ov, PyArrayObject *ap) {
}

/* Convert the object into a NumPy datetime */
if (convert_pyobject_to_datetime(meta, op, &temp) < 0) {
if (convert_pyobject_to_datetime(meta, op,
NPY_SAME_KIND_CASTING, &temp) < 0) {
return -1;
}

Expand Down Expand Up @@ -845,7 +846,8 @@ TIMEDELTA_setitem(PyObject *op, char *ov, PyArrayObject *ap) {
}

/* Convert the object into a NumPy datetime */
if (convert_pyobject_to_timedelta(meta, op, &temp) < 0) {
if (convert_pyobject_to_timedelta(meta, op,
NPY_SAME_KIND_CASTING, &temp) < 0) {
return -1;
}

Expand Down Expand Up @@ -3436,7 +3438,6 @@ _init_datetime_descr(PyArray_Descr *descr)
dt_data = PyArray_malloc(sizeof(PyArray_DatetimeMetaData));
dt_data->base = NPY_DATETIME_DEFAULTUNIT;
dt_data->num = 1;
dt_data->events = 1;

/* FIXME
* There is no error check here and no way to indicate an error
Expand Down
Loading