summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/neps/datetime-proposal.rst655
-rw-r--r--numpy/core/_internal.py38
-rw-r--r--numpy/core/_mx_datetime_parser.py962
-rw-r--r--numpy/core/arrayprint.py11
-rw-r--r--numpy/core/code_generators/generate_numpy_api.py6
-rw-r--r--numpy/core/code_generators/generate_umath.py166
-rw-r--r--numpy/core/code_generators/numpy_api_order.txt5
-rw-r--r--numpy/core/code_generators/ufunc_api_order.txt1
-rw-r--r--numpy/core/include/numpy/arrayscalars.h12
-rw-r--r--numpy/core/include/numpy/ndarrayobject.h67
-rw-r--r--numpy/core/include/numpy/noprefix.h10
-rw-r--r--numpy/core/include/numpy/npy_common.h39
-rw-r--r--numpy/core/include/numpy/old_defines.h4
-rw-r--r--numpy/core/include/numpy/ufuncobject.h12
-rw-r--r--numpy/core/numerictypes.py13
-rw-r--r--numpy/core/src/multiarray/arrayobject.c18
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src586
-rw-r--r--numpy/core/src/multiarray/common.c111
-rw-r--r--numpy/core/src/multiarray/common.h14
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c4
-rw-r--r--numpy/core/src/multiarray/ctors.c144
-rw-r--r--numpy/core/src/multiarray/ctors.h14
-rw-r--r--numpy/core/src/multiarray/descriptor.c495
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c49
-rw-r--r--numpy/core/src/multiarray/parse_datetime.c862
-rw-r--r--numpy/core/src/multiarray/scalarapi.c16
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src78
-rw-r--r--numpy/core/src/multiarray/testcalcs.py71
-rw-r--r--numpy/core/src/umath/funcs.inc.src2
-rw-r--r--numpy/core/src/umath/loops.c.src167
-rw-r--r--numpy/core/src/umath/ufunc_object.c90
-rw-r--r--numpy/core/tests/test_dtype.py18
-rw-r--r--numpy/lib/tests/test_arrayterator.py4
33 files changed, 4403 insertions, 341 deletions
diff --git a/doc/neps/datetime-proposal.rst b/doc/neps/datetime-proposal.rst
new file mode 100644
index 000000000..39e3f7a98
--- /dev/null
+++ b/doc/neps/datetime-proposal.rst
@@ -0,0 +1,655 @@
+====================================================================
+ A proposal for implementing some date/time types in NumPy
+====================================================================
+
+:Author: Travis Oliphant
+:Contact: oliphant@enthought.com
+:Date: 2009-06-09
+
+Revised only slightly from the third proposal by
+
+:Author: Francesc Alted i Abad
+:Contact: faltet@pytables.com
+:Author: Ivan Vilata i Balaguer
+:Contact: ivan@selidor.net
+:Date: 2008-07-30
+
+
+Executive summary
+=================
+
+A date/time mark is something very handy to have in many fields where
+one has to deal with data sets. While Python has several modules that
+define a date/time type (like the integrated ``datetime`` [1]_ or
+``mx.DateTime`` [2]_), NumPy has a lack of them.
+
+We are proposing the addition of date/time types to fill this gap.
+The requirements for the proposed types are two-fold: 1) they have
+to be fast to operate with and 2) they have to be as compatible as
+possible with the existing ``datetime`` module that comes with Python.
+
+
+Types proposed
+==============
+
+It is virtually impossible to come up with a single date/time type
+that fills the needs of every use case. As a result, we propose two
+general date-time types: 1) ``timedelta64`` -- a relative time and 2)
+``datetime64`` -- an absolute time.
+
+Each of these times are represented internally as 64-bit signed
+integers that refer to a particular unit (hour, minute, microsecond,
+etc.). There are several pre-defined units as well as the ability to
+create rational multiples of these units. A representation is also
+supported such that the stored date-time integer can encode both the
+number of a particular unit as well as a number of sequential events
+tracked for each unit.
+
+The ``datetime64`` represents an absolute time. Internally it is
+represented as the number of time units between the intended time and
+the epoch (12:00am on January 1, 1970 --- POSIX time including its
+lack of leap seconds).
+
+.. Important: The information that provides meaning to the integers stored in
+ the date/time dtypes are stored as metadata which is a new feature to be
+ added to the dtype object.
+
+Time units
+===========
+
+The 64-bit integer time can represent several different basic units as
+well as derived units. The basic units are listed in the following
+table:
+
+======== ================ ======================= ==========================
+ Time unit Time span Time span (years)
+------------------------- ----------------------- --------------------------
+ Code Meaning Relative Time Absolute Time
+======== ================ ======================= ==========================
+ Y year +- 9.2e18 years [9.2e18 BC, 9.2e18 AC]
+ M month +- 7.6e17 years [7.6e17 BC, 7.6e17 AC]
+ W week +- 1.7e17 years [1.7e17 BC, 1.7e17 AC]
+ B business day +- 3.5e16 years [3.5e16 BC, 3.5e16 AC]
+ D day +- 2.5e16 years [2.5e16 BC, 2.5e16 AC]
+ h hour +- 1.0e15 years [1.0e15 BC, 1.0e15 AC]
+ m minute +- 1.7e13 years [1.7e13 BC, 1.7e13 AC]
+ s second +- 2.9e12 years [ 2.9e9 BC, 2.9e9 AC]
+ ms millisecond +- 2.9e9 years [ 2.9e6 BC, 2.9e6 AC]
+ us microsecond +- 2.9e6 years [290301 BC, 294241 AC]
+ ns nanosecond +- 292 years [ 1678 AC, 2262 AC]
+ ps picosecond +- 106 days [ 1969 AC, 1970 AC]
+ fs femtosecond +- 2.6 hours [ 1969 AC, 1970 AC]
+ as attosecond +- 9.2 seconds [ 1969 AC, 1970 AC]
+======== ================ ======================= ==========================
+
+A time unit is specified by a string consisting of a base-type given in
+the above table
+
+Besides these basic code units, the user can create derived units
+consisting of multiples of any basic unit: 100ns, 3M, 15m, etc.
+
+A limited number of divisions of any basic unit can be used to create multiples
+of a higher-resolution unit provided the divisor can be divided evenly into
+the number of higher-resolution units available.
+For example: Y/4 is just short-hand for -> (12M)/4 -> 3M and Y/4 will be represented
+ after creation as 3M
+The first lower unit found to have an even divisor will be chosen
+ (up to 3 lower units). The following standardized definitions are used
+ in this specific case to find acceptable divisors
+
+
+Y - 12M, 52W, 365D
+M - 4W, 30D, 720h
+W - 5B,7D, 168h, 10080m
+B - 24h, 1440m, 86400s
+D - 24h, 1440m, 86400s
+h - 60m, 3600s
+m - 60s, 60000ms
+
+s, ms, us, ns, ps, fs (use 1000 and 1000000 of the next two
+ available lower units respectively).
+
+
+Finally, a date-time data-type can be created with support for tracking
+sequential events within a basic unit: [D]//100, [Y]//4 (notice the
+required brackets). These ``modulo`` event units provide the following
+interpretation to the date-time integer:
+
+ * the divisor is the number of events in each period
+ * the (integer) quotient is the integer number representing the base units
+ * the remainder is the particular event in the period.
+
+Modulo event-units can be combined with any derived units, but brackets
+are required. Thus [100ns]//50 which allows recording 50 events for
+every 100ns so that 0 represents the first event in the first 100ns
+tick, 1 represents the second event in the first 100ns tick, while 50
+represents the first event in the second 100ns tick, and 51 represents
+the second event in the second 100ns tick.
+
+To fully specify a date-time type, the time unit string must be
+combined with either the string for a datetime64 ('M8') or a
+timedelta64 ('m8') using brackets '[]'. Therefore, a fully-specified
+string representing a date-time dtype is 'M8[Y]' or (for a more
+complicated example) 'M8[7s/9]//5'.
+
+If a time unit is not specified, then it defaults to [us]. Thus 'M8' is
+equivalent to 'M8[us]' (except when modulo event-units are desired --
+i.e. you cannot specify 'M8[us]//5' as 'M8//5'
+
+``datetime64``
+==============
+
+This dtype represents a time that is absolute (i.e. not relative). It
+is implemented internally as an ``int64`` type. The integer represents
+units from the internal POSIX epoch (see [3]_). Like POSIX, the
+representation of a date doesn't take leap seconds into account.
+
+In time unit *conversions* and time *representations* (but not in other
+time computations), the value -2**63 (0x8000000000000000) is interpreted
+as an invalid or unknown date, *Not a Time* or *NaT*. See the section
+on time unit conversions for more information.
+
+The value of an absolute date is thus *an integer number of units of
+the chosen time unit* passed since the epoch. If the integer is a
+negative number, then the magnitude of the integer represents the
+number of units prior to the epoch. When working with business days,
+Saturdays and Sundays are simply ignored from the count (i.e. day 3 in
+business days is not Saturday 1970-01-03, but Monday 1970-01-05).
+
+Building a ``datetime64`` dtype
+--------------------------------
+
+The proposed ways to specify the time unit in the dtype constructor are:
+
+Using the long string notation::
+
+ dtype('datetime64[us]')
+
+Using the short string notation::
+
+ dtype('M8[us]')
+
+If a time unit is not specified, then it defaults to [us]. Thus 'M8'
+is equivalent to 'M8[us]'.
+
+
+Setting and getting values
+---------------------------
+
+The objects with this dtype can be set in a series of ways::
+
+ t = numpy.ones(3, dtype='M8[s]')
+ t[0] = 1199164176 # assign to July 30th, 2008 at 17:31:00
+ t[1] = datetime.datetime(2008, 7, 30, 17, 31, 01) # with datetime module
+ t[2] = '2008-07-30T17:31:02' # with ISO 8601
+
+And can be get in different ways too::
+
+ str(t[0]) --> 2008-07-30T17:31:00
+ repr(t[1]) --> datetime64(1199164177, 's')
+ str(t[0].item()) --> 2008-07-30 17:31:00 # datetime module object
+ repr(t[0].item()) --> datetime.datetime(2008, 7, 30, 17, 31) # idem
+ str(t) --> [2008-07-30T17:31:00 2008-07-30T17:31:01 2008-07-30T17:31:02]
+ repr(t) --> array([1199164176, 1199164177, 1199164178],
+ dtype='datetime64[s]')
+
+Comparisons
+------------
+
+The comparisons will be supported too::
+
+ numpy.array(['1980'], 'M8[Y]') == numpy.array(['1979'], 'M8[Y]')
+ --> [False]
+
+including applying broadcasting::
+
+ numpy.array(['1979', '1980'], 'M8[Y]') == numpy.datetime64('1980', 'Y')
+ --> [False, True]
+
+The following should also work::
+
+ numpy.array(['1979', '1980'], 'M8[Y]') == '1980-01-01'
+ --> [False, True]
+
+because the right hand expression can be broadcasted into an array of 2
+elements of dtype 'M8[Y]'.
+
+Compatibility issues
+---------------------
+
+This will be fully compatible with the ``datetime`` class of the
+``datetime`` module of Python only when using a time unit of
+microseconds. For other time units, the conversion process will lose
+precision or will overflow as needed. The conversion from/to a
+``datetime`` object doesn't take leap seconds into account.
+
+
+``timedelta64``
+===============
+
+It represents a time that is relative (i.e. not absolute). It is
+implemented internally as an ``int64`` type.
+
+In time unit *conversions* and time *representations* (but not in other
+time computations), the value -2**63 (0x8000000000000000) is interpreted
+as an invalid or unknown time, *Not a Time* or *NaT*. See the section
+on time unit conversions for more information.
+
+The value of a time delta is *an integer number of units of the
+chosen time unit*.
+
+Building a ``timedelta64`` dtype
+---------------------------------
+
+The proposed ways to specify the time unit in the dtype constructor are:
+
+Using the long string notation::
+
+ dtype('timedelta64[us]')
+
+Using the short string notation::
+
+ dtype('m8[us]')
+
+If a time unit is not specified, then a default of [us] is assumed.
+Thus 'm8' and 'm8[us]' are equivalent.
+
+Setting and getting values
+---------------------------
+
+The objects with this dtype can be set in a series of ways::
+
+ t = numpy.ones(3, dtype='m8[ms]')
+ t[0] = 12 # assign to 12 ms
+ t[1] = datetime.timedelta(0, 0, 13000) # 13 ms
+ t[2] = '0:00:00.014' # 14 ms
+
+And can be get in different ways too::
+
+ str(t[0]) --> 0:00:00.012
+ repr(t[1]) --> timedelta64(13, 'ms')
+ str(t[0].item()) --> 0:00:00.012000 # datetime module object
+ repr(t[0].item()) --> datetime.timedelta(0, 0, 12000) # idem
+ str(t) --> [0:00:00.012 0:00:00.014 0:00:00.014]
+ repr(t) --> array([12, 13, 14], dtype="timedelta64[ms]")
+
+Comparisons
+------------
+
+The comparisons will be supported too::
+
+ numpy.array([12, 13, 14], 'm8[ms]') == numpy.array([12, 13, 13], 'm8[ms]')
+ --> [True, True, False]
+
+or by applying broadcasting::
+
+ numpy.array([12, 13, 14], 'm8[ms]') == numpy.timedelta64(13, 'ms')
+ --> [False, True, False]
+
+The following should work too::
+
+ numpy.array([12, 13, 14], 'm8[ms]') == '0:00:00.012'
+ --> [True, False, False]
+
+because the right hand expression can be broadcasted into an array of 3
+elements of dtype 'm8[ms]'.
+
+Compatibility issues
+---------------------
+
+This will be fully compatible with the ``timedelta`` class of the
+``datetime`` module of Python only when using a time unit of
+microseconds. For other units, the conversion process will loose
+precision or will overflow as needed.
+
+
+Examples of use
+===============
+
+Here is an example of use for the ``datetime64``::
+
+ In [5]: numpy.datetime64(42, 'us')
+ Out[5]: datetime64(42, 'us')
+
+ In [6]: print numpy.datetime64(42, 'us')
+ 1970-01-01T00:00:00.000042 # representation in ISO 8601 format
+
+ In [7]: print numpy.datetime64(367.7, 'D') # decimal part is lost
+ 1971-01-02 # still ISO 8601 format
+
+ In [8]: numpy.datetime('2008-07-18T12:23:18', 'm') # from ISO 8601
+ Out[8]: datetime64(20273063, 'm')
+
+ In [9]: print numpy.datetime('2008-07-18T12:23:18', 'm')
+ Out[9]: 2008-07-18T12:23
+
+ In [10]: t = numpy.zeros(5, dtype="datetime64[ms]")
+
+ In [11]: t[0] = datetime.datetime.now() # setter in action
+
+ In [12]: print t
+ [2008-07-16T13:39:25.315 1970-01-01T00:00:00.000
+ 1970-01-01T00:00:00.000 1970-01-01T00:00:00.000
+ 1970-01-01T00:00:00.000]
+
+ In [13]: repr(t)
+ Out[13]: array([267859210457, 0, 0, 0, 0], dtype="datetime64[ms]")
+
+ In [14]: t[0].item() # getter in action
+ Out[14]: datetime.datetime(2008, 7, 16, 13, 39, 25, 315000)
+
+ In [15]: print t.dtype
+ dtype('datetime64[ms]')
+
+And here it goes an example of use for the ``timedelta64``::
+
+ In [5]: numpy.timedelta64(10, 'us')
+ Out[5]: timedelta64(10, 'us')
+
+ In [6]: print numpy.timedelta64(10, 'us')
+ 0:00:00.000010
+
+ In [7]: print numpy.timedelta64(3600.2, 'm') # decimal part is lost
+ 2 days, 12:00
+
+ In [8]: t1 = numpy.zeros(5, dtype="datetime64[ms]")
+
+ In [9]: t2 = numpy.ones(5, dtype="datetime64[ms]")
+
+ In [10]: t = t2 - t1
+
+ In [11]: t[0] = datetime.timedelta(0, 24) # setter in action
+
+ In [12]: print t
+ [0:00:24.000 0:00:01.000 0:00:01.000 0:00:01.000 0:00:01.000]
+
+ In [13]: print repr(t)
+ Out[13]: array([24000, 1, 1, 1, 1], dtype="timedelta64[ms]")
+
+ In [14]: t[0].item() # getter in action
+ Out[14]: datetime.timedelta(0, 24)
+
+ In [15]: print t.dtype
+ dtype('timedelta64[s]')
+
+
+Operating with date/time arrays
+===============================
+
+``datetime64`` vs ``datetime64``
+--------------------------------
+
+The only arithmetic operation allowed between absolute dates is
+subtraction::
+
+ In [10]: numpy.ones(3, "M8[s]") - numpy.zeros(3, "M8[s]")
+ Out[10]: array([1, 1, 1], dtype=timedelta64[s])
+
+But not other operations::
+
+ In [11]: numpy.ones(3, "M8[s]") + numpy.zeros(3, "M8[s]")
+ TypeError: unsupported operand type(s) for +: 'numpy.ndarray' and 'numpy.ndarray'
+
+Comparisons between absolute dates are allowed.
+
+Casting rules
+~~~~~~~~~~~~~
+
+When operating (basically, only the subtraction will be allowed) two
+absolute times with different unit times, the outcome would be to raise
+an exception. This is because the ranges and time-spans of the different
+time units can be very different, and it is not clear at all what time
+unit will be preferred for the user. For example, this should be
+allowed::
+
+ >>> numpy.ones(3, dtype="M8[Y]") - numpy.zeros(3, dtype="M8[Y]")
+ array([1, 1, 1], dtype="timedelta64[Y]")
+
+But the next should not::
+
+ >>> numpy.ones(3, dtype="M8[Y]") - numpy.zeros(3, dtype="M8[ns]")
+ raise numpy.IncompatibleUnitError # what unit to choose?
+
+
+``datetime64`` vs ``timedelta64``
+---------------------------------
+
+It will be possible to add and subtract relative times from absolute
+dates::
+
+ In [10]: numpy.zeros(5, "M8[Y]") + numpy.ones(5, "m8[Y]")
+ Out[10]: array([1971, 1971, 1971, 1971, 1971], dtype=datetime64[Y])
+
+ In [11]: numpy.ones(5, "M8[Y]") - 2 * numpy.ones(5, "m8[Y]")
+ Out[11]: array([1969, 1969, 1969, 1969, 1969], dtype=datetime64[Y])
+
+But not other operations::
+
+ In [12]: numpy.ones(5, "M8[Y]") * numpy.ones(5, "m8[Y]")
+ TypeError: unsupported operand type(s) for *: 'numpy.ndarray' and 'numpy.ndarray'
+
+Casting rules
+~~~~~~~~~~~~~
+
+In this case the absolute time should have priority for determining the
+time unit of the outcome. That would represent what the people wants to
+do most of the times. For example, this would allow to do::
+
+ >>> series = numpy.array(['1970-01-01', '1970-02-01', '1970-09-01'],
+ dtype='datetime64[D]')
+ >>> series2 = series + numpy.timedelta(1, 'Y') # Add 2 relative years
+ >>> series2
+ array(['1972-01-01', '1972-02-01', '1972-09-01'],
+ dtype='datetime64[D]') # the 'D'ay time unit has been chosen
+
+
+``timedelta64`` vs ``timedelta64``
+----------------------------------
+
+Finally, it will be possible to operate with relative times as if they
+were regular int64 dtypes *as long as* the result can be converted back
+into a ``timedelta64``::
+
+ In [10]: numpy.ones(3, 'm8[us]')
+ Out[10]: array([1, 1, 1], dtype="timedelta64[us]")
+
+ In [11]: (numpy.ones(3, 'm8[M]') + 2) ** 3
+ Out[11]: array([27, 27, 27], dtype="timedelta64[M]")
+
+But::
+
+ In [12]: numpy.ones(5, 'm8') + 1j
+ TypeError: the result cannot be converted into a ``timedelta64``
+
+Casting rules
+~~~~~~~~~~~~~
+
+When combining two ``timedelta64`` dtypes with different time units the
+outcome will be the shorter of both ("keep the precision" rule). For
+example::
+
+ In [10]: numpy.ones(3, 'm8[s]') + numpy.ones(3, 'm8[m]')
+ Out[10]: array([61, 61, 61], dtype="timedelta64[s]")
+
+However, due to the impossibility to know the exact duration of a
+relative year or a relative month, when these time units appear in one
+of the operands, the operation will not be allowed::
+
+ In [11]: numpy.ones(3, 'm8[Y]') + numpy.ones(3, 'm8[D]')
+ raise numpy.IncompatibleUnitError # how to convert relative years to days?
+
+In order to being able to perform the above operation a new NumPy
+function, called ``change_timeunit`` is proposed. Its signature will
+be::
+
+ change_timeunit(time_object, new_unit, reference)
+
+where 'time_object' is the time object whose unit is to be changed,
+'new_unit' is the desired new time unit, and 'reference' is an absolute
+date (NumPy datetime64 scalar) that will be used to allow the conversion
+of relative times in case of using time units with an uncertain number
+of smaller time units (relative years or months cannot be expressed in
+days).
+
+With this, the above operation can be done as follows::
+
+ In [10]: t_years = numpy.ones(3, 'm8[Y]')
+
+ In [11]: t_days = numpy.change_timeunit(t_years, 'D', '2001-01-01')
+
+ In [12]: t_days + numpy.ones(3, 'm8[D]')
+ Out[12]: array([366, 366, 366], dtype="timedelta64[D]")
+
+
+dtype vs time units conversions
+===============================
+
+For changing the date/time dtype of an existing array, we propose to use
+the ``.astype()`` method. This will be mainly useful for changing time
+units.
+
+For example, for absolute dates::
+
+ In[10]: t1 = numpy.zeros(5, dtype="datetime64[s]")
+
+ In[11]: print t1
+ [1970-01-01T00:00:00 1970-01-01T00:00:00 1970-01-01T00:00:00
+ 1970-01-01T00:00:00 1970-01-01T00:00:00]
+
+ In[12]: print t1.astype('datetime64[D]')
+ [1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01]
+
+For relative times::
+
+ In[10]: t1 = numpy.ones(5, dtype="timedelta64[s]")
+
+ In[11]: print t1
+ [1 1 1 1 1]
+
+ In[12]: print t1.astype('timedelta64[ms]')
+ [1000 1000 1000 1000 1000]
+
+Changing directly from/to relative to/from absolute dtypes will not be
+supported::
+
+ In[13]: numpy.zeros(5, dtype="datetime64[s]").astype('timedelta64')
+ TypeError: data type cannot be converted to the desired type
+
+Business days have the peculiarity that they do not cover a continuous
+line of time (they have gaps at weekends). Thus, when converting from
+any ordinary time to business days, it can happen that the original time
+is not representable. In that case, the result of the conversion is
+*Not a Time* (*NaT*)::
+
+ In[10]: t1 = numpy.arange(5, dtype="datetime64[D]")
+
+ In[11]: print t1
+ [1970-01-01 1970-01-02 1970-01-03 1970-01-04 1970-01-05]
+
+ In[12]: t2 = t1.astype("datetime64[B]")
+
+ In[13]: print t2 # 1970 begins in a Thursday
+ [1970-01-01 1970-01-02 NaT NaT 1970-01-05]
+
+When converting back to ordinary days, NaT values are left untouched
+(this happens in all time unit conversions)::
+
+ In[14]: t3 = t2.astype("datetime64[D]")
+
+ In[13]: print t3
+ [1970-01-01 1970-01-02 NaT NaT 1970-01-05]
+
+Necessary changes to NumPy
+==========================
+
+In order to facilitate the addition of the date-time data-types a few changes
+to NumPy were made:
+
+Addition of metadata to dtypes
+------------------------------
+
+All data-types now have a metadata dictionary. It can be set using the
+metadata keyword during construction of the object.
+
+Date-time data-types will place the word "__frequency__" in the meta-data
+dictionary containing a 4-tuple with the following parameters.
+
+(basic unit string (str),
+ number of multiples (int),
+ number of sub-divisions (int),
+ number of events (int)).
+
+Simple time units like 'D' for days will thus be specified by ('D', 1, 1, 1) in
+the "__frequency__" key of the metadata. More complicated time units (like '[2W/5]//50') will be indicated by ('D', 2, 5, 50).
+
+The "__frequency__" key is reserved for metadata and cannot be set with a
+dtype constructor.
+
+
+Ufunc interface extension
+-------------------------
+
+ufuncs that have datetime and timedelta arguments can use the Python API
+during ufunc calls (to raise errors).
+
+There is a new ufunc C-API call to set the data for a particular
+function pointer (for a particular set of data-types) to be the list of arrays
+passed in to the ufunc.
+
+Array Intervace Extensions
+--------------------------
+
+The array interface is extended to both handle datetime and timedelta
+typestr (including extended notation).
+
+In addition, the typestr element of the __array_interface__ can be a tuple
+as long as the version string is 4. The tuple is
+('typestr', metadata dictionary).
+
+This extension to the typestr concept extends to the descr portion of
+the __array_interface__. Thus, the second element in the tuple of a
+list of tuples describing a data-format can itself be a tuple of
+('typestr', metadata dictionary).
+
+
+Final considerations
+====================
+
+Why the ``origin`` metadata disappeared
+---------------------------------------
+
+During the discussion of the date/time dtypes in the NumPy list, the
+idea of having an ``origin`` metadata that complemented the definition
+of the absolute ``datetime64`` was initially found to be useful.
+
+However, after thinking more about this, we found that the combination
+of an absolute ``datetime64`` with a relative ``timedelta64`` does offer
+the same functionality while removing the need for the additional
+``origin`` metadata. This is why we have removed it from this proposal.
+
+Operations with mixed time units
+--------------------------------
+
+Whenever an operation between two time values of the same dtype with the
+same unit is accepted, the same operation with time values of different
+units should be possible (e.g. adding a time delta in seconds and one in
+microseconds), resulting in an adequate time unit. The exact semantics
+of this kind of operations is defined int the "Casting rules"
+subsections of the "Operating with date/time arrays" section.
+
+Due to the peculiarities of business days, it is most probable that
+operations mixing business days with other time units will not be
+allowed.
+
+
+.. [1] http://docs.python.org/lib/module-datetime.html
+.. [2] http://www.egenix.com/products/python/mxBase/mxDateTime
+.. [3] http://en.wikipedia.org/wiki/Unix_time
+
+
+.. Local Variables:
+.. mode: rst
+.. coding: utf-8
+.. fill-column: 72
+.. End:
+
diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py
index 7d5c3a49e..b89c94d9b 100644
--- a/numpy/core/_internal.py
+++ b/numpy/core/_internal.py
@@ -3,6 +3,7 @@
import re
import sys
+from _mx_datetime_parser import *
if (sys.byteorder == 'little'):
_nbo = '<'
@@ -77,11 +78,18 @@ def _usefields(adict, align):
# a simple typestring
def _array_descr(descriptor):
+ from multiarray import METADATA_DTSTR
fields = descriptor.fields
if fields is None:
subdtype = descriptor.subdtype
if subdtype is None:
- return descriptor.str
+ if descriptor.metadata is None:
+ return descriptor.str
+ else:
+ new = descriptor.metadata.copy()
+ # Eliminate any key related to internal implementation
+ _ = new.pop(METADATA_DTSTR, None)
+ return (descriptor.str, new)
else:
return (_array_descr(subdtype[0]), subdtype[1])
@@ -156,6 +164,34 @@ def _split(input):
return newlist
+format_datetime = re.compile(r"""(?P<typecode>M8|m8|datetime64|timedelta64)
+ ([[]
+ ((?P<num>\d+)?
+ (?P<baseunit>Y|M|W|B|D|h|m|s|ms|us|ns|ps|fs|as)
+ (/(?P<den>\d+))?
+ []])
+ (//(?P<events>\d+))?)?""", re.X)
+# Return (baseunit, num, den, events), datetime
+# from date-time string
+def _datetimestring(astr):
+ res = format_datetime.match(astr)
+ if res is None:
+ raise ValueError, "Incorrect date-time string."
+ typecode = res.group('typecode')
+ datetime = (typecode == 'M8' or typecode == 'datetime64')
+ defaults = ['us', 1, 1, 1]
+ names = ['baseunit', 'num', 'den', 'events']
+ func = [str, int, int, int]
+ dt_tuple = []
+ for i, name in enumerate(names):
+ value = res.group(name)
+ if value:
+ dt_tuple.append(func[i](value))
+ else:
+ dt_tuple.append(defaults[i])
+
+ return tuple(dt_tuple), datetime
+
format_re = re.compile(r'(?P<order1>[<>|=]?)(?P<repeats> *[(]?[ ,0-9]*[)]? *)(?P<order2>[<>|=]?)(?P<dtype>[A-Za-z0-9.]*)')
# astr is a string (perhaps comma separated)
diff --git a/numpy/core/_mx_datetime_parser.py b/numpy/core/_mx_datetime_parser.py
new file mode 100644
index 000000000..f1b330f00
--- /dev/null
+++ b/numpy/core/_mx_datetime_parser.py
@@ -0,0 +1,962 @@
+#-*- coding: latin-1 -*-
+"""
+Date/Time string parsing module.
+
+This code is a slightly modified version of Parser.py found in mx.DateTime
+version 3.0.0
+
+As such, it is subject to the terms of the eGenix public license version 1.1.0.
+
+FIXME: Add license.txt to NumPy
+"""
+
+__all__ = ['date_from_string', 'datetime_from_string']
+
+import types
+import re
+import datetime as dt
+
+class RangeError(Exception): pass
+
+# Enable to produce debugging output
+_debug = 0
+
+# REs for matching date and time parts in a string; These REs
+# parse a superset of ARPA, ISO, American and European style dates.
+# Timezones are supported via the Timezone submodule.
+
+_year = '(?P<year>-?\d+\d(?!:))'
+_fullyear = '(?P<year>-?\d+\d\d(?!:))'
+_year_epoch = '(?:' + _year + '(?P<epoch> *[ABCDE\.]+)?)'
+_fullyear_epoch = '(?:' + _fullyear + '(?P<epoch> *[ABCDE\.]+)?)'
+_relyear = '(?:\((?P<relyear>[-+]?\d+)\))'
+
+_month = '(?P<month>\d?\d(?!:))'
+_fullmonth = '(?P<month>\d\d(?!:))'
+_litmonth = ('(?P<litmonth>'
+ 'jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|'
+ 'mär|mae|mrz|mai|okt|dez|'
+ 'fev|avr|juin|juil|aou|aoû|déc|'
+ 'ene|abr|ago|dic|'
+ 'out'
+ ')[a-z,\.;]*')
+litmonthtable = {
+ # English
+ 'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6,
+ 'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12,
+ # German
+ 'mär':3, 'mae':3, 'mrz':3, 'mai':5, 'okt':10, 'dez':12,
+ # French
+ 'fev':2, 'avr':4, 'juin':6, 'juil':7, 'aou':8, 'aoû':8,
+ 'déc':12,
+ # Spanish
+ 'ene':1, 'abr':4, 'ago':8, 'dic':12,
+ # Portuguese
+ 'out':10,
+ }
+_relmonth = '(?:\((?P<relmonth>[-+]?\d+)\))'
+
+_day = '(?P<day>\d?\d(?!:))'
+_usday = '(?P<day>\d?\d(?!:))(?:st|nd|rd|th|[,\.;])?'
+_fullday = '(?P<day>\d\d(?!:))'
+_litday = ('(?P<litday>'
+ 'mon|tue|wed|thu|fri|sat|sun|'
+ 'die|mit|don|fre|sam|son|'
+ 'lun|mar|mer|jeu|ven|sam|dim|'
+ 'mie|jue|vie|sab|dom|'
+ 'pri|seg|ter|cua|qui'
+ ')[a-z]*')
+litdaytable = {
+ # English
+ 'mon':0, 'tue':1, 'wed':2, 'thu':3, 'fri':4, 'sat':5, 'sun':6,
+ # German
+ 'die':1, 'mit':2, 'don':3, 'fre':4, 'sam':5, 'son':6,
+ # French
+ 'lun':0, 'mar':1, 'mer':2, 'jeu':3, 'ven':4, 'sam':5, 'dim':6,
+ # Spanish
+ 'mie':2, 'jue':3, 'vie':4, 'sab':5, 'dom':6,
+ # Portuguese
+ 'pri':0, 'seg':1, 'ter':2, 'cua':3, 'qui':4,
+ }
+_relday = '(?:\((?P<relday>[-+]?\d+)\))'
+
+_hour = '(?P<hour>[012]?\d)'
+_minute = '(?P<minute>[0-6]\d)'
+_second = '(?P<second>[0-6]\d(?:[.,]\d+)?)'
+
+_days = '(?P<days>\d*\d(?:[.,]\d+)?)'
+_hours = '(?P<hours>\d*\d(?:[.,]\d+)?)'
+_minutes = '(?P<minutes>\d*\d(?:[.,]\d+)?)'
+_seconds = '(?P<seconds>\d*\d(?:[.,]\d+)?)'
+
+_reldays = '(?:\((?P<reldays>[-+]?\d+(?:[.,]\d+)?)\))'
+_relhours = '(?:\((?P<relhours>[-+]?\d+(?:[.,]\d+)?)\))'
+_relminutes = '(?:\((?P<relminutes>[-+]?\d+(?:[.,]\d+)?)\))'
+_relseconds = '(?:\((?P<relseconds>[-+]?\d+(?:[.,]\d+)?)\))'
+
+_sign = '(?:(?P<sign>[-+]) *)'
+_week = 'W(?P<week>\d?\d)'
+_zone = '(?P<zone>[A-Z]+|[+-]\d\d?:?(?:\d\d)?)'
+_ampm = '(?P<ampm>[ap][m.]+)'
+
+_time = (_hour + ':' + _minute + '(?::' + _second + '|[^:]|$) *'
+ + _ampm + '? *' + _zone + '?')
+_isotime = _hour + ':?' + _minute + ':?' + _second + '? *' + _zone + '?'
+
+_yeardate = _year
+_weekdate = _year + '-?(?:' + _week + '-?' + _day + '?)?'
+_eurodate = _day + '\.' + _month + '\.' + _year_epoch + '?'
+_usdate = _month + '/' + _day + '(?:/' + _year_epoch + '|[^/]|$)'
+_altusdate = _month + '-' + _day + '-' + _fullyear_epoch
+_isodate = _year + '-' + _month + '-?' + _day + '?(?!:)'
+_altisodate = _year + _fullmonth + _fullday + '(?!:)'
+_usisodate = _fullyear + '/' + _fullmonth + '/' + _fullday
+_litdate = ('(?:'+ _litday + ',? )? *' +
+ _usday + ' *' +
+ '[- ] *(?:' + _litmonth + '|'+ _month +') *[- ] *' +
+ _year_epoch + '?')
+_altlitdate = ('(?:'+ _litday + ',? )? *' +
+ _litmonth + '[ ,.a-z]+' +
+ _usday +
+ '(?:[ a-z]+' + _year_epoch + ')?')
+_eurlitdate = ('(?:'+ _litday + ',?[ a-z]+)? *' +
+ '(?:'+ _usday + '[ a-z]+)? *' +
+ _litmonth +
+ '(?:[ ,.a-z]+' + _year_epoch + ')?')
+
+_relany = '[*%?a-zA-Z]+'
+
+_relisodate = ('(?:(?:' + _relany + '|' + _year + '|' + _relyear + ')-' +
+ '(?:' + _relany + '|' + _month + '|' + _relmonth + ')-' +
+ '(?:' + _relany + '|' + _day + '|' + _relday + '))')
+
+_asctime = ('(?:'+ _litday + ',? )? *' +
+ _usday + ' *' +
+ '[- ] *(?:' + _litmonth + '|'+ _month +') *[- ]' +
+ '(?:[0-9: ]+)' +
+ _year_epoch + '?')
+
+_relisotime = ('(?:(?:' + _relany + '|' + _hour + '|' + _relhours + '):' +
+ '(?:' + _relany + '|' + _minute + '|' + _relminutes + ')' +
+ '(?::(?:' + _relany + '|' + _second + '|' + _relseconds + '))?)')
+
+_isodelta1 = (_sign + '?' +
+ _days + ':' + _hours + ':' + _minutes + ':' + _seconds)
+_isodelta2 = (_sign + '?' +
+ _hours + ':' + _minutes + ':' + _seconds)
+_isodelta3 = (_sign + '?' +
+ _hours + ':' + _minutes)
+_litdelta = (_sign + '?' +
+ '(?:' + _days + ' *d[a-z]*[,; ]*)?' +
+ '(?:' + _hours + ' *h[a-z]*[,; ]*)?' +
+ '(?:' + _minutes + ' *m[a-z]*[,; ]*)?' +
+ '(?:' + _seconds + ' *s[a-z]*[,; ]*)?')
+_litdelta2 = (_sign + '?' +
+ '(?:' + _days + ' *d[a-z]*[,; ]*)?' +
+ _hours + ':' + _minutes + '(?::' + _seconds + ')?')
+
+_timeRE = re.compile(_time, re.I)
+_isotimeRE = re.compile(_isotime, re.I)
+_isodateRE = re.compile(_isodate, re.I)
+_altisodateRE = re.compile(_altisodate, re.I)
+_usisodateRE = re.compile(_usisodate, re.I)
+_yeardateRE = re.compile(_yeardate, re.I)
+_eurodateRE = re.compile(_eurodate, re.I)
+_usdateRE = re.compile(_usdate, re.I)
+_altusdateRE = re.compile(_altusdate, re.I)
+_litdateRE = re.compile(_litdate, re.I)
+_altlitdateRE = re.compile(_altlitdate, re.I)
+_eurlitdateRE = re.compile(_eurlitdate, re.I)
+_relisodateRE = re.compile(_relisodate, re.I)
+_asctimeRE = re.compile(_asctime, re.I)
+_isodelta1RE = re.compile(_isodelta1)
+_isodelta2RE = re.compile(_isodelta2)
+_isodelta3RE = re.compile(_isodelta3)
+_litdeltaRE = re.compile(_litdelta)
+_litdelta2RE = re.compile(_litdelta2)
+_relisotimeRE = re.compile(_relisotime, re.I)
+
+# Available date parsers
+_date_formats = ('euro',
+ 'usiso', 'us', 'altus',
+ 'iso', 'altiso',
+ 'lit', 'altlit', 'eurlit',
+ 'year', 'unknown')
+
+# Available time parsers
+_time_formats = ('standard',
+ 'iso',
+ 'unknown')
+
+_zoneoffset = ('(?:'
+ '(?P<zonesign>[+-])?'
+ '(?P<hours>\d\d?)'
+ ':?'
+ '(?P<minutes>\d\d)?'
+ '(?P<extra>\d+)?'
+ ')'
+ )
+
+_zoneoffsetRE = re.compile(_zoneoffset)
+
+_zonetable = {
+ # Timezone abbreviations
+ # Std Summer
+
+ # Standards
+ 'UT':0,
+ 'UTC':0,
+ 'GMT':0,
+
+ # A few common timezone abbreviations
+ 'CET':1, 'CEST':2, 'CETDST':2, # Central European
+ 'MET':1, 'MEST':2, 'METDST':2, # Mean European
+ 'MEZ':1, 'MESZ':2, # Mitteleuropäische Zeit
+ 'EET':2, 'EEST':3, 'EETDST':3, # Eastern Europe
+ 'WET':0, 'WEST':1, 'WETDST':1, # Western Europe
+ 'MSK':3, 'MSD':4, # Moscow
+ 'IST':5.5, # India
+ 'JST':9, # Japan
+ 'KST':9, # Korea
+ 'HKT':8, # Hong Kong
+
+ # US time zones
+ 'AST':-4, 'ADT':-3, # Atlantic
+ 'EST':-5, 'EDT':-4, # Eastern
+ 'CST':-6, 'CDT':-5, # Central
+ 'MST':-7, 'MDT':-6, # Midwestern
+ 'PST':-8, 'PDT':-7, # Pacific
+
+ # Australian time zones
+ 'CAST':9.5, 'CADT':10.5, # Central
+ 'EAST':10, 'EADT':11, # Eastern
+ 'WAST':8, 'WADT':9, # Western
+ 'SAST':9.5, 'SADT':10.5, # Southern
+
+ # US military time zones
+ 'Z': 0,
+ 'A': 1,
+ 'B': 2,
+ 'C': 3,
+ 'D': 4,
+ 'E': 5,
+ 'F': 6,
+ 'G': 7,
+ 'H': 8,
+ 'I': 9,
+ 'K': 10,
+ 'L': 11,
+ 'M': 12,
+ 'N':-1,
+ 'O':-2,
+ 'P':-3,
+ 'Q':-4,
+ 'R':-5,
+ 'S':-6,
+ 'T':-7,
+ 'U':-8,
+ 'V':-9,
+ 'W':-10,
+ 'X':-11,
+ 'Y':-12
+ }
+
+
+def utc_offset(zone):
+ """ utc_offset(zonestring)
+
+ Return the UTC time zone offset in minutes.
+
+ zone must be string and can either be given as +-HH:MM,
+ +-HHMM, +-HH numeric offset or as time zone
+ abbreviation. Daylight saving time must be encoded into the
+ zone offset.
+
+ Timezone abbreviations are treated case-insensitive.
+
+ """
+ if not zone:
+ return 0
+ uzone = zone.upper()
+ if uzone in _zonetable:
+ return _zonetable[uzone]*60
+ offset = _zoneoffsetRE.match(zone)
+ if not offset:
+ raise ValueError,'wrong format or unkown time zone: "%s"' % zone
+ zonesign,hours,minutes,extra = offset.groups()
+ if extra:
+ raise ValueError,'illegal time zone offset: "%s"' % zone
+ offset = int(hours or 0) * 60 + int(minutes or 0)
+ if zonesign == '-':
+ offset = -offset
+ return offset
+
+def add_century(year):
+
+ """ Sliding window approach to the Y2K problem: adds a suitable
+ century to the given year and returns it as integer.
+
+ The window used depends on the current year. If adding the current
+ century to the given year gives a year within the range
+ current_year-70...current_year+30 [both inclusive], then the
+ current century is added. Otherwise the century (current + 1 or
+ - 1) producing the least difference is chosen.
+
+ """
+
+ current_year=dt.datetime.now().year
+ current_century=(dt.datetime.now().year / 100) * 100
+
+ if year > 99:
+ # Take it as-is
+ return year
+ year = year + current_century
+ diff = year - current_year
+ if diff >= -70 and diff <= 30:
+ return year
+ elif diff < -70:
+ return year + 100
+ else:
+ return year - 100
+
+
+def _parse_date(text):
+ """
+ Parses the date part given in text and returns a tuple
+ (text,day,month,year,style) with the following meanings:
+
+ * text gives the original text without the date part
+
+ * day,month,year give the parsed date
+
+ * style gives information about which parser was successful:
+ 'euro' - the European date parser
+ 'us' - the US date parser
+ 'altus' - the alternative US date parser (with '-' instead of '/')
+ 'iso' - the ISO date parser
+ 'altiso' - the alternative ISO date parser (without '-')
+ 'usiso' - US style ISO date parser (yyyy/mm/dd)
+ 'lit' - the US literal date parser
+ 'altlit' - the alternative US literal date parser
+ 'eurlit' - the Eurpean literal date parser
+ 'unknown' - no date part was found, defaultdate was used
+
+ Formats may be set to a tuple of style strings specifying which of the above
+ parsers to use and in which order to try them.
+ Default is to try all of them in the above order.
+
+ ``defaultdate`` provides the defaults to use in case no date part is found.
+ Most other parsers default to the current year January 1 if some of these
+ date parts are missing.
+
+ If ``'unknown'`` is not given in formats and the date cannot be parsed,
+ a :exc:`ValueError` is raised.
+
+ """
+ match = None
+ style = ''
+
+ formats = _date_formats
+
+ us_formats=('us', 'altus')
+ iso_formats=('iso', 'altiso', 'usiso')
+
+ now=dt.datetime.now
+
+ # Apply parsers in the order given in formats
+ for format in formats:
+
+ if format == 'euro':
+ # European style date
+ match = _eurodateRE.search(text)
+ if match is not None:
+ day,month,year,epoch = match.groups()
+ if year:
+ if len(year) == 2:
+ # Y2K problem:
+ year = add_century(int(year))
+ else:
+ year = int(year)
+ else:
+ defaultdate = now()
+ year = defaultdate.year
+ if epoch and 'B' in epoch:
+ year = -year + 1
+ month = int(month)
+ day = int(day)
+ # Could have mistaken euro format for us style date
+ # which uses month, day order
+ if month > 12 or month == 0:
+ match = None
+ continue
+ break
+
+ elif format == 'year':
+ # just a year specified
+ match = _yeardateRE.match(text)
+ if match is not None:
+ year = match.groups()[0]
+ if year:
+ if len(year) == 2:
+ # Y2K problem:
+ year = add_century(int(year))
+ else:
+ year = int(year)
+ else:
+ defaultdate = now()
+ year = defaultdate.year
+ day = 1
+ month = 1
+ break
+
+ elif format in iso_formats:
+ # ISO style date
+ if format == 'iso':
+ match = _isodateRE.search(text)
+ elif format == 'altiso':
+ match = _altisodateRE.search(text)
+ # Avoid mistaking ISO time parts ('Thhmmss') for dates
+ if match is not None:
+ left, right = match.span()
+ if left > 0 and \
+ text[left - 1:left] == 'T':
+ match = None
+ continue
+ else:
+ match = _usisodateRE.search(text)
+ if match is not None:
+ year,month,day = match.groups()
+ if len(year) == 2:
+ # Y2K problem:
+ year = add_century(int(year))
+ else:
+ year = int(year)
+ # Default to January 1st
+ if not month:
+ month = 1
+ else:
+ month = int(month)
+ if not day:
+ day = 1
+ else:
+ day = int(day)
+ break
+
+ elif format in us_formats:
+ # US style date
+ if format == 'us':
+ match = _usdateRE.search(text)
+ else:
+ match = _altusdateRE.search(text)
+ if match is not None:
+ month,day,year,epoch = match.groups()
+ if year:
+ if len(year) == 2:
+ # Y2K problem:
+ year = add_century(int(year))
+ else:
+ year = int(year)
+ else:
+ defaultdate = now()
+ year = defaultdate.year
+ if epoch and 'B' in epoch:
+ year = -year + 1
+ # Default to 1 if no day is given
+ if day:
+ day = int(day)
+ else:
+ day = 1
+ month = int(month)
+ # Could have mistaken us format for euro style date
+ # which uses day, month order
+ if month > 12 or month == 0:
+ match = None
+ continue
+ break
+
+ elif format == 'lit':
+ # US style literal date
+ match = _litdateRE.search(text)
+ if match is not None:
+ litday,day,litmonth,month,year,epoch = match.groups()
+ break
+
+ elif format == 'altlit':
+ # Alternative US style literal date
+ match = _altlitdateRE.search(text)
+ if match is not None:
+ litday,litmonth,day,year,epoch = match.groups()
+ month = '<missing>'
+ break
+
+ elif format == 'eurlit':
+ # European style literal date
+ match = _eurlitdateRE.search(text)
+ if match is not None:
+ litday,day,litmonth,year,epoch = match.groups()
+ month = '<missing>'
+ break
+
+ elif format == 'unknown':
+ # No date part: use defaultdate
+ defaultdate = now()
+ year = defaultdate.year
+ month = defaultdate.month
+ day = defaultdate.day
+ style = format
+ break
+
+ # Check success
+ if match is not None:
+ # Remove date from text
+ left, right = match.span()
+ if 0 and _debug:
+ print 'parsed date:',repr(text[left:right]),\
+ 'giving:',year,month,day
+ text = text[:left] + text[right:]
+ style = format
+
+ elif not style:
+ # Not recognized: raise an error
+ raise ValueError, 'unknown date format: "%s"' % text
+
+ # Literal date post-processing
+ if style in ('lit', 'altlit', 'eurlit'):
+ if 0 and _debug: print match.groups()
+ # Default to current year, January 1st
+ if not year:
+ defaultdate = now()
+ year = defaultdate.year
+ else:
+ if len(year) == 2:
+ # Y2K problem:
+ year = add_century(int(year))
+ else:
+ year = int(year)
+ if epoch and 'B' in epoch:
+ year = -year + 1
+ if litmonth:
+ litmonth = litmonth.lower()
+ try:
+ month = litmonthtable[litmonth]
+ except KeyError:
+ raise ValueError,\
+ 'wrong month name: "%s"' % litmonth
+ elif month:
+ month = int(month)
+ else:
+ month = 1
+ if day:
+ day = int(day)
+ else:
+ day = 1
+
+ #print '_parse_date:',text,day,month,year,style
+ return text,day,month,year,style
+
+def _parse_time(text):
+
+ """ Parses a time part given in text and returns a tuple
+ (text,hour,minute,second,offset,style) with the following
+ meanings:
+
+ * text gives the original text without the time part
+ * hour,minute,second give the parsed time
+ * offset gives the time zone UTC offset
+ * style gives information about which parser was successful:
+ 'standard' - the standard parser
+ 'iso' - the ISO time format parser
+ 'unknown' - no time part was found
+
+ formats may be set to a tuple specifying the parsers to use:
+ 'standard' - standard time format with ':' delimiter
+ 'iso' - ISO time format (superset of 'standard')
+ 'unknown' - default to 0:00:00, 0 zone offset
+
+ If 'unknown' is not given in formats and the time cannot be
+ parsed, a ValueError is raised.
+
+ """
+ match = None
+ style = ''
+
+ formats=_time_formats
+
+ # Apply parsers in the order given in formats
+ for format in formats:
+
+ # Standard format
+ if format == 'standard':
+ match = _timeRE.search(text)
+ if match is not None:
+ hour,minute,second,ampm,zone = match.groups()
+ style = 'standard'
+ break
+
+ # ISO format
+ if format == 'iso':
+ match = _isotimeRE.search(text)
+ if match is not None:
+ hour,minute,second,zone = match.groups()
+ ampm = None
+ style = 'iso'
+ break
+
+ # Default handling
+ elif format == 'unknown':
+ hour,minute,second,offset = 0,0,0.0,0
+ style = 'unknown'
+ break
+
+ if not style:
+ # If no default handling should be applied, raise an error
+ raise ValueError, 'unknown time format: "%s"' % text
+
+ # Post-processing
+ if match is not None:
+
+ if zone:
+ # Convert to UTC offset
+ offset = utc_offset(zone)
+ else:
+ offset = 0
+
+ hour = int(hour)
+ if ampm:
+ if ampm[0] in ('p', 'P'):
+ # 12pm = midday
+ if hour < 12:
+ hour = hour + 12
+ else:
+ # 12am = midnight
+ if hour >= 12:
+ hour = hour - 12
+ if minute:
+ minute = int(minute)
+ else:
+ minute = 0
+ if not second:
+ second = 0.0
+ else:
+ if ',' in second:
+ second = second.replace(',', '.')
+ second = float(second)
+
+ # Remove time from text
+ left,right = match.span()
+ if 0 and _debug:
+ print 'parsed time:',repr(text[left:right]),\
+ 'giving:',hour,minute,second,offset
+ text = text[:left] + text[right:]
+
+ #print '_parse_time:',text,hour,minute,second,offset,style
+ return text,hour,minute,second,offset,style
+
+###
+
+def datetime_from_string(text):
+
+ """ datetime_from_string(text, [formats, defaultdate])
+
+ Returns a datetime instance reflecting the date and time given
+ in text. In case a timezone is given, the returned instance
+ will point to the corresponding UTC time value. Otherwise, the
+ value is set as given in the string.
+
+ formats may be set to a tuple of strings specifying which of
+ the following parsers to use and in which order to try
+ them. Default is to try all of them in the order given below:
+
+ 'euro' - the European date parser
+ 'us' - the US date parser
+ 'altus' - the alternative US date parser (with '-' instead of '/')
+ 'iso' - the ISO date parser
+ 'altiso' - the alternative ISO date parser (without '-')
+ 'usiso' - US style ISO date parser (yyyy/mm/dd)
+ 'lit' - the US literal date parser
+ 'altlit' - the alternative US literal date parser
+ 'eurlit' - the Eurpean literal date parser
+ 'unknown' - if no date part is found, use defaultdate
+
+ defaultdate provides the defaults to use in case no date part
+ is found. Most of the parsers default to the current year
+ January 1 if some of these date parts are missing.
+
+ If 'unknown' is not given in formats and the date cannot
+ be parsed, a ValueError is raised.
+
+ time_formats may be set to a tuple of strings specifying which
+ of the following parsers to use and in which order to try
+ them. Default is to try all of them in the order given below:
+
+ 'standard' - standard time format HH:MM:SS (with ':' delimiter)
+ 'iso' - ISO time format (superset of 'standard')
+ 'unknown' - default to 00:00:00 in case the time format
+ cannot be parsed
+
+ Defaults to 00:00:00.00 for time parts that are not included
+ in the textual representation.
+
+ If 'unknown' is not given in time_formats and the time cannot
+ be parsed, a ValueError is raised.
+
+ """
+ origtext = text
+
+ text,hour,minute,second,offset,timestyle = _parse_time(origtext)
+ text,day,month,year,datestyle = _parse_date(text)
+
+ if 0 and _debug:
+ print 'tried time/date on %s, date=%s, time=%s' % (origtext,
+ datestyle,
+ timestyle)
+
+ # If this fails, try the ISO order (date, then time)
+ if timestyle in ('iso', 'unknown'):
+ text,day,month,year,datestyle = _parse_date(origtext)
+ text,hour,minute,second,offset,timestyle = _parse_time(text)
+ if 0 and _debug:
+ print 'tried ISO on %s, date=%s, time=%s' % (origtext,
+ datestyle,
+ timestyle)
+
+ try:
+ microsecond = int(round(1000000 * (second % 1)))
+ second = int(second)
+ return dt.datetime(year,month,day,hour,minute,second, microsecond) - \
+ dt.timedelta(minutes=offset)
+ except ValueError, why:
+ raise RangeError,\
+ 'Failed to parse "%s": %s' % (origtext, why)
+
+def date_from_string(text):
+
+ """ date_from_string(text, [formats, defaultdate])
+
+ Returns a datetime instance reflecting the date given in
+ text. A possibly included time part is ignored.
+
+ formats and defaultdate work just like for
+ datetime_from_string().
+
+ """
+ _text,day,month,year,datestyle = _parse_date(text)
+
+ try:
+ return dt.datetime(year,month,day)
+ except ValueError, why:
+ raise RangeError,\
+ 'Failed to parse "%s": %s' % (text, why)
+
+def validateDateTimeString(text):
+
+ """ validateDateTimeString(text, [formats, defaultdate])
+
+ Validates the given text and returns 1/0 depending on whether
+ text includes parseable date and time values or not.
+
+ formats works just like for datetime_from_string() and defines
+ the order of date/time parsers to apply. It defaults to the
+ same list of parsers as for datetime_from_string().
+
+ XXX Undocumented !
+
+ """
+ try:
+ datetime_from_string(text)
+ except ValueError, why:
+ return 0
+ return 1
+
+
+def validateDateString(text):
+
+ """ validateDateString(text, [formats, defaultdate])
+
+ Validates the given text and returns 1/0 depending on whether
+ text includes a parseable date value or not.
+
+ formats works just like for datetime_from_string() and defines
+ the order of date/time parsers to apply. It defaults to the
+ same list of parsers as for datetime_from_string().
+
+ XXX Undocumented !
+
+ """
+ try:
+ date_from_string(text)
+ except ValueError, why:
+ return 0
+ return 1
+
+### Tests
+
+def _test():
+
+ import sys
+
+ t = dt.datetime.now()
+ _date = t.strftime('%Y-%m-%d')
+
+ print 'Testing DateTime Parser...'
+
+ l = [
+
+ # Literal formats
+ ('Sun Nov 6 08:49:37 1994', '1994-11-06 08:49:37.00'),
+ ('sun nov 6 08:49:37 1994', '1994-11-06 08:49:37.00'),
+ ('sUN NOV 6 08:49:37 1994', '1994-11-06 08:49:37.00'),
+ ('Sunday, 06-Nov-94 08:49:37 GMT', '1994-11-06 08:49:37.00'),
+ ('Sun, 06 Nov 1994 08:49:37 GMT', '1994-11-06 08:49:37.00'),
+ ('06-Nov-94 08:49:37', '1994-11-06 08:49:37.00'),
+ ('06-Nov-94', '1994-11-06 00:00:00.00'),
+ ('06-NOV-94', '1994-11-06 00:00:00.00'),
+ ('November 19 08:49:37', '%s-11-19 08:49:37.00' % t.year),
+ ('Nov. 9', '%s-11-09 00:00:00.00' % t.year),
+ ('Sonntag, der 6. November 1994, 08:49:37 GMT', '1994-11-06 08:49:37.00'),
+ ('6. November 2001, 08:49:37', '2001-11-06 08:49:37.00'),
+ ('sep 6', '%s-09-06 00:00:00.00' % t.year),
+ ('sep 6 2000', '2000-09-06 00:00:00.00'),
+ ('September 29', '%s-09-29 00:00:00.00' % t.year),
+ ('Sep. 29', '%s-09-29 00:00:00.00' % t.year),
+ ('6 sep', '%s-09-06 00:00:00.00' % t.year),
+ ('29 September', '%s-09-29 00:00:00.00' % t.year),
+ ('29 Sep.', '%s-09-29 00:00:00.00' % t.year),
+ ('sep 6 2001', '2001-09-06 00:00:00.00'),
+ ('Sep 6, 2001', '2001-09-06 00:00:00.00'),
+ ('September 6, 2001', '2001-09-06 00:00:00.00'),
+ ('sep 6 01', '2001-09-06 00:00:00.00'),
+ ('Sep 6, 01', '2001-09-06 00:00:00.00'),
+ ('September 6, 01', '2001-09-06 00:00:00.00'),
+ ('30 Apr 2006 20:19:00', '2006-04-30 20:19:00.00'),
+
+ # ISO formats
+ ('1994-11-06 08:49:37', '1994-11-06 08:49:37.00'),
+ ('010203', '2001-02-03 00:00:00.00'),
+ ('2001-02-03 00:00:00.00', '2001-02-03 00:00:00.00'),
+ ('2001-02 00:00:00.00', '2001-02-01 00:00:00.00'),
+ ('2001-02-03', '2001-02-03 00:00:00.00'),
+ ('2001-02', '2001-02-01 00:00:00.00'),
+ ('20000824/2300', '2000-08-24 23:00:00.00'),
+ ('20000824/0102', '2000-08-24 01:02:00.00'),
+ ('20000824', '2000-08-24 00:00:00.00'),
+ ('20000824/020301', '2000-08-24 02:03:01.00'),
+ ('20000824 020301', '2000-08-24 02:03:01.00'),
+ ('20000824T020301', '2000-08-24 02:03:01.00'),
+ ('20000824 020301', '2000-08-24 02:03:01.00'),
+ ('2000-08-24 02:03:01.00', '2000-08-24 02:03:01.00'),
+ ('T020311', '%s 02:03:11.00' % _date),
+ ('2003-12-9', '2003-12-09 00:00:00.00'),
+ ('03-12-9', '2003-12-09 00:00:00.00'),
+ ('003-12-9', '0003-12-09 00:00:00.00'),
+ ('0003-12-9', '0003-12-09 00:00:00.00'),
+ ('2003-1-9', '2003-01-09 00:00:00.00'),
+ ('03-1-9', '2003-01-09 00:00:00.00'),
+ ('003-1-9', '0003-01-09 00:00:00.00'),
+ ('0003-1-9', '0003-01-09 00:00:00.00'),
+
+ # US formats
+ ('06/11/94 08:49:37', '1994-06-11 08:49:37.00'),
+ ('11/06/94 08:49:37', '1994-11-06 08:49:37.00'),
+ ('9/23/2001', '2001-09-23 00:00:00.00'),
+ ('9-23-2001', '2001-09-23 00:00:00.00'),
+ ('9/6', '%s-09-06 00:00:00.00' % t.year),
+ ('09/6', '%s-09-06 00:00:00.00' % t.year),
+ ('9/06', '%s-09-06 00:00:00.00' % t.year),
+ ('09/06', '%s-09-06 00:00:00.00' % t.year),
+ ('9/6/2001', '2001-09-06 00:00:00.00'),
+ ('09/6/2001', '2001-09-06 00:00:00.00'),
+ ('9/06/2001', '2001-09-06 00:00:00.00'),
+ ('09/06/2001', '2001-09-06 00:00:00.00'),
+ ('9-6-2001', '2001-09-06 00:00:00.00'),
+ ('09-6-2001', '2001-09-06 00:00:00.00'),
+ ('9-06-2001', '2001-09-06 00:00:00.00'),
+ ('09-06-2001', '2001-09-06 00:00:00.00'),
+ ('2002/05/28 13:10:56.114700 GMT+2', '2002-05-28 13:10:56.114700'),
+ ('1970/01/01', '1970-01-01 00:00:00.00'),
+ ('20021025 12:00 PM', '2002-10-25 12:00:00.00'),
+ ('20021025 12:30 PM', '2002-10-25 12:30:00.00'),
+ ('20021025 12:00 AM', '2002-10-25 00:00:00.00'),
+ ('20021025 12:30 AM', '2002-10-25 00:30:00.00'),
+ ('20021025 1:00 PM', '2002-10-25 13:00:00.00'),
+ ('20021025 2:00 AM', '2002-10-25 02:00:00.00'),
+ ('Thursday, February 06, 2003 12:40 PM', '2003-02-06 12:40:00.00'),
+ ('Mon, 18 Sep 2006 23:03:00', '2006-09-18 23:03:00.00'),
+
+ # European formats
+ ('6.11.2001, 08:49:37', '2001-11-06 08:49:37.00'),
+ ('06.11.2001, 08:49:37', '2001-11-06 08:49:37.00'),
+ ('06.11. 08:49:37', '%s-11-06 08:49:37.00' % t.year),
+ #('21/12/2002', '2002-12-21 00:00:00.00'),
+ #('21/08/2002', '2002-08-21 00:00:00.00'),
+ #('21-08-2002', '2002-08-21 00:00:00.00'),
+ #('13/01/03', '2003-01-13 00:00:00.00'),
+ #('13/1/03', '2003-01-13 00:00:00.00'),
+ #('13/1/3', '2003-01-13 00:00:00.00'),
+ #('13/01/3', '2003-01-13 00:00:00.00'),
+
+ # Time only formats
+ ('01:03', '%s 01:03:00.00' % _date),
+ ('01:03:11', '%s 01:03:11.00' % _date),
+ ('01:03:11.50', '%s 01:03:11.500000' % _date),
+ ('01:03:11.50 AM', '%s 01:03:11.500000' % _date),
+ ('01:03:11.50 PM', '%s 13:03:11.500000' % _date),
+ ('01:03:11.50 a.m.', '%s 01:03:11.500000' % _date),
+ ('01:03:11.50 p.m.', '%s 13:03:11.500000' % _date),
+
+ # Invalid formats
+ ('6..2001, 08:49:37', '%s 08:49:37.00' % _date),
+ ('9//2001', 'ignore'),
+ ('06--94 08:49:37', 'ignore'),
+ ('20-03 00:00:00.00', 'ignore'),
+ ('9/2001', 'ignore'),
+ ('9-6', 'ignore'),
+ ('09-6', 'ignore'),
+ ('9-06', 'ignore'),
+ ('09-06', 'ignore'),
+ ('20000824/23', 'ignore'),
+ ('November 1994 08:49:37', 'ignore'),
+ ]
+
+ # Add Unicode versions
+ try:
+ unicode
+ except NameError:
+ pass
+ else:
+ k = []
+ for text, result in l:
+ k.append((unicode(text), result))
+ l.extend(k)
+
+ for text, reference in l:
+ try:
+ value = datetime_from_string(text)
+ except:
+ if reference is None:
+ continue
+ else:
+ value = str(sys.exc_info()[1])
+ valid_datetime = validateDateTimeString(text)
+ valid_date = validateDateString(text)
+
+ if reference[-3:] == '.00': reference = reference[:-3]
+
+ if str(value) != reference and \
+ not reference == 'ignore':
+ print 'Failed to parse "%s"' % text
+ print ' expected: %s' % (reference or '<exception>')
+ print ' parsed: %s' % value
+ elif _debug:
+ print 'Parsed "%s" successfully' % text
+ if _debug:
+ if not valid_datetime:
+ print ' "%s" failed date/time validation' % text
+ if not valid_date:
+ print ' "%s" failed date validation' % text
+
+ et = dt.datetime.now()
+ print 'done. (after %f seconds)' % ((et-t).seconds)
+
+if __name__ == '__main__':
+ _test()
diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py
index 5e5a96b68..c8bc9438a 100644
--- a/numpy/core/arrayprint.py
+++ b/numpy/core/arrayprint.py
@@ -189,10 +189,13 @@ def _array2string(a, max_line_width, precision, suppress_small, separator=' ',
# make sure True and False line up.
format_function = _boolFormatter
elif issubclass(dtypeobj, _nt.integer):
- max_str_len = max(len(str(maximum.reduce(data))),
- len(str(minimum.reduce(data))))
- format = '%' + str(max_str_len) + 'd'
- format_function = lambda x: _formatInteger(x, format)
+ if issubclass(dtypeobj, _nt.timeinteger):
+ format_function = str
+ else:
+ max_str_len = max(len(str(maximum.reduce(data))),
+ len(str(minimum.reduce(data))))
+ format = '%' + str(max_str_len) + 'd'
+ format_function = lambda x: _formatInteger(x, format)
elif issubclass(dtypeobj, _nt.floating):
if issubclass(dtypeobj, _nt.longfloat):
format_function = _longfloatFormatter(precision)
diff --git a/numpy/core/code_generators/generate_numpy_api.py b/numpy/core/code_generators/generate_numpy_api.py
index 06bb6d4d2..69f8c2026 100644
--- a/numpy/core/code_generators/generate_numpy_api.py
+++ b/numpy/core/code_generators/generate_numpy_api.py
@@ -2,12 +2,12 @@ import os
import genapi
types = ['Generic','Number','Integer','SignedInteger','UnsignedInteger',
- 'Inexact',
+ 'Inexact', 'TimeInteger',
'Floating', 'ComplexFloating', 'Flexible', 'Character',
'Byte','Short','Int', 'Long', 'LongLong', 'UByte', 'UShort',
'UInt', 'ULong', 'ULongLong', 'Float', 'Double', 'LongDouble',
'CFloat', 'CDouble', 'CLongDouble', 'Object', 'String', 'Unicode',
- 'Void']
+ 'Void', 'Datetime', 'Timedelta']
h_template = r"""
#ifdef _MULTIARRAYMODULE
@@ -123,7 +123,7 @@ _import_array(void)
}
#elif NPY_BYTE_ORDER == NPY_LITTLE_ENDIAN
if (st != NPY_CPU_LITTLE) {
- PyErr_Format(PyExc_RuntimeError, "FATAL: module compiled as"\
+ PyErr_Format(PyExc_RuntimeError, "FATAL: module compiled as "\
"little endian, but detected different endianness at runtime");
return -1;
}
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py
index fd829933a..60a8b5a30 100644
--- a/numpy/core/code_generators/generate_umath.py
+++ b/numpy/core/code_generators/generate_umath.py
@@ -1,5 +1,9 @@
-import re, textwrap
-import sys, os
+import os
+import re
+import struct
+import sys
+import textwrap
+
sys.path.insert(0, os.path.dirname(__file__))
import ufunc_docstrings as docstrings
sys.path.pop(0)
@@ -8,16 +12,25 @@ Zero = "PyUFunc_Zero"
One = "PyUFunc_One"
None_ = "PyUFunc_None"
+# Sentinel value to specify that the loop for the given TypeDescription uses the
+# pointer to arrays as its func_data.
+UsesArraysAsData = object()
+
+
class TypeDescription(object):
- """Type signature for a ufunc
+ """Type signature for a ufunc.
Attributes
----------
-
- type: character representing the type
- func_data:
- in_:
- out:
+ type : str
+ Character representing the nominal type.
+ func_data : str or None or UsesArraysAsData, optional
+ The string representing the expression to insert into the data array, if
+ any.
+ in_ : str or None, optional
+ The typecode(s) of the inputs.
+ out : str or None, optional
+ The typecode(s) of the outputs.
"""
def __init__(self, type, f=None, in_=None, out=None):
self.type = type
@@ -94,6 +107,39 @@ class Ufunc(object):
for td in self.type_descriptions:
td.finish_signature(self.nin, self.nout)
+# String-handling utilities to avoid locale-dependence.
+
+import string
+UPPER_TABLE = string.maketrans(string.ascii_lowercase, string.ascii_uppercase)
+
+def english_upper(s):
+ """ Apply English case rules to convert ASCII strings to all upper case.
+
+ This is an internal utility function to replace calls to str.upper() such
+ that we can avoid changing behavior with changing locales. In particular,
+ Turkish has distinct dotted and dotless variants of the Latin letter "I" in
+ both lowercase and uppercase. Thus, "i".upper() != "I" in a "tr" locale.
+
+ Parameters
+ ----------
+ s : str
+
+ Returns
+ -------
+ uppered : str
+
+ Examples
+ --------
+ >>> from numpy.lib.utils import english_upper
+ >>> english_upper('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_')
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
+ >>> english_upper('')
+ ''
+ """
+ uppered = s.translate(UPPER_TABLE)
+ return uppered
+
+
#each entry in defdict is a Ufunc object.
#name: [string of chars for which it is defined,
@@ -122,16 +168,19 @@ chartoname = {'?': 'bool',
'F': 'cfloat',
'D': 'cdouble',
'G': 'clongdouble',
+ 'M': 'datetime',
+ 'm': 'timedelta',
'O': 'OBJECT',
- # M is like O, but calls a method of the object instead
+ # '.' is like 'O', but calls a method of the object instead
# of a function
- 'M': 'OBJECT',
+ '.': 'OBJECT',
}
-all = '?bBhHiIlLqQfdgFDGO'
+all = '?bBhHiIlLqQfdgFDGOMm'
O = 'O'
-M = 'M'
+M = '.'
ints = 'bBhHiIlLqQ'
+times = 'Mm'
intsO = ints + O
bints = '?' + ints
bintsO = bints + O
@@ -144,33 +193,51 @@ cmplxM = cmplx + M
inexact = flts + cmplx
noint = inexact+O
nointM = inexact+M
-allM = bints+flts+cmplxM
+allM = bints+times+flts+cmplxM
nobool = all[1:]
-nobool_or_obj = all[1:-1]
+noobj = all[:-3]+all[-2:]
+nobool_or_obj = all[1:-3]+all[-2:]
intflt = ints+flts
-intfltcmplx = nobool_or_obj
-nocmplx = bints+flts
+intfltcmplx = ints+flts+cmplx
+nocmplx = bints+times+flts
nocmplxO = nocmplx+O
nocmplxM = nocmplx+M
-noobj = all[:-1]
+notimes_or_obj = bints + inexact
+
+# Find which code corresponds to int64.
+int64 = ''
+uint64 = ''
+for code in 'bhilq':
+ if struct.calcsize(code) == 8:
+ int64 = code
+ uint64 = english_upper(code)
+ break
defdict = {
'add' :
Ufunc(2, 1, Zero,
docstrings.get('numpy.core.umath.add'),
- TD(noobj),
+ TD(notimes_or_obj),
+ [TypeDescription('M', UsesArraysAsData, 'Mm', 'M'),
+ TypeDescription('m', UsesArraysAsData, 'mm', 'm'),
+ TypeDescription('M', UsesArraysAsData, 'mM', 'M'),
+ ],
TD(O, f='PyNumber_Add'),
),
'subtract' :
Ufunc(2, 1, Zero,
docstrings.get('numpy.core.umath.subtract'),
- TD(noobj),
+ TD(notimes_or_obj),
+ [TypeDescription('M', UsesArraysAsData, 'Mm', 'M'),
+ TypeDescription('m', UsesArraysAsData, 'mm', 'm'),
+ TypeDescription('M', UsesArraysAsData, 'MM', 'm'),
+ ],
TD(O, f='PyNumber_Subtract'),
),
'multiply' :
Ufunc(2, 1, One,
docstrings.get('numpy.core.umath.multiply'),
- TD(noobj),
+ TD(notimes_or_obj),
TD(O, f='PyNumber_Multiply'),
),
'divide' :
@@ -196,7 +263,7 @@ defdict = {
'conjugate' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.conjugate'),
- TD(nobool_or_obj),
+ TD(ints+flts+cmplx),
TD(M, f='conjugate'),
),
'fmod' :
@@ -209,13 +276,13 @@ defdict = {
'square' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.square'),
- TD(nobool_or_obj),
+ TD(ints+inexact),
TD(O, f='Py_square'),
),
'reciprocal' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.reciprocal'),
- TD(nobool_or_obj),
+ TD(ints+inexact),
TD(O, f='Py_reciprocal'),
),
'ones_like' :
@@ -234,14 +301,14 @@ defdict = {
'absolute' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.absolute'),
- TD(nocmplx),
+ TD(bints+flts+times),
TD(cmplx, out=('f', 'd', 'g')),
TD(O, f='PyNumber_Absolute'),
),
'negative' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.negative'),
- TD(nocmplx),
+ TD(bints+flts+times),
TD(cmplx, f='neg'),
TD(O, f='PyNumber_Negative'),
),
@@ -338,6 +405,7 @@ defdict = {
docstrings.get('numpy.core.umath.logaddexp2'),
TD(flts, f="logaddexp2")
),
+# FIXME: decide if the times should have the bitwise operations.
'bitwise_and' :
Ufunc(2, 1, One,
docstrings.get('numpy.core.umath.bitwise_and'),
@@ -608,7 +676,7 @@ chartotype1 = {'f': 'f_f',
'D': 'D_D',
'G': 'G_G',
'O': 'O_O',
- 'M': 'O_O_method'}
+ '.': 'O_O_method'}
chartotype2 = {'f': 'ff_f',
'd': 'dd_d',
@@ -617,45 +685,12 @@ chartotype2 = {'f': 'ff_f',
'D': 'DD_D',
'G': 'GG_G',
'O': 'OO_O',
- 'M': 'OO_O_method'}
+ '.': 'OO_O_method'}
#for each name
# 1) create functions, data, and signature
# 2) fill in functions and data in InitOperators
# 3) add function.
-# String-handling utilities to avoid locale-dependence.
-
-import string
-UPPER_TABLE = string.maketrans(string.ascii_lowercase, string.ascii_uppercase)
-
-def english_upper(s):
- """ Apply English case rules to convert ASCII strings to all upper case.
-
- This is an internal utility function to replace calls to str.upper() such
- that we can avoid changing behavior with changing locales. In particular,
- Turkish has distinct dotted and dotless variants of the Latin letter "I" in
- both lowercase and uppercase. Thus, "i".upper() != "I" in a "tr" locale.
-
- Parameters
- ----------
- s : str
-
- Returns
- -------
- uppered : str
-
- Examples
- --------
- >>> from numpy.lib.utils import english_upper
- >>> english_upper('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_')
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
- >>> english_upper('')
- ''
- """
- uppered = s.translate(UPPER_TABLE)
- return uppered
-
-
def make_arrays(funcdict):
# functions array contains an entry for every type implemented
# NULL should be placed where PyUfunc_ style function will be filled in later
@@ -679,7 +714,7 @@ def make_arrays(funcdict):
thedict = chartotype1 # one input and one output
for t in uf.type_descriptions:
- if t.func_data is not None:
+ if t.func_data not in (None, UsesArraysAsData):
funclist.append('NULL')
astr = '%s_functions[%d] = PyUFunc_%s;' % \
(name, k, thedict[t.type])
@@ -689,7 +724,7 @@ def make_arrays(funcdict):
(name, k, t.func_data)
code2list.append(astr)
datalist.append('(void *)NULL')
- elif t.type == 'M':
+ elif t.type == '.':
datalist.append('(void *)"%s"' % t.func_data)
else:
astr = '%s_data[%d] = (void *) %s;' % \
@@ -698,8 +733,13 @@ def make_arrays(funcdict):
datalist.append('(void *)NULL')
#datalist.append('(void *)%s' % t.func_data)
sub += 1
+ elif t.func_data is UsesArraysAsData:
+ tname = english_upper(chartoname[t.type])
+ datalist.append('(void *)NULL')
+ funclist.append('%s_%s_%s_%s' % (tname, t.in_, t.out, name))
+ code2list.append('PyUFunc_SetUsesArraysAsData(%s_data, %s);' % (name, k))
else:
- datalist.append('(void *)NULL');
+ datalist.append('(void *)NULL')
tname = english_upper(chartoname[t.type])
funclist.append('%s_%s' % (tname, name))
diff --git a/numpy/core/code_generators/numpy_api_order.txt b/numpy/core/code_generators/numpy_api_order.txt
index 2b71fc547..72f8d5c82 100644
--- a/numpy/core/code_generators/numpy_api_order.txt
+++ b/numpy/core/code_generators/numpy_api_order.txt
@@ -175,3 +175,8 @@ PyArray_GetEndianness
PyArray_GetNDArrayCFeatureVersion
PyArray_Correlate2
PyArray_NeighborhoodIterNew
+PyArray_SetDatetimeParseFunction
+PyArray_DatetimeToDatetimeStruct
+PyArray_TimedeltaToTimedeltaStruct
+PyArray_DatetimeStructToDatetime
+PyArray_TimedeltaStructToTimedelt
diff --git a/numpy/core/code_generators/ufunc_api_order.txt b/numpy/core/code_generators/ufunc_api_order.txt
index d80f30ec8..4737e4502 100644
--- a/numpy/core/code_generators/ufunc_api_order.txt
+++ b/numpy/core/code_generators/ufunc_api_order.txt
@@ -32,3 +32,4 @@ PyUFunc_getfperr
PyUFunc_handlefperr
PyUFunc_ReplaceLoopBySignature
PyUFunc_FromFuncAndDataAndSignature
+PyUFunc_SetUsesArraysAsData
diff --git a/numpy/core/include/numpy/arrayscalars.h b/numpy/core/include/numpy/arrayscalars.h
index 01ed1e1a6..4d840e719 100644
--- a/numpy/core/include/numpy/arrayscalars.h
+++ b/numpy/core/include/numpy/arrayscalars.h
@@ -110,6 +110,18 @@ typedef struct {
PyObject * obval;
} PyObjectScalarObject;
+typedef struct {
+ PyObject_HEAD
+ npy_datetime obval;
+ PyArray_DatetimeMetaData obmeta;
+} PyDatetimeScalarObject;
+
+typedef struct {
+ PyObject_HEAD
+ npy_timedelta obval;
+ PyArray_DatetimeMetaData obmeta;
+} PyTimedeltaScalarObject;
+
typedef struct {
PyObject_HEAD
diff --git a/numpy/core/include/numpy/ndarrayobject.h b/numpy/core/include/numpy/ndarrayobject.h
index b6c366cdd..34b080732 100644
--- a/numpy/core/include/numpy/ndarrayobject.h
+++ b/numpy/core/include/numpy/ndarrayobject.h
@@ -78,12 +78,15 @@ enum NPY_TYPES { NPY_BOOL=0,
NPY_OBJECT=17,
NPY_STRING, NPY_UNICODE,
NPY_VOID,
+ NPY_DATETIME, NPY_TIMEDELTA,
NPY_NTYPES,
NPY_NOTYPE,
NPY_CHAR, /* special flag */
NPY_USERDEF=256 /* leave room for characters */
};
+#define NPY_METADATA_DTSTR "__frequency__"
+
/* basetype array priority */
#define NPY_PRIORITY 0.0
@@ -128,6 +131,8 @@ enum NPY_TYPECHAR { NPY_BOOLLTR = '?',
NPY_STRINGLTR2 = 'a',
NPY_UNICODELTR = 'U',
NPY_VOIDLTR = 'V',
+ NPY_DATETIMELTR = 'M',
+ NPY_TIMEDELTALTR = 'm',
NPY_CHARLTR = 'c',
/* No Descriptor, just a define -- this let's
@@ -182,6 +187,40 @@ typedef enum {
NPY_RAISE=2
} NPY_CLIPMODE;
+typedef enum {
+ NPY_FR_Y,
+ NPY_FR_M,
+ NPY_FR_W,
+ NPY_FR_B,
+ NPY_FR_D,
+ NPY_FR_h,
+ NPY_FR_m,
+ NPY_FR_s,
+ NPY_FR_ms,
+ NPY_FR_us,
+ NPY_FR_ns,
+ NPY_FR_ps,
+ NPY_FR_fs,
+ NPY_FR_as
+} NPY_DATETIMEUNIT;
+
+#define NPY_DATETIME_NUMUNITS (NPY_FR_as + 1)
+#define NPY_DATETIME_DEFAULTUNIT NPY_FR_us
+
+#define NPY_STR_Y "Y"
+#define NPY_STR_M "M"
+#define NPY_STR_W "W"
+#define NPY_STR_B "B"
+#define NPY_STR_D "D"
+#define NPY_STR_h "h"
+#define NPY_STR_s "s"
+#define NPY_STR_ms "ms"
+#define NPY_STR_us "us"
+#define NPY_STR_ns "ns"
+#define NPY_STR_ps "ps"
+#define NPY_STR_fs "fs"
+#define NPY_STR_as "as"
+
/* This is to typedef npy_intp to the appropriate pointer size for this
* platform. Py_intptr_t, Py_uintptr_t are defined in pyport.h. */
@@ -478,6 +517,8 @@ typedef struct _PyArray_Descr {
PyArray_ArrFuncs *f; /* a table of functions specific for each
basic data descriptor */
+
+ PyObject *metadata; /* Metadata about this dtype */
} PyArray_Descr;
typedef struct _arr_descr {
@@ -532,6 +573,27 @@ typedef struct {
int flags;
} PyArray_Chunk;
+
+typedef struct {
+ NPY_DATETIMEUNIT base;
+ int num;
+ int den; /* Converted to 1 on input for now -- an input-only mechanism */
+ int events;
+} PyArray_DatetimeMetaData;
+
+typedef struct {
+ npy_longlong year;
+ int month, day, hour, min, sec, us, ps, as;
+} npy_datetimestruct;
+
+typedef struct {
+ npy_longlong day;
+ int sec, us, ps, as;
+} npy_timedeltastruct;
+
+
+#define PyDataType_GetDatetimeMetaData(descr) ((descr->metadata == NULL) ? NULL : ((PyArray_DatetimeMetaData *)(PyCObject_AsVoidPtr(PyDict_GetItemString(descr->metadata, NPY_METADATA_DTSTR)))))
+
typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
/* Means c-style contiguous (last index varies the fastest). The
@@ -1071,6 +1133,9 @@ PyArrayNeighborhoodIter_Next(PyArrayNeighborhoodIterObject* iter);
#define PyTypeNum_ISFLEXIBLE(type) (((type) >=NPY_STRING) && \
((type) <=NPY_VOID))
+#define PyTypeNum_ISDATETIME(type) (((type) >=NPY_DATETIME) && \
+ ((type) <=NPY_TIMEDELTA))
+
#define PyTypeNum_ISUSERDEF(type) (((type) >= NPY_USERDEF) && \
((type) < NPY_USERDEF+ \
NPY_NUMUSERTYPES))
@@ -1091,6 +1156,7 @@ PyArrayNeighborhoodIter_Next(PyArrayNeighborhoodIterObject* iter);
#define PyDataType_ISCOMPLEX(obj) PyTypeNum_ISCOMPLEX(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_ISPYTHON(obj) PyTypeNum_ISPYTHON(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_ISFLEXIBLE(obj) PyTypeNum_ISFLEXIBLE(((PyArray_Descr*)(obj))->type_num)
+#define PyDataType_ISDATETIME(obj) PyTypeNum_ISDATETIME(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_ISUSERDEF(obj) PyTypeNum_ISUSERDEF(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_ISEXTENDED(obj) PyTypeNum_ISEXTENDED(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_ISOBJECT(obj) PyTypeNum_ISOBJECT(((PyArray_Descr*)(obj))->type_num)
@@ -1106,6 +1172,7 @@ PyArrayNeighborhoodIter_Next(PyArrayNeighborhoodIterObject* iter);
#define PyArray_ISCOMPLEX(obj) PyTypeNum_ISCOMPLEX(PyArray_TYPE(obj))
#define PyArray_ISPYTHON(obj) PyTypeNum_ISPYTHON(PyArray_TYPE(obj))
#define PyArray_ISFLEXIBLE(obj) PyTypeNum_ISFLEXIBLE(PyArray_TYPE(obj))
+#define PyArray_ISDATETIME(obj) PyTypeNum_ISDATETIME(PyArray_TYPE(obj))
#define PyArray_ISUSERDEF(obj) PyTypeNum_ISUSERDEF(PyArray_TYPE(obj))
#define PyArray_ISEXTENDED(obj) PyTypeNum_ISEXTENDED(PyArray_TYPE(obj))
#define PyArray_ISOBJECT(obj) PyTypeNum_ISOBJECT(PyArray_TYPE(obj))
diff --git a/numpy/core/include/numpy/noprefix.h b/numpy/core/include/numpy/noprefix.h
index dc4f71c70..ca66d2970 100644
--- a/numpy/core/include/numpy/noprefix.h
+++ b/numpy/core/include/numpy/noprefix.h
@@ -54,9 +54,13 @@ compatibility measure*/
#define Complex256 npy_complex256
#define intp npy_intp
#define uintp npy_uintp
+#define datetime npy_datetime
+#define timedelta npy_timedelta
#define SIZEOF_INTP NPY_SIZEOF_INTP
#define SIZEOF_UINTP NPY_SIZEOF_UINTP
+#define SIZEOF_DATETIME NPY_SIZEOF_DATETIME
+#define SIZEOF_TIMEDELTA NPY_SIZEOF_TIMEDELTA
#define LONGLONG_FMT NPY_LONGLONG_FMT
#define ULONGLONG_FMT NPY_ULONGLONG_FMT
@@ -97,6 +101,10 @@ compatibility measure*/
#define MAX_LONGLONG NPY_MAX_LONGLONG
#define MIN_LONGLONG NPY_MIN_LONGLONG
#define MAX_ULONGLONG NPY_MAX_ULONGLONG
+#define MIN_DATETIME NPY_MIN_DATETIME
+#define MAX_DATETIME NPY_MAX_DATETIME
+#define MIN_TIMEDELTA NPY_MIN_TIMEDELTA
+#define MAX_TIMEDELTA NPY_MAX_TIMEDELTA
#define SIZEOF_LONGDOUBLE NPY_SIZEOF_LONGDOUBLE
#define SIZEOF_LONGLONG NPY_SIZEOF_LONGLONG
@@ -109,6 +117,8 @@ compatibility measure*/
#define BITSOF_FLOAT NPY_BITSOF_FLOAT
#define BITSOF_DOUBLE NPY_BITSOF_DOUBLE
#define BITSOF_LONGDOUBLE NPY_BITSOF_LONGDOUBLE
+#define BITSOF_DATETIME NPY_BITSOF_DATETIME
+#define BITSOF_TIMEDELTA NPY_BITSOF_TIMEDELTA
#define PyArray_UCS4 npy_ucs4
#define _pya_malloc PyArray_malloc
diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h
index 8d553a67a..8f39c8871 100644
--- a/numpy/core/include/numpy/npy_common.h
+++ b/numpy/core/include/numpy/npy_common.h
@@ -110,6 +110,10 @@ typedef struct {npy_longdouble real, imag;} npy_clongdouble;
#define NPY_MAX_INT256 NPY_LONGLONG_SUFFIX(57896044618658097711785492504343953926634992332820282019728792003956564819967)
#define NPY_MIN_INT256 (-NPY_MAX_INT256 - NPY_LONGLONG_SUFFIX(1))
#define NPY_MAX_UINT256 NPY_ULONGLONG_SUFFIX(115792089237316195423570985008687907853269984665640564039457584007913129639935)
+#define NPY_MIN_DATETIME NPY_MIN_INT64
+#define NPY_MAX_DATETIME NPY_MAX_INT64
+#define NPY_MIN_TIMEDELTA NPY_MIN_INT64
+#define NPY_MAX_TIMEDELTA NPY_MAX_INT64
/* Need to find the number of bits for each type and
make definitions accordingly.
@@ -143,6 +147,9 @@ typedef struct {npy_longdouble real, imag;} npy_clongdouble;
#define NPY_MIN_LONG LONG_MIN
#define NPY_MAX_ULONG ULONG_MAX
+#define NPY_SIZEOF_DATETIME 8
+#define NPY_SIZEOF_TIMEDELTA 8
+
#define NPY_BITSOF_BOOL (sizeof(npy_bool)*CHAR_BIT)
#define NPY_BITSOF_CHAR CHAR_BIT
#define NPY_BITSOF_SHORT (NPY_SIZEOF_SHORT * CHAR_BIT)
@@ -152,6 +159,8 @@ typedef struct {npy_longdouble real, imag;} npy_clongdouble;
#define NPY_BITSOF_FLOAT (NPY_SIZEOF_FLOAT * CHAR_BIT)
#define NPY_BITSOF_DOUBLE (NPY_SIZEOF_DOUBLE * CHAR_BIT)
#define NPY_BITSOF_LONGDOUBLE (NPY_SIZEOF_LONGDOUBLE * CHAR_BIT)
+#define NPY_BITSOF_DATETIME (NPY_SIZEOF_DATETIME * CHAR_BIT)
+#define NPY_BITSOF_TIMEDELTA (NPY_SIZEOF_TIMEDELTA * CHAR_BIT)
#if NPY_BITSOF_LONG == 8
#define NPY_INT8 NPY_LONG
@@ -198,6 +207,12 @@ typedef struct {npy_longdouble real, imag;} npy_clongdouble;
#define PyUInt64ArrType_Type PyULongArrType_Type
#define NPY_INT64_FMT NPY_LONG_FMT
#define NPY_UINT64_FMT NPY_ULONG_FMT
+ typedef long npy_datetime;
+#define NPY_DATETIME_FMT NPY_LONG_FMT
+ typedef long npy_timedelta;
+#define NPY_TIMEDELTA_FMT NPY_LONG_FMT
+#define MyPyLong_FromInt64 PyLong_FromLong
+#define MyPyLong_AsInt64 PyLong_AsLong
#elif NPY_BITSOF_LONG == 128
#define NPY_INT128 NPY_LONG
#define NPY_UINT128 NPY_ULONG
@@ -272,6 +287,12 @@ typedef struct {npy_longdouble real, imag;} npy_clongdouble;
# define PyUInt64ArrType_Type PyULongLongArrType_Type
#define NPY_INT64_FMT NPY_LONGLONG_FMT
#define NPY_UINT64_FMT NPY_ULONGLONG_FMT
+ typedef npy_longlong npy_datetime;
+# define NPY_DATETIME_FMT NPY_LONGLONG_FMT
+ typedef npy_longlong npy_timedelta;
+# define NPY_TIMEDELTA_FMT NPY_LONGLONG_FMT
+# define MyPyLong_FromInt64 PyLong_FromLongLong
+# define MyPyLong_AsInt64 PyLong_AsLongLong
# endif
# define NPY_MAX_LONGLONG NPY_MAX_INT64
# define NPY_MIN_LONGLONG NPY_MIN_INT64
@@ -360,6 +381,12 @@ typedef struct {npy_longdouble real, imag;} npy_clongdouble;
# define PyUInt64ArrType_Type PyUIntArrType_Type
#define NPY_INT64_FMT NPY_INT_FMT
#define NPY_UINT64_FMT NPY_UINT_FMT
+ typedef int npy_datetime;
+# define NPY_DATETIME_FMT NPY_INT_FMT
+ typedef int npy_timedelta;
+# define NPY_TIMEDELTA_FMT NPY_INT_FMT
+# define MyPyLong_FromInt64 PyLong_FromLong
+# define MyPyLong_AsInt64 PyLong_AsLong
#endif
#elif NPY_BITSOF_INT == 128
#ifndef NPY_INT128
@@ -428,6 +455,12 @@ typedef struct {npy_longdouble real, imag;} npy_clongdouble;
# define PyUInt64ArrType_Type PyUShortArrType_Type
#define NPY_INT64_FMT NPY_SHORT_FMT
#define NPY_UINT64_FMT NPY_USHORT_FMT
+ typedef short npy_datetime;
+# define NPY_DATETIME_FMT NPY_SHORT_FMT
+ typedef short npy_timedelta;
+# define NPY_TIMEDELTA_FMT NPY_SHORT_FMT
+# define MyPyLong_FromInt64 PyLong_FromLong
+# define MyPyLong_AsInt64 PyLong_AsLong
#endif
#elif NPY_BITSOF_SHORT == 128
#ifndef NPY_INT128
@@ -497,6 +530,12 @@ typedef struct {npy_longdouble real, imag;} npy_clongdouble;
# define PyUInt64ArrType_Type PyUByteArrType_Type
#define NPY_INT64_FMT NPY_BYTE_FMT
#define NPY_UINT64_FMT NPY_UBYTE_FMT
+ typedef signed char npy_datetime;
+# define NPY_DATETIME_FMT NPY_BYTE_FMT
+ typedef signed char npy_timedelta;
+# define NPY_TIMEDELTA_FMT NPY_BYTE_FMT
+# define MyPyLong_FromInt64 PyLong_FromLong
+# define MyPyLong_AsInt64 PyLong_AsLong
#endif
#elif NPY_BITSOF_CHAR == 128
#ifndef NPY_INT128
diff --git a/numpy/core/include/numpy/old_defines.h b/numpy/core/include/numpy/old_defines.h
index c21665268..a9dfd5c21 100644
--- a/numpy/core/include/numpy/old_defines.h
+++ b/numpy/core/include/numpy/old_defines.h
@@ -33,6 +33,8 @@
#define PyArray_STRING NPY_STRING
#define PyArray_UNICODE NPY_UNICODE
#define PyArray_VOID NPY_VOID
+#define PyArray_DATETIME NPY_DATETIME
+#define PyArray_TIMEDELTA NPY_TIMEDELTA
#define PyArray_NTYPES NPY_NTYPES
#define PyArray_NOTYPE NPY_NOTYPE
#define PyArray_CHAR NPY_CHAR
@@ -108,6 +110,8 @@
#define PyArray_STRINGLTR2 NPY_STRINGLTR2
#define PyArray_UNICODELTR NPY_UNICODELTR
#define PyArray_VOIDLTR NPY_VOIDLTR
+#define PyArray_DATETIMELTR NPY_DATETIMELTR
+#define PyArray_TIMEDELTALTR NPY_TIMEDELTALTR
#define PyArray_CHARLTR NPY_CHARLTR
#define PyArray_INTPLTR NPY_INTPLTR
#define PyArray_UINTPLTR NPY_UINTPLTR
diff --git a/numpy/core/include/numpy/ufuncobject.h b/numpy/core/include/numpy/ufuncobject.h
index 9bbedc9e4..6bbf3de13 100644
--- a/numpy/core/include/numpy/ufuncobject.h
+++ b/numpy/core/include/numpy/ufuncobject.h
@@ -67,6 +67,9 @@ typedef struct {
#define UFUNC_ERR_DEFAULT 0 /* Error mode that avoids look-up (no checking) */
+#define UFUNC_OBJ_ISOBJECT 1
+#define UFUNC_OBJ_NEEDS_API 2
+
/* Default user error mode */
#define UFUNC_ERR_DEFAULT2 \
(UFUNC_ERR_PRINT << UFUNC_SHIFT_DIVIDEBYZERO) + \
@@ -132,7 +135,8 @@ typedef struct {
*/
npy_intp steps[NPY_MAXARGS];
- int obj; /* This loop uses object arrays */
+ int obj; /* This loop uses object arrays or needs the Python API */
+ /* Flags: UFUNC_OBJ_ISOBJECT, UFUNC_OBJ_NEEDS_API */
int notimplemented; /* The loop caused notimplemented */
int objfunc; /* This loop calls object functions
(an inner-loop function with argument types */
@@ -193,8 +197,8 @@ typedef struct {
#if NPY_ALLOW_THREADS
-#define NPY_LOOP_BEGIN_THREADS do {if (!(loop->obj)) _save = PyEval_SaveThread();} while (0)
-#define NPY_LOOP_END_THREADS do {if (!(loop->obj)) PyEval_RestoreThread(_save);} while (0)
+#define NPY_LOOP_BEGIN_THREADS do {if (!(loop->obj & UFUNC_OBJ_NEEDS_API)) _save = PyEval_SaveThread();} while (0)
+#define NPY_LOOP_END_THREADS do {if (!(loop->obj & UFUNC_OBJ_NEEDS_API)) PyEval_RestoreThread(_save);} while (0)
#else
#define NPY_LOOP_BEGIN_THREADS
#define NPY_LOOP_END_THREADS
@@ -232,7 +236,7 @@ typedef struct _loop1d_info {
#define UFUNC_PYVALS_NAME "UFUNC_PYVALS"
#define UFUNC_CHECK_ERROR(arg) \
- do {if (((arg)->obj && PyErr_Occurred()) || \
+ do {if ((((arg)->obj & UFUNC_OBJ_NEEDS_API) && PyErr_Occurred()) || \
((arg)->errormask && \
PyUFunc_checkfperr((arg)->errormask, \
(arg)->errobj, \
diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py
index d329ed70e..70315f1e0 100644
--- a/numpy/core/numerictypes.py
+++ b/numpy/core/numerictypes.py
@@ -15,6 +15,7 @@ Exported symbols include:
uint8 uint16 uint32 uint64 uint128
float16 float32 float64 float96 float128 float256
complex32 complex64 complex128 complex192 complex256 complex512
+ datetime64 timedelta64
c-based names
@@ -31,10 +32,14 @@ Exported symbols include:
int_, uint,
longlong, ulonglong,
+
single, csingle,
float_, complex_,
longfloat, clongfloat,
+ datetime, timedelta, (these inherit from timeinteger which inherits from signedinteger)
+
+
As part of the type-hierarchy: xx -- is bit-width
generic
@@ -85,7 +90,6 @@ import types as _types
# as numerictypes.bool, etc.
from __builtin__ import bool, int, long, float, complex, object, unicode, str
-
# String-handling utilities to avoid locale-dependence.
# "import string" is costly to import!
@@ -627,7 +631,8 @@ typecodes = {'Character':'c',
'Complex':'FDG',
'AllInteger':'bBhHiIlLqQpP',
'AllFloat':'fdgFDG',
- 'All':'?bhilqpBHILQPfdgFDGSUVO'}
+ 'Datetime': 'Mm',
+ 'All':'?bhilqpBHILQPfdgFDGSUVOMm'}
# backwards compatibility --- deprecated name
typeDict = sctypeDict
@@ -638,11 +643,13 @@ typeNA = sctypeNA
# i -> signed integer
# f -> floating point
# c -> complex
+# M -> datetime
+# m -> timedelta
# S -> string
# U -> Unicode string
# V -> record
# O -> Python object
-_kind_list = ['b', 'u', 'i', 'f', 'c', 'S', 'U', 'V', 'O']
+_kind_list = ['b', 'u', 'i', 'f', 'c', 'S', 'U', 'V', 'O', 'M', 'm']
__test_types = typecodes['AllInteger'][:-2]+typecodes['AllFloat']+'O'
__len_test_types = len(__test_types)
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c
index 1a1cd2dc8..2f49e03a5 100644
--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -319,6 +319,7 @@ array_repr_builtin(PyArrayObject *self, int repr)
static PyObject *PyArray_StrFunction = NULL;
static PyObject *PyArray_ReprFunction = NULL;
+static PyObject *PyArray_DatetimeParseFunction = NULL;
/*NUMPY_API
* Set the array print function to be a Python function.
@@ -344,6 +345,21 @@ PyArray_SetStringFunction(PyObject *op, int repr)
}
}
+/*NUMPY_API
+ * Set the date time print function to be a Python function.
+ */
+NPY_NO_EXPORT void
+PyArray_SetDatetimeParseFunction(PyObject *op)
+{
+ /* Dispose of previous callback */
+ Py_XDECREF(PyArray_DatetimeParseFunction);
+ /* Add a reference to the new callback */
+ Py_XINCREF(op);
+ /* Remember new callback */
+ PyArray_DatetimeParseFunction = op;
+}
+
+
static PyObject *
array_repr(PyArrayObject *self)
{
@@ -912,7 +928,7 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op)
Py_DECREF(result);
result = _void_compare
(self,
- (PyArrayObject *)array_other,
+ (PyArrayObject *)array_other,
cmp_op);
Py_DECREF(array_other);
}
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index 58bad98e1..420fcea7d 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -76,6 +76,9 @@ MyPyLong_AsUnsigned@Type@ (PyObject *obj)
/**end repeat**/
+
+static char * _SEQUENCE_MESSAGE = "error setting an array element with a sequence";
+
/****************** getitem and setitem **********************/
/**begin repeat
@@ -94,6 +97,7 @@ MyPyLong_AsUnsigned@Type@ (PyObject *obj)
* #kind = Bool, Byte, UByte, Short, UShort, Int, Long, UInt, ULong,
* LongLong, ULongLong, Float, Double#
*/
+
static PyObject *
@TYP@_getitem(char *ip, PyArrayObject *ap) {
@typ@ t1;
@@ -121,8 +125,7 @@ static int
if (PyErr_Occurred()) {
if (PySequence_Check(op)) {
PyErr_Clear();
- PyErr_SetString(PyExc_ValueError, "setting an array" \
- " element with a sequence.");
+ PyErr_SetString(PyExc_ValueError, _SEQUENCE_MESSAGE);
}
return -1;
}
@@ -137,6 +140,378 @@ static int
/**end repeat**/
+/* Acknowledgment: Example code contributed by Marty Fuhr sponsored by
+ Google Summer of Code 2009 was used to integrate and adapt the mxDateTime parser */
+#include "parse_datetime.c"
+/* End of contributed code. */
+
+
+/* DateTime Objects in Python only keep microsecond resolution.
+
+ When converting from datetime objects with an event component return a tuple:
+ (baseunit, number of event) where baseunit follows is a datetime type and
+ number of events is a Python integer
+ */
+
+
+/* Return a Python Datetime Object from a number representing the number of
+ units since the epoch (1970-01-01T00:00:00Z) ignoring leap seconds.
+*/
+
+NPY_NO_EXPORT PyObject *
+PyDateTime_FromNormalized(npy_datetime val, NPY_DATETIMEUNIT base)
+{
+ npy_datetimestruct ydate;
+
+ /* Must be here to use PyDateTime_FromDateAndTime */
+ PyDateTime_IMPORT;
+
+ /* We just truncate the unused variables and don't wory about overflow */
+ PyArray_DatetimeToDatetimeStruct(val, base, &ydate);
+
+ /* FIXME?: We discard ydate.ns, ydate.ps, ydate.fs, and ydate.as */
+ return PyDateTime_FromDateAndTime(ydate.year, ydate.month, ydate.day,
+ ydate.hour, ydate.min, ydate.sec,
+ ydate.us);
+}
+
+/* We also can lose precision and range here. Ignored.
+ Don't use this function if you care.
+ */
+
+NPY_NO_EXPORT PyObject *
+PyTimeDelta_FromNormalized(npy_timedelta val, NPY_DATETIMEUNIT base)
+{
+ npy_timedeltastruct td;
+
+ PyDateTime_IMPORT;
+
+ PyArray_TimedeltaToTimedeltaStruct(val, base, &td);
+
+ /* We discard td.ps and td.as */
+
+ return PyDelta_FromDSU(td.day, td.sec, td.us);
+}
+
+
+NPY_NO_EXPORT PyObject *
+PyDateTime_FromInt64(datetime val, PyArray_Descr *descr)
+{
+ PyArray_DatetimeMetaData *meta;
+
+ meta = PyDataType_GetDatetimeMetaData(descr);
+ if (meta == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "metadata not set for descriptor");
+ return NULL;
+ }
+
+ if (meta->events > 1) {
+ int events, rem, div;
+ PyObject *obj;
+ obj = PyTuple_New(2);
+ events = meta->events;
+ div = val / events;
+ rem = val % events;
+ PyTuple_SET_ITEM(obj, 1, PyInt_FromLong(rem));
+ meta->events = 1; /* This resets meta->events for recursive call */
+ PyTuple_SET_ITEM(obj, 0, PyDateTime_FromInt64(div, descr));
+ meta->events = events;
+ if (PyErr_Occurred()) {
+ Py_DECREF(obj);
+ return NULL;
+ }
+ return obj;
+ }
+
+ /* We normalize the number to a base-unit and then return a
+ Python Datetime Object
+
+ FIXME? : We silently truncate if it doesn't fit, either too wide (e.g. 10 BC)
+ or too narrow (nanoseconds)
+ */
+
+ /* Normalization and then conversion to Datetime */
+
+ /* FIXME? : Check for Overflow... */
+ return PyDateTime_FromNormalized(val*meta->num, meta->base);
+}
+
+
+NPY_NO_EXPORT PyObject *
+PyTimeDelta_FromInt64(timedelta val, PyArray_Descr *descr)
+{
+ PyArray_DatetimeMetaData *meta;
+ meta = PyDataType_GetDatetimeMetaData(descr);
+ if (meta == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "metadata not set for descriptor");
+ return NULL;
+ }
+
+ if (meta->events > 1) {
+ int events, rem, div;
+ PyObject *obj;
+ obj = PyTuple_New(2);
+ events = meta->events;
+ div = val / events;
+ rem = val % events;
+ PyTuple_SET_ITEM(obj, 1, PyInt_FromLong(rem));
+ meta->events = 1; /* This resets meta->events for recursive call */
+ PyTuple_SET_ITEM(obj, 0, PyTimeDelta_FromInt64(div, descr));
+ meta->events = events;
+ if (PyErr_Occurred()) {
+ Py_DECREF(obj);
+ return NULL;
+ }
+ return obj;
+ }
+
+ /* FIXME? : Check for Overflow */
+ return PyTimeDelta_FromNormalized(val*meta->num, meta->base);
+}
+
+
+
+NPY_NO_EXPORT npy_datetime
+PyDateTime_AsNormalized(PyObject *obj, NPY_DATETIMEUNIT base)
+{
+ npy_datetimestruct ydate;
+
+ /* Must be here to use PyDateTime_FromDateAndTime */
+ PyDateTime_IMPORT;
+
+ if (!PyDateTime_Check(obj) && !PyDate_Check(obj)) {
+ PyErr_SetString(PyExc_ValueError, "Must be a datetime.date or datetime.datetime object");
+ return -1;
+ }
+
+ ydate.year = PyDateTime_GET_YEAR(obj);
+ ydate.month = PyDateTime_GET_MONTH(obj);
+ ydate.day = PyDateTime_GET_DAY(obj);
+
+ if (PyDateTime_Check(obj)) {
+ ydate.hour = PyDateTime_DATE_GET_HOUR(obj);
+ ydate.min = PyDateTime_DATE_GET_MINUTE(obj);
+ ydate.sec = PyDateTime_DATE_GET_SECOND(obj);
+ ydate.us = PyDateTime_DATE_GET_MICROSECOND(obj);
+ }
+ else {
+ ydate.hour = 0;
+ ydate.min = 0;
+ ydate.sec = 0;
+ ydate.us = 0;
+ }
+
+ ydate.ps = 0;
+ ydate.as = 0;
+
+ /* We just truncate the unused variables and don't wory about overflow */
+ return PyArray_DatetimeStructToDatetime(base, &ydate);
+}
+
+NPY_NO_EXPORT npy_timedelta
+PyTimeDelta_AsNormalized(PyObject *obj, NPY_DATETIMEUNIT base)
+{
+ npy_timedeltastruct td;
+
+ PyDateTime_IMPORT;
+
+ if (!PyDelta_Check(obj)) {
+ PyErr_SetString(PyExc_ValueError, "Must be a datetime.timedelta object");
+ return -1;
+ }
+
+ td.day = ((PyDateTime_Delta *)obj)->days;
+ td.sec = ((PyDateTime_Delta *)obj)->seconds;
+ td.us = ((PyDateTime_Delta *)obj)->microseconds;
+ td.ps = 0;
+ td.as = 0;
+
+ return PyArray_TimedeltaStructToTimedelta(base, &td);
+}
+
+
+/* These expect:
+
+ * a 2-tuple if meta->events > 1 (baseobj, num-counts)
+ * where baseobj is a datetime object or a timedelta object
+ * respectively.
+
+*/
+
+NPY_NO_EXPORT npy_datetime
+PyDateTime_AsInt64(PyObject *obj, PyArray_Descr *descr)
+{
+ PyArray_DatetimeMetaData *meta;
+ npy_datetime res;
+
+ meta = PyDataType_GetDatetimeMetaData(descr);
+ if (meta == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "metadata not set for descriptor");
+ return -1;
+ }
+
+
+ if (meta->events > 1) {
+ datetime tmp;
+ if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) {
+ PyErr_SetString(PyExc_ValueError, "need a 2-tuple on setting if events > 1");
+ return -1;
+ }
+ tmp = PyDateTime_AsInt64(PyTuple_GET_ITEM(obj, 0), descr);
+ if (PyErr_Occurred()) return -1;
+ /* FIXME: Check for overflow */
+ tmp *= meta->events;
+ tmp += MyPyLong_AsLongLong(PyTuple_GET_ITEM(obj, 1));
+ if (PyErr_Occurred()) return -1;
+ return tmp;
+ }
+
+ res = PyDateTime_AsNormalized(obj, meta->base);
+ return res / meta->num;
+}
+
+
+NPY_NO_EXPORT timedelta
+PyTimeDelta_AsInt64(PyObject *obj, PyArray_Descr *descr)
+{
+ PyArray_DatetimeMetaData *meta;
+ npy_timedelta res;
+ meta = PyDataType_GetDatetimeMetaData(descr);
+ if (meta == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "metadata not set for descriptor");
+ return -1;
+ }
+
+ if (meta->events > 1) {
+ timedelta tmp;
+ if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) {
+ PyErr_SetString(PyExc_ValueError, "need a 2-tuple on setting if events > 1");
+ return -1;
+ }
+ tmp = PyTimeDelta_AsInt64(PyTuple_GET_ITEM(obj, 0), descr);
+ if (PyErr_Occurred()) return -1;
+ /* FIXME: Check for overflow */
+ tmp *= meta->events;
+ tmp += MyPyLong_AsLongLong(PyTuple_GET_ITEM(obj, 1));
+ if (PyErr_Occurred()) return -1;
+ return tmp;
+ }
+
+ res = PyTimeDelta_AsNormalized(obj, meta->base);
+ return res / meta->num;
+}
+
+
+/* Always return DateTime Object after normalizing to basic units (or a tuple
+ if meta->events > 1):
+
+ Problem: DateTime does not support all the resolutions (ns) nor the dynamic
+ range (pre 1 AD) of NumPy Date-times.
+
+ * getitem is not used that much --- if losing resolution hurts, stick
+ with the array scalar versions of the date-time.
+
+ * considered returning array scalars here just like longdouble.
+ This has the problem of recursion in some cases (because in a few
+ places the code expects getitem to return a Python-system object)
+
+ * considered returning different things depending on the resolution but
+ this would make it hard to write generic code ---
+ but do you need to write generic code on all the frequencies
+ because they cover a wide range.
+
+ Solution: The use-case of actually wanting a date-time object when the resolution
+ and dynamic range match, make it the compelling default. When it
+ does fails, there are alternatives for the programmer to use.
+
+ New question: Should we change (c)longdouble at this point? to return Python Float?
+
+*/
+
+static PyObject *
+DATETIME_getitem(char *ip, PyArrayObject *ap) {
+ datetime t1;
+
+ if ((ap==NULL) || PyArray_ISBEHAVED_RO(ap)) {
+ t1 = *((datetime *)ip);
+ return PyDateTime_FromInt64((datetime)t1, ap->descr);
+ }
+ else {
+ ap->descr->f->copyswap(&t1, ip, !PyArray_ISNOTSWAPPED(ap), ap);
+ return PyDateTime_FromInt64((datetime)t1, ap->descr);
+ }
+}
+
+
+static PyObject *
+TIMEDELTA_getitem(char *ip, PyArrayObject *ap) {
+ timedelta t1;
+
+ if ((ap==NULL) || PyArray_ISBEHAVED_RO(ap)) {
+ t1 = *((timedelta *)ip);
+ return PyTimeDelta_FromInt64((timedelta)t1, ap->descr);
+ }
+ else {
+ ap->descr->f->copyswap(&t1, ip, !PyArray_ISNOTSWAPPED(ap), ap);
+ return PyTimeDelta_FromInt64((timedelta)t1, ap->descr);
+ }
+}
+
+
+static int
+DATETIME_setitem(PyObject *op, char *ov, PyArrayObject *ap) {
+ datetime temp; /* ensures alignment */
+
+ if (PyArray_IsScalar(op, Datetime)) {
+ temp = ((PyDatetimeScalarObject *)op)->obval;
+ }
+ else {
+ temp = PyDateTime_AsInt64(op, ap->descr);
+ }
+ if (PyErr_Occurred()) {
+ if (PySequence_Check(op)) {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_ValueError, _SEQUENCE_MESSAGE);
+ }
+ return -1;
+ }
+ if (ap == NULL || PyArray_ISBEHAVED(ap))
+ *((datetime *)ov)=temp;
+ else {
+ ap->descr->f->copyswap(ov, &temp, !PyArray_ISNOTSWAPPED(ap),
+ ap);
+ }
+ return 0;
+}
+
+
+static int
+TIMEDELTA_setitem(PyObject *op, char *ov, PyArrayObject *ap) {
+ timedelta temp; /* ensures alignment */
+
+ if (PyArray_IsScalar(op, Timedelta)) {
+ temp = ((PyTimedeltaScalarObject *)op)->obval;
+ }
+ else {
+ temp = PyTimeDelta_AsInt64(op, ap->descr);
+ }
+ if (PyErr_Occurred()) {
+ if (PySequence_Check(op)) {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_ValueError, _SEQUENCE_MESSAGE);
+ }
+ return -1;
+ }
+ if (ap == NULL || PyArray_ISBEHAVED(ap))
+ *((timedelta *)ov)=temp;
+ else {
+ ap->descr->f->copyswap(ov, &temp, !PyArray_ISNOTSWAPPED(ap),
+ ap);
+ }
+ return 0;
+}
+
+
/**begin repeat
*
* #TYP = CFLOAT, CDOUBLE#
@@ -211,6 +586,9 @@ static int
/**end repeat**/
+/* These return array scalars which is different than other date-types.
+ */
+
static PyObject *
LONGDOUBLE_getitem(char *ip, PyArrayObject *ap)
{
@@ -684,17 +1062,21 @@ fail:
/**begin repeat
*
* #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
- * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
+ * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, DATETIME,
+ * TIMEDELTA#
* #totype = byte, ubyte, short, ushort, int, uint, long, ulong,
- * longlong, ulonglong, float, double, longdouble#
+ * longlong, ulonglong, float, double, longdouble, datetime,
+ * timedelta#
*/
/**begin repeat1
*
* #FROMTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
- * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
+ * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, DATETIME,
+ * TIMEDELTA#
* #fromtype = byte, ubyte, short, ushort, int, uint, long, ulong,
- * longlong, ulonglong, float, double, longdouble#
+ * longlong, ulonglong, float, double, longdouble, datetime,
+ * timedelta#
*/
static void
@FROMTYPE@_to_@TOTYPE@(@fromtype@ *ip, @totype@ *op, intp n,
@@ -728,9 +1110,11 @@ static void
/**begin repeat
*
* #FROMTYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
- * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
+ * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, DATETIME,
+ * TIMEDELTA#
* #fromtype = Bool, byte, ubyte, short, ushort, int, uint, long, ulong,
- * longlong, ulonglong, float, double, longdouble#
+ * longlong, ulonglong, float, double, longdouble, datetime,
+ * timedelta#
*/
static void
@FROMTYPE@_to_BOOL(@fromtype@ *ip, Bool *op, intp n,
@@ -761,9 +1145,11 @@ static void
/**begin repeat
* #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
- * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
+ * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, DATETIME,
+ * TIMEDELTA#
* #totype = byte, ubyte, short, ushort, int, uint, long, ulong,
- * longlong, ulonglong, float, double, longdouble#
+ * longlong, ulonglong, float, double, longdouble, datetime,
+ * timedelta#
*/
static void
BOOL_to_@TOTYPE@(Bool *ip, @totype@ *op, intp n,
@@ -783,9 +1169,11 @@ BOOL_to_@TOTYPE@(Bool *ip, @totype@ *op, intp n,
/**begin repeat1
* #FROMTYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
- * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
+ * LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, DATETIME,
+ * TIMEDELTA#
* #fromtype = Bool, byte, ubyte, short, ushort, int, uint, long, ulong,
- * longlong, ulonglong, float, double, longdouble#
+ * longlong, ulonglong, float, double, longdouble, datetime,
+ * timedelta#
*/
static void
@FROMTYPE@_to_@TOTYPE@(@fromtype@ *ip, @totype@ *op, intp n,
@@ -827,11 +1215,13 @@ static void
*
* #FROMTYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
* LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,
- * CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, VOID, OBJECT#
+ * CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, VOID, OBJECT,
+ * DATETIME, TIMEDELTA#
* #fromtype = Bool, byte, ubyte, short, ushort, int, uint, long, ulong,
* longlong, ulonglong, float, double, longdouble,
- * cfloat, cdouble, clongdouble, char, char, char, PyObject *#
- * #skip = 1*17, aip->descr->elsize*3, 1#
+ * cfloat, cdouble, clongdouble, char, char, char, PyObject *,
+ * datetime, timedelta#
+ * #skip = 1*17, aip->descr->elsize*3, 1*3#
*/
static void
@FROMTYPE@_to_OBJECT(@fromtype@ *ip, PyObject **op, intp n, PyArrayObject *aip,
@@ -863,6 +1253,8 @@ static void
#define _NPY_UNUSEDCFLOAT NPY_UNUSED
#define _NPY_UNUSEDCDOUBLE NPY_UNUSED
#define _NPY_UNUSEDCLONGDOUBLE NPY_UNUSED
+#define _NPY_UNUSEDDATETIME NPY_UNUSED
+#define _NPY_UNUSEDTIMEDELTA NPY_UNUSED
#define _NPY_UNUSEDSTRING
#define _NPY_UNUSEDVOID
#define _NPY_UNUSEDUNICODE
@@ -871,11 +1263,13 @@ static void
*
* #TOTYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
* LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,
- * CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, VOID#
+ * CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, VOID, DATETIME,
+ * TIMEDELTA#
* #totype = Bool, byte, ubyte, short, ushort, int, uint, long, ulong,
* longlong, ulonglong, float, double, longdouble,
- * cfloat, cdouble, clongdouble, char, char, char#
- * #skip = 1*17, aip->descr->elsize*3#
+ * cfloat, cdouble, clongdouble, char, char, char, datetime,
+ * timedelta#
+ * #skip = 1*17, aip->descr->elsize*3, 1*2#
*/
static void
OBJECT_to_@TOTYPE@(PyObject **ip, @totype@ *op, intp n,
@@ -898,13 +1292,13 @@ OBJECT_to_@TOTYPE@(PyObject **ip, @totype@ *op, intp n,
/**begin repeat
*
- * #from = STRING*20, UNICODE*20, VOID*20#
- * #fromtyp = char*60#
- * #to = (BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, VOID)*3#
- * #totyp = (Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, char, char, char)*3#
- * #oskip = (1*17,aop->descr->elsize*3)*3#
- * #convert = 1*17, 0*3, 1*17, 0*3, 0*20#
- * #convstr = (Int*9, Long*2, Float*3, Complex*3, Tuple*3)*3#
+ * #from = STRING*22, UNICODE*22, VOID*22#
+ * #fromtyp = char*66#
+ * #to = (BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, VOID, DATETIME, TIMEDELTA)*3#
+ * #totyp = (Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, char, char, char, datetime, timedelta)*3#
+ * #oskip = (1*17,aop->descr->elsize*3,1*2)*3#
+ * #convert = 1*17, 0*3, 1*2, 1*17, 0*3, 1*2, 0*22#
+ * #convstr = (Int*9, Long*2, Float*3, Complex*3, Tuple*3, Long*2)*3#
*/
static void
@from@_to_@to@(@fromtyp@ *ip, @totyp@ *op, intp n, PyArrayObject *aip,
@@ -940,10 +1334,10 @@ static void
/**begin repeat
*
- * #to = STRING*17, UNICODE*17, VOID*17#
- * #totyp = char*17, char*17, char*17#
- * #from = (BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE)*3#
- * #fromtyp = (Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble)*3#
+ * #to = STRING*19, UNICODE*19, VOID*19#
+ * #totyp = char*19, char*19, char*19#
+ * #from = (BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE, DATETIME, TIMEDELTA)*3#
+ * #fromtyp = (Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, datetime, timedelta)*3#
*/
static void
@from@_to_@to@(@fromtyp@ *ip, @totyp@ *op, intp n, PyArrayObject *aip,
@@ -1032,7 +1426,8 @@ BOOL_scan(FILE *fp, Bool *ip, void *NPY_UNUSED(ignore), PyArray_Descr *NPY_UNUSE
}
/**begin repeat
- * #fname = CFLOAT, CDOUBLE, CLONGDOUBLE, OBJECT, STRING, UNICODE, VOID#
+ * #fname = CFLOAT, CDOUBLE, CLONGDOUBLE, OBJECT, STRING, UNICODE, VOID,
+ * DATETIME, TIMEDELTA#
*/
#define @fname@_scan NULL
/**end repeat**/
@@ -1040,10 +1435,12 @@ BOOL_scan(FILE *fp, Bool *ip, void *NPY_UNUSED(ignore), PyArray_Descr *NPY_UNUSE
/****************** fromstr *************************************/
/**begin repeat
- * #fname = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG#
- * #type = byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong#
- * #func = (l, ul)*5#
- * #btype = (long, ulong)*5#
+ * #fname = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG,
+ * ULONGLONG, DATETIME, TIMEDELTA#
+ * #type = byte, ubyte, short, ushort, int, uint, long, ulong, longlong,
+ * ulonglong, datetime, timedelta#
+ * #func = (l, ul)*5, l, l#
+ * #btype = (long, ulong)*5, long, long#
*/
static int
@fname@_fromstr(char *str, @type@ *ip, char **endptr, PyArray_Descr *NPY_UNUSED(ignore))
@@ -1085,9 +1482,12 @@ static int
/**begin repeat
*
- * #fname = SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
- * #fsize = SHORT, SHORT, INT, INT, LONG, LONG, LONGLONG, LONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
- * #type = short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble#
+ * #fname = SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT,
+ * DOUBLE, LONGDOUBLE, DATETIME, TIMEDELTA#
+ * #fsize = SHORT, SHORT, INT, INT, LONG, LONG, LONGLONG, LONGLONG, FLOAT,
+ * DOUBLE, LONGDOUBLE, DATETIME, TIMEDELTA#
+ * #type = short, ushort, int, uint, long, ulong, longlong, ulonglong, float,
+ * double, longdouble, datetime, timedelta#
*/
static void
@fname@_copyswapn (void *dst, intp dstride, void *src, intp sstride,
@@ -1572,8 +1972,8 @@ UNICODE_copyswap (char *dst, char *src, int swap, PyArrayObject *arr)
/****************** nonzero **********************************/
/**begin repeat
-#fname=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE#
-#type=Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble#
+#fname=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,DATETIME,TIMEDELTA#
+#type=Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, datetime, timedelta#
*/
static Bool
@fname@_nonzero (@type@ *ip, PyArrayObject *ap)
@@ -1766,9 +2166,9 @@ BOOL_compare(Bool *ip1, Bool *ip2, PyArrayObject *NPY_UNUSED(ap))
/**begin repeat
* #TYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
- * LONGLONG, ULONGLONG#
+ * LONGLONG, ULONGLONG, DATETIME, TIMEDELTA#
* #type = byte, ubyte, short, ushort, int, uint, long, ulong,
- * longlong, ulonglong#
+ * longlong, ulonglong, datetime, timedelta#
*/
static int
@@ -2019,9 +2419,9 @@ finish:
/**begin repeat
-#fname= BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE#
-#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, float, double, longdouble#
-#incr= ip++*14, ip+=2*3#
+#fname= BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE, DATETIME, TIMEDELTA#
+#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, float, double, longdouble, datetime, timedelta#
+#incr= ip++*14, ip+=2*3, ip++*2#
*/
static int
@@ -2109,9 +2509,9 @@ BOOL_dot(char *ip1, intp is1, char *ip2, intp is2, char *op, intp n,
}
/**begin repeat
-#name=BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
-#type= byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble#
-#out= long, ulong, long, ulong, long, ulong, long, ulong, longlong, ulonglong, float, double, longdouble#
+#name=BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, DATETIME, TIMEDELTA#
+#type= byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, datetime, timedelta#
+#out= long, ulong, long, ulong, long, ulong, long, ulong, longlong, ulonglong, float, double, longdouble, datetime, timedelta#
*/
static void
@name@_dot(char *ip1, intp is1, char *ip2, intp is2, char *op, intp n,
@@ -2209,8 +2609,8 @@ finish:
}
/**begin repeat
-#NAME=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE#
-#typ=byte,ubyte,short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble#
+#NAME=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,DATETIME,TIMEDELTA#
+#typ=byte,ubyte,short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble,datetime,timedelta#
*/
static void
@NAME@_fill(@typ@ *buffer, intp length, void *NPY_UNUSED(ignored))
@@ -2275,8 +2675,8 @@ static void
/**end repeat**/
/**begin repeat
-#NAME=SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE#
-#typ=short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble,cfloat,cdouble,clongdouble#
+#NAME=SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,DATETIME,TIMEDELTA#
+#typ=short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble,cfloat,cdouble,clongdouble,datetime,timedelta#
*/
static void
@NAME@_fillwithscalar(@typ@ *buffer, intp length, @typ@ *value, void *NPY_UNUSED(ignored))
@@ -2297,8 +2697,8 @@ static void
*************************/
/**begin repeat
-#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
-#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble#
+#name=BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, DATETIME, TIMEDELTA#
+#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, datetime, timedelta#
*/
static void
@name@_fastclip(@type@ *in, intp ni, @type@ *min, @type@ *max, @type@ *out)
@@ -2396,8 +2796,8 @@ static void
*************************/
/**begin repeat
-#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,CFLOAT, CDOUBLE, CLONGDOUBLE#
-#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble,cfloat, cdouble, clongdouble#
+#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,CFLOAT, CDOUBLE, CLONGDOUBLE, DATETIME, TIMEDELTA#
+#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble,cfloat, cdouble, clongdouble, datetime, timedelta#
*/
static void
@name@_fastputmask(@type@ *in, Bool *mask, intp ni, @type@ *vals, intp nv)
@@ -2433,8 +2833,8 @@ static void
*************************/
/**begin repeat
-#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,CFLOAT, CDOUBLE, CLONGDOUBLE#
-#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble,cfloat, cdouble, clongdouble#
+#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,CFLOAT, CDOUBLE, CLONGDOUBLE, DATETIME, TIMEDELTA#
+#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble,cfloat, cdouble, clongdouble, datetime, timedelta#
*/
static int
@name@_fasttake(@type@ *dest, @type@ *src, intp *indarray,
@@ -2550,7 +2950,9 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = {
(PyArray_VectorUnaryFunc*)@from@_to_OBJECT,
(PyArray_VectorUnaryFunc*)@from@_to_STRING,
(PyArray_VectorUnaryFunc*)@from@_to_UNICODE,
- (PyArray_VectorUnaryFunc*)@from@_to_VOID
+ (PyArray_VectorUnaryFunc*)@from@_to_VOID,
+ (PyArray_VectorUnaryFunc*)@from@_to_DATETIME,
+ (PyArray_VectorUnaryFunc*)@from@_to_TIMEDELTA
},
(PyArray_GetItemFunc*)@from@_getitem,
(PyArray_SetItemFunc*)@from@_setitem,
@@ -2598,13 +3000,13 @@ static PyArray_Descr @from@_Descr = {
/**begin repeat
-#from= BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT#
-#num= 1*14,2*3,1#
-#fromtyp= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, float, double, longdouble, PyObject *#
-#NAME= Bool, Byte, UByte, Short, UShort, Int, UInt, Long, ULong, LongLong, ULongLong, Float, Double, LongDouble, CFloat, CDouble, CLongDouble, Object#
-#kind= GENBOOL, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, FLOATING, FLOATING, FLOATING, COMPLEX, COMPLEX, COMPLEX, OBJECT#
-#endian= |*3, =*14, |#
-#isobject= 0*17,NPY_OBJECT_DTYPE_FLAGS#
+#from= BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,DATETIME,TIMEDELTA#
+#num= 1*14,2*3,1*3#
+#fromtyp= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, float, double, longdouble, PyObject *, datetime, timedelta#
+#NAME= Bool, Byte, UByte, Short, UShort, Int, UInt, Long, ULong, LongLong, ULongLong, Float, Double, LongDouble, CFloat, CDouble, CLongDouble, Object, Datetime, Timedelta#
+#kind= GENBOOL, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, FLOATING, FLOATING, FLOATING, COMPLEX, COMPLEX, COMPLEX, OBJECT, DATETIME, TIMEDELTA#
+#endian= |*3, =*14, |, =*2#
+#isobject= 0*17,NPY_OBJECT_DTYPE_FLAGS,0*2#
*/
static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = {
@@ -2629,7 +3031,9 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = {
(PyArray_VectorUnaryFunc*)@from@_to_OBJECT,
(PyArray_VectorUnaryFunc*)@from@_to_STRING,
(PyArray_VectorUnaryFunc*)@from@_to_UNICODE,
- (PyArray_VectorUnaryFunc*)@from@_to_VOID
+ (PyArray_VectorUnaryFunc*)@from@_to_VOID,
+ (PyArray_VectorUnaryFunc*)@from@_to_DATETIME,
+ (PyArray_VectorUnaryFunc*)@from@_to_TIMEDELTA
},
(PyArray_GetItemFunc*)@from@_getitem,
(PyArray_SetItemFunc*)@from@_setitem,
@@ -2675,6 +3079,25 @@ NPY_NO_EXPORT PyArray_Descr @from@_Descr = {
/**end repeat**/
+static void
+_init_datetime_descr(PyArray_Descr *descr)
+{
+ PyArray_DatetimeMetaData *dt_data;
+ PyObject *cobj;
+
+ dt_data = _pya_malloc(sizeof(PyArray_DatetimeMetaData));
+ dt_data->base = NPY_FR_us;
+ dt_data->num = 1;
+ dt_data->den = 1;
+ dt_data->events = 1;
+
+ cobj = PyCObject_FromVoidPtr((void *)dt_data, _pya_free);
+ descr->metadata = PyDict_New();
+ PyDict_SetItemString(descr->metadata, NPY_METADATA_DTSTR, cobj);
+ Py_DECREF(cobj);
+
+}
+
#define _MAX_LETTER 128
static char _letter_to_num[_MAX_LETTER];
@@ -2700,6 +3123,8 @@ static PyArray_Descr *_builtin_descrs[] = {
&STRING_Descr,
&UNICODE_Descr,
&VOID_Descr,
+ &DATETIME_Descr,
+ &TIMEDELTA_Descr,
};
/*NUMPY_API
@@ -2753,6 +3178,14 @@ PyArray_DescrFromType(int type)
else {
Py_INCREF(ret);
}
+
+ /* Make sure dtype metadata is initialized for DATETIME */
+ if (PyTypeNum_ISDATETIME(type)) {
+ if (ret->metadata == NULL) {
+ _init_datetime_descr(ret);
+ }
+ }
+
return ret;
}
@@ -2768,14 +3201,14 @@ set_typeinfo(PyObject *dict)
}
/**begin repeat
-#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,INTP,UINTP,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,STRING,UNICODE,VOID#
+#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,INTP,UINTP,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,STRING,UNICODE,VOID,DATETIME,TIMEDELTA#
*/
_letter_to_num[PyArray_@name@LTR] = PyArray_@name@;
/**end repeat**/
_letter_to_num[PyArray_STRINGLTR2] = PyArray_STRING;
/**begin repeat
-#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,STRING,UNICODE,VOID#
+#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,STRING,UNICODE,VOID,DATETIME,TIMEDELTA#
*/
@name@_Descr.fields = Py_None;
/**end repeat**/
@@ -2857,6 +3290,22 @@ set_typeinfo(PyObject *dict)
(PyObject *)\
&PyVoidArrType_Type));
Py_DECREF(s);
+ PyDict_SetItemString(infodict, "DATETIME",
+ s=Py_BuildValue("ciiiNNO", PyArray_DATETIMELTR,
+ PyArray_DATETIME,
+ sizeof(npy_datetime)*CHAR_BIT,
+ _ALIGN(npy_datetime),
+ MyPyLong_FromInt64(MAX_DATETIME), MyPyLong_FromInt64(MIN_DATETIME),
+ (PyObject *)&PyDatetimeArrType_Type));
+ Py_DECREF(s);
+ PyDict_SetItemString(infodict, "TIMEDELTA",
+ s=Py_BuildValue("ciiiNNO", PyArray_TIMEDELTALTR,
+ PyArray_TIMEDELTA,
+ sizeof(npy_timedelta)*CHAR_BIT,
+ _ALIGN(npy_timedelta),
+ MyPyLong_FromInt64(MAX_TIMEDELTA), MyPyLong_FromInt64(MIN_TIMEDELTA),
+ (PyObject *)&PyTimedeltaArrType_Type));
+ Py_DECREF(s);
#define SETTYPE(name) \
Py_INCREF(&Py##name##ArrType_Type); \
@@ -2868,6 +3317,7 @@ set_typeinfo(PyObject *dict)
SETTYPE(Integer);
SETTYPE(Inexact);
SETTYPE(SignedInteger);
+ SETTYPE(TimeInteger);
SETTYPE(UnsignedInteger);
SETTYPE(Floating);
SETTYPE(ComplexFloating);
diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c
index f764f1830..83d9a1a72 100644
--- a/numpy/core/src/multiarray/common.c
+++ b/numpy/core/src/multiarray/common.c
@@ -564,114 +564,3 @@ _IsWriteable(PyArrayObject *ap)
}
return TRUE;
}
-
-NPY_NO_EXPORT void
-_unaligned_strided_byte_copy(char *dst, intp outstrides, char *src,
- intp instrides, intp N, int elsize)
-{
- intp i;
- char *tout = dst;
- char *tin = src;
-
-#define _COPY_N_SIZE(size) \
- for(i=0; i<N; i++) { \
- memcpy(tout, tin, size); \
- tin += instrides; \
- tout += outstrides; \
- } \
- return
-
- switch(elsize) {
- case 8:
- _COPY_N_SIZE(8);
- case 4:
- _COPY_N_SIZE(4);
- case 1:
- _COPY_N_SIZE(1);
- case 2:
- _COPY_N_SIZE(2);
- case 16:
- _COPY_N_SIZE(16);
- default:
- _COPY_N_SIZE(elsize);
- }
-#undef _COPY_N_SIZE
-
-}
-
-NPY_NO_EXPORT void
-_strided_byte_swap(void *p, intp stride, intp n, int size)
-{
- char *a, *b, c = 0;
- int j, m;
-
- switch(size) {
- case 1: /* no byteswap necessary */
- break;
- case 4:
- for (a = (char*)p; n > 0; n--, a += stride - 1) {
- b = a + 3;
- c = *a; *a++ = *b; *b-- = c;
- c = *a; *a = *b; *b = c;
- }
- break;
- case 8:
- for (a = (char*)p; n > 0; n--, a += stride - 3) {
- b = a + 7;
- c = *a; *a++ = *b; *b-- = c;
- c = *a; *a++ = *b; *b-- = c;
- c = *a; *a++ = *b; *b-- = c;
- c = *a; *a = *b; *b = c;
- }
- break;
- case 2:
- for (a = (char*)p; n > 0; n--, a += stride) {
- b = a + 1;
- c = *a; *a = *b; *b = c;
- }
- break;
- default:
- m = size/2;
- for (a = (char *)p; n > 0; n--, a += stride - m) {
- b = a + (size - 1);
- for (j = 0; j < m; j++) {
- c=*a; *a++ = *b; *b-- = c;
- }
- }
- break;
- }
-}
-
-NPY_NO_EXPORT void
-byte_swap_vector(void *p, intp n, int size)
-{
- _strided_byte_swap(p, (intp) size, n, size);
- return;
-}
-
-/* If numitems > 1, then dst must be contiguous */
-NPY_NO_EXPORT void
-copy_and_swap(void *dst, void *src, int itemsize, intp numitems,
- intp srcstrides, int swap)
-{
- intp i;
- char *s1 = (char *)src;
- char *d1 = (char *)dst;
-
-
- if ((numitems == 1) || (itemsize == srcstrides)) {
- memcpy(d1, s1, itemsize*numitems);
- }
- else {
- for (i = 0; i < numitems; i++) {
- memcpy(d1, s1, itemsize);
- d1 += itemsize;
- s1 += srcstrides;
- }
- }
-
- if (swap) {
- byte_swap_vector(d1, numitems, itemsize);
- }
-}
-
diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h
index 3dbf1994e..67ee9b66f 100644
--- a/numpy/core/src/multiarray/common.h
+++ b/numpy/core/src/multiarray/common.h
@@ -27,20 +27,6 @@ _IsAligned(PyArrayObject *ap);
NPY_NO_EXPORT Bool
_IsWriteable(PyArrayObject *ap);
-NPY_NO_EXPORT void
-_unaligned_strided_byte_copy(char *dst, intp outstrides, char *src,
- intp instrides, intp N, int elsize);
-
-NPY_NO_EXPORT void
-_strided_byte_swap(void *p, intp stride, intp n, int size);
-
-NPY_NO_EXPORT void
-byte_swap_vector(void *p, intp n, int size);
-
-NPY_NO_EXPORT void
-copy_and_swap(void *dst, void *src, int itemsize, intp numitems,
- intp srcstrides, int swap);
-
#ifndef Py_UNICODE_WIDE
#include "ucsnarrow.h"
#endif
diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c
index eb5d79e3f..a667bb30e 100644
--- a/numpy/core/src/multiarray/convert_datatype.c
+++ b/numpy/core/src/multiarray/convert_datatype.c
@@ -510,6 +510,10 @@ PyArray_CanCastSafely(int fromtype, int totype)
if (totype == PyArray_BOOL) {
return 0;
}
+ if (fromtype == PyArray_DATETIME || fromtype == PyArray_TIMEDELTA ||
+ totype == PyArray_DATETIME || totype == PyArray_TIMEDELTA) {
+ return 0;
+ }
if (totype == PyArray_OBJECT || totype == PyArray_VOID) {
return 1;
}
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index 27cb54d1d..e4873913a 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -417,6 +417,116 @@ _unaligned_strided_byte_move(char *dst, intp outstrides, char *src,
}
+NPY_NO_EXPORT void
+_unaligned_strided_byte_copy(char *dst, intp outstrides, char *src,
+ intp instrides, intp N, int elsize)
+{
+ intp i;
+ char *tout = dst;
+ char *tin = src;
+
+#define _COPY_N_SIZE(size) \
+ for(i=0; i<N; i++) { \
+ memcpy(tout, tin, size); \
+ tin += instrides; \
+ tout += outstrides; \
+ } \
+ return
+
+ switch(elsize) {
+ case 8:
+ _COPY_N_SIZE(8);
+ case 4:
+ _COPY_N_SIZE(4);
+ case 1:
+ _COPY_N_SIZE(1);
+ case 2:
+ _COPY_N_SIZE(2);
+ case 16:
+ _COPY_N_SIZE(16);
+ default:
+ _COPY_N_SIZE(elsize);
+ }
+#undef _COPY_N_SIZE
+
+}
+
+NPY_NO_EXPORT void
+_strided_byte_swap(void *p, intp stride, intp n, int size)
+{
+ char *a, *b, c = 0;
+ int j, m;
+
+ switch(size) {
+ case 1: /* no byteswap necessary */
+ break;
+ case 4:
+ for (a = (char*)p; n > 0; n--, a += stride - 1) {
+ b = a + 3;
+ c = *a; *a++ = *b; *b-- = c;
+ c = *a; *a = *b; *b = c;
+ }
+ break;
+ case 8:
+ for (a = (char*)p; n > 0; n--, a += stride - 3) {
+ b = a + 7;
+ c = *a; *a++ = *b; *b-- = c;
+ c = *a; *a++ = *b; *b-- = c;
+ c = *a; *a++ = *b; *b-- = c;
+ c = *a; *a = *b; *b = c;
+ }
+ break;
+ case 2:
+ for (a = (char*)p; n > 0; n--, a += stride) {
+ b = a + 1;
+ c = *a; *a = *b; *b = c;
+ }
+ break;
+ default:
+ m = size/2;
+ for (a = (char *)p; n > 0; n--, a += stride - m) {
+ b = a + (size - 1);
+ for (j = 0; j < m; j++) {
+ c=*a; *a++ = *b; *b-- = c;
+ }
+ }
+ break;
+ }
+}
+
+NPY_NO_EXPORT void
+byte_swap_vector(void *p, intp n, int size)
+{
+ _strided_byte_swap(p, (intp) size, n, size);
+ return;
+}
+
+/* If numitems > 1, then dst must be contiguous */
+NPY_NO_EXPORT void
+copy_and_swap(void *dst, void *src, int itemsize, intp numitems,
+ intp srcstrides, int swap)
+{
+ intp i;
+ char *s1 = (char *)src;
+ char *d1 = (char *)dst;
+
+
+ if ((numitems == 1) || (itemsize == srcstrides)) {
+ memcpy(d1, s1, itemsize*numitems);
+ }
+ else {
+ for (i = 0; i < numitems; i++) {
+ memcpy(d1, s1, itemsize);
+ d1 += itemsize;
+ s1 += srcstrides;
+ }
+ }
+
+ if (swap) {
+ byte_swap_vector(d1, numitems, itemsize);
+ }
+}
+
static int
_copy_from0d(PyArrayObject *dest, PyArrayObject *src, int usecopy, int swap)
{
@@ -1433,23 +1543,6 @@ PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
return NULL;
}
-/*
- * This is the main array creation routine.
- *
- * Flags argument has multiple related meanings
- * depending on data and strides:
- *
- * If data is given, then flags is flags associated with data.
- * If strides is not given, then a contiguous strides array will be created
- * and the CONTIGUOUS bit will be set. If the flags argument
- * has the FORTRAN bit set, then a FORTRAN-style strides array will be
- * created (and of course the FORTRAN flag bit will be set).
- *
- * If data is not given but created here, then flags will be DEFAULT
- * and a non-zero flags argument can be used to indicate a FORTRAN style
- * array is desired.
- */
-
/*NUMPY_API
* Generic new array creation routine.
*/
@@ -3234,6 +3327,23 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, intp count)
return (PyObject *)ret;
}
+/*
+ * This is the main array creation routine.
+ *
+ * Flags argument has multiple related meanings
+ * depending on data and strides:
+ *
+ * If data is given, then flags is flags associated with data.
+ * If strides is not given, then a contiguous strides array will be created
+ * and the CONTIGUOUS bit will be set. If the flags argument
+ * has the FORTRAN bit set, then a FORTRAN-style strides array will be
+ * created (and of course the FORTRAN flag bit will be set).
+ *
+ * If data is not given but created here, then flags will be DEFAULT
+ * and a non-zero flags argument can be used to indicate a FORTRAN style
+ * array is desired.
+ */
+
NPY_NO_EXPORT size_t
_array_fill_strides(intp *strides, intp *dims, int nd, size_t itemsize,
int inflag, int *objflags)
diff --git a/numpy/core/src/multiarray/ctors.h b/numpy/core/src/multiarray/ctors.h
index 50b321ea7..5fd1d8e58 100644
--- a/numpy/core/src/multiarray/ctors.h
+++ b/numpy/core/src/multiarray/ctors.h
@@ -53,4 +53,18 @@ NPY_NO_EXPORT size_t
_array_fill_strides(intp *strides, intp *dims, int nd, size_t itemsize,
int inflag, int *objflags);
+NPY_NO_EXPORT void
+_unaligned_strided_byte_copy(char *dst, intp outstrides, char *src,
+ intp instrides, intp N, int elsize);
+
+NPY_NO_EXPORT void
+_strided_byte_swap(void *p, intp stride, intp n, int size);
+
+NPY_NO_EXPORT void
+copy_and_swap(void *dst, void *src, int itemsize, intp numitems,
+ intp srcstrides, int swap);
+
+NPY_NO_EXPORT void
+byte_swap_vector(void *p, intp n, int size);
+
#endif
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c
index 45fdc2cea..edcab8b09 100644
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -125,6 +125,21 @@ _check_for_commastring(char *type, int len)
return 0;
}
+static int
+_check_for_datetime(char *type, int len)
+{
+ if (len < 1) return 0;
+ if (type[1] == '8' && (type[0] == 'M' || type[0] == 'm'))
+ return 1;
+ if (len < 10) return 0;
+ if (strncmp(type, "datetime64", 10) == 0) return 1;
+ if (len < 11) return 0;
+ if (strncmp(type, "timedelta64", 11) == 0) return 1;
+ return 0;
+}
+
+
+
#undef _chk_byteorder
static PyArray_Descr *
@@ -175,6 +190,12 @@ _convert_from_tuple(PyObject *obj)
type->elsize = itemsize;
}
}
+ else if PyDict_Check(val) { /* Assume it's a metadata dictionary */
+ if (PyDict_Merge(type->metadata, val, 0) == -1) {
+ Py_DECREF(type);
+ return NULL;
+ }
+ }
else {
/*
* interpret next item as shape (if it's a tuple)
@@ -229,7 +250,11 @@ _convert_from_tuple(PyObject *obj)
*
* (field-name, data-type (either a list or a string), and an optional
* shape parameter).
+ *
+ * field-name can be a string or a 2-tuple
+ * data-type can now be a list, string, or 2-tuple (string, metadata dictionary))
*/
+
static PyArray_Descr *
_convert_from_array_descr(PyObject *obj, int align)
{
@@ -443,6 +468,241 @@ _convert_from_list(PyObject *obj, int align)
return NULL;
}
+static char *_datetime_strings[] = {
+ NPY_STR_Y,
+ NPY_STR_M,
+ NPY_STR_W,
+ NPY_STR_B,
+ NPY_STR_D,
+ NPY_STR_h,
+ NPY_STR_s,
+ NPY_STR_ms,
+ NPY_STR_us,
+ NPY_STR_ns,
+ NPY_STR_ps,
+ NPY_STR_fs,
+ NPY_STR_as
+};
+
+static NPY_DATETIMEUNIT
+ _unit_from_str(char *base)
+{
+ NPY_DATETIMEUNIT unit;
+
+ if (base == NULL)
+ return NPY_DATETIME_DEFAULTUNIT;
+
+ unit = NPY_FR_Y;
+ while (unit < NPY_DATETIME_NUMUNITS) {
+ if (strcmp(base, _datetime_strings[unit]) == 0)
+ break;
+ unit++;
+ }
+
+ if (unit == NPY_DATETIME_NUMUNITS)
+ return NPY_DATETIME_DEFAULTUNIT;
+
+ return unit;
+}
+
+static int _multiples_table[16][4] = {
+ {12, 52, 365}, /* NPY_FR_Y */
+ {NPY_FR_M, NPY_FR_W, NPY_FR_D},
+ {4, 30, 720}, /* NPY_FR_M */
+ {NPY_FR_W, NPY_FR_D, NPY_FR_h},
+ {5, 7, 168, 10080}, /* NPY_FR_W */
+ {NPY_FR_B, NPY_FR_D, NPY_FR_h, NPY_FR_m},
+ {24, 1440, 86400}, /* NPY_FR_B */
+ {NPY_FR_h, NPY_FR_m, NPY_FR_s},
+ {24, 1440, 86400}, /* NPY_FR_D */
+ {NPY_FR_h, NPY_FR_m, NPY_FR_s},
+ {60, 3600}, /* NPY_FR_h */
+ {NPY_FR_m, NPY_FR_s},
+ {60, 60000}, /* NPY_FR_m */
+ {NPY_FR_s, NPY_FR_ms},
+ {1000, 1000000}, /* >=NPY_FR_s */
+ {0, 0}
+};
+
+
+/* Translate divisors into multiples of smaller units */
+static int
+_convert_divisor_to_multiple(PyArray_DatetimeMetaData *meta)
+{
+ int i, num, ind;
+ int *totry;
+ NPY_DATETIMEUNIT *baseunit;
+ int q, r;
+
+ ind = ((int)meta->base - (int)NPY_FR_Y)*2;
+ totry = _multiples_table[ind];
+ baseunit = (NPY_DATETIMEUNIT *)_multiples_table[ind+1];
+
+ num = 3;
+ if (meta->base == NPY_FR_W) num = 4;
+ else if (meta->base > NPY_FR_D) num = 2;
+
+ if (meta->base >= NPY_FR_s) {
+ ind = ((int)NPY_FR_s - (int)NPY_FR_Y)*2;
+ totry = _multiples_table[ind];
+ baseunit = (NPY_DATETIMEUNIT *)_multiples_table[ind+1];
+ baseunit[0] = meta->base - 1;
+ baseunit[1] = meta->base - 2;
+ if (meta->base == NPY_DATETIME_NUMUNITS-1) num = 1;
+ }
+
+ for (i=0; i<num; i++) {
+ q = totry[i] / meta->den;
+ r = totry[i] % meta->den;
+ if (r==0) break;
+ }
+ if (i==num) {
+ PyErr_Format(PyExc_ValueError, "divisor (%d) is not a multiple of a lower-unit", meta->den);
+ return -1;
+ }
+ meta->base = (NPY_DATETIMEUNIT) baseunit[i];
+ meta->den = 1;
+ meta->num = q;
+
+ return 0;
+}
+
+
+static PyObject *
+_get_datetime_tuple_from_cobj(PyObject *cobj)
+{
+ PyArray_DatetimeMetaData *dt_data;
+ PyObject *dt_tuple;
+
+ dt_data = PyCObject_AsVoidPtr(cobj);
+ dt_tuple = PyTuple_New(4);
+
+ PyTuple_SET_ITEM(dt_tuple, 0,
+ PyString_FromString(_datetime_strings[dt_data->base]));
+ PyTuple_SET_ITEM(dt_tuple, 1,
+ PyInt_FromLong(dt_data->num));
+ PyTuple_SET_ITEM(dt_tuple, 2,
+ PyInt_FromLong(dt_data->den));
+ PyTuple_SET_ITEM(dt_tuple, 3,
+ PyInt_FromLong(dt_data->events));
+
+ return dt_tuple;
+}
+
+static PyObject *
+_convert_datetime_tuple_to_cobj(PyObject *tuple)
+{
+ PyArray_DatetimeMetaData *dt_data;
+
+ dt_data = _pya_malloc(sizeof(PyArray_DatetimeMetaData));
+
+ dt_data->base = _unit_from_str\
+ (PyString_AsString(PyTuple_GET_ITEM(tuple, 0)));
+
+ /* Assumes other objects are Python integers */
+ dt_data->num = PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 1));
+ dt_data->den = PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 2));
+ dt_data->events = PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 3));
+
+ if (dt_data->den > 1) {
+ if (_convert_divisor_to_multiple(dt_data) < 0) return NULL;
+ }
+
+ return PyCObject_FromVoidPtr((void *)dt_data, _pya_free);
+}
+
+static PyArray_Descr *
+_convert_from_datetime_tuple(PyObject *obj)
+{
+ PyArray_Descr *new;
+ PyObject *dt_tuple;
+ PyObject *dt_cobj;
+ PyObject *datetime;
+ static PyObject *freq_key=NULL;
+
+
+ if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj)!=2) {
+ PyErr_SetString(PyExc_RuntimeError, "_datetimestring is " \
+ "not returning a tuple with length 2");
+ return NULL;
+ }
+
+ dt_tuple = PyTuple_GET_ITEM(obj, 0);
+ datetime = PyTuple_GET_ITEM(obj, 1);
+ if (!PyTuple_Check(dt_tuple) || PyTuple_GET_SIZE(dt_tuple) != 4 || \
+ !PyInt_Check(datetime)) {
+ PyErr_SetString(PyExc_RuntimeError, "_datetimestring is " \
+ "not returning a length 4 tuple and an integer");
+ return NULL;
+ }
+
+ /* Create new timedelta or datetime dtype */
+ if (PyObject_IsTrue(datetime)) {
+ new = PyArray_DescrNewFromType(PyArray_DATETIME);
+ }
+ else {
+ new = PyArray_DescrNewFromType(PyArray_TIMEDELTA);
+ }
+
+ if (new == NULL) return NULL;
+
+ /* Add correct metadata */
+ if (freq_key == NULL) {
+ freq_key = PyString_InternFromString(NPY_METADATA_DTSTR);
+ if (freq_key == NULL) return NULL;
+ }
+
+ if (new->metadata == NULL) {
+ if ((new->metadata = PyDict_New())== NULL) return NULL;
+ }
+ if (!PyDict_Check(new->metadata)) {
+ PyErr_SetString(PyExc_RuntimeError, "metadata is not a dictionary");
+ return NULL;
+ }
+
+ dt_cobj = _convert_datetime_tuple_to_cobj(dt_tuple);
+ if (dt_cobj == NULL) { /* Failure in conversion */
+ Py_DECREF(new);
+ return NULL;
+ }
+
+ /* Assume this sets a new reference to dt_cobj */
+ PyDict_SetItem(new->metadata, freq_key, dt_cobj);
+ Py_DECREF(dt_cobj);
+
+ return new;
+}
+
+
+static PyArray_Descr *
+_convert_from_datetime(PyObject *obj)
+{
+ PyObject *tupleobj;
+ PyArray_Descr *res;
+ PyObject *_numpy_internal;
+
+ if (!PyString_Check(obj)) {
+ return NULL;
+ }
+ _numpy_internal = PyImport_ImportModule("numpy.core._internal");
+ if (_numpy_internal == NULL) {
+ return NULL;
+ }
+ tupleobj = PyObject_CallMethod(_numpy_internal, "_datetimestring", "O", obj);
+ Py_DECREF(_numpy_internal);
+ if (!tupleobj) return NULL;
+ /* tuple of a standard tuple (baseunit, num, den, events) and a
+ timedelta boolean
+ */
+ res = _convert_from_datetime_tuple(tupleobj);
+ Py_DECREF(tupleobj);
+ if (!res && !PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ValueError, "invalid data-type");
+ return NULL;
+ }
+ return res;
+}
+
/*
* comma-separated string
@@ -552,6 +812,8 @@ _use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag)
* must have at least two and up to four
* keys These must all be sequences of the same length.
*
+ * can also have an additional key called "metadata" which can be any dictionary
+ *
* "names" --- field names
* "formats" --- the data-type descriptors for the field.
*
@@ -605,6 +867,7 @@ _convert_from_dict(PyObject *obj, int align)
PyArray_Descr *new;
PyObject *fields = NULL;
PyObject *names, *offsets, *descrs, *titles;
+ PyObject *metadata;
int n, i;
int totalsize;
int maxalign = 0;
@@ -745,6 +1008,19 @@ _convert_from_dict(PyObject *obj, int align)
new->names = names;
new->fields = fields;
new->hasobject = dtypeflags;
+
+ metadata = PyDict_GetItemString(obj, "metadata");
+
+ if (new->metadata == NULL) {
+ new->metadata = metadata;
+ Py_XINCREF(new->metadata);
+ }
+ else if (metadata != NULL) {
+ if (PyDict_Merge(new->metadata, metadata, 0) == -1) {
+ Py_DECREF(new);
+ return NULL;
+ }
+ }
return new;
fail:
@@ -868,6 +1144,14 @@ PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at)
if (len <= 0) {
goto fail;
}
+ /* check for datetime format */
+ if ((len > 1) && _check_for_datetime(type, len)) {
+ *at = _convert_from_datetime(obj);
+ if (*at) {
+ return PY_SUCCEED;
+ }
+ return PY_FAIL;
+ }
/* check for commas present or first (or second) element a digit */
if (_check_for_commastring(type, len)) {
*at = _convert_from_commastring(obj, 0);
@@ -1043,7 +1327,9 @@ PyArray_DescrNew(PyArray_Descr *base)
Py_INCREF(new->subarray->shape);
Py_INCREF(new->subarray->base);
}
- Py_XINCREF(new->typeobj);
+ Py_XINCREF(new->typeobj);
+ Py_XINCREF(new->metadata);
+
return new;
}
@@ -1070,6 +1356,7 @@ arraydescr_dealloc(PyArray_Descr *self)
Py_DECREF(self->subarray->base);
_pya_free(self->subarray);
}
+ Py_XDECREF(self->metadata);
self->ob_type->tp_free((PyObject *)self);
}
@@ -1110,13 +1397,55 @@ arraydescr_subdescr_get(PyArray_Descr *self)
self->subarray->shape);
}
+static PyObject *
+_append_to_datetime_typestr(PyArray_Descr *self, PyObject *ret)
+{
+ PyObject *tmp;
+ PyObject *res;
+ int num, den, events;
+ char *basestr;
+ PyArray_DatetimeMetaData *dt_data;
+
+ /* This shouldn't happen */
+ if (self->metadata == NULL) return ret;
+
+ tmp = PyDict_GetItemString(self->metadata, NPY_METADATA_DTSTR);
+ dt_data = PyCObject_AsVoidPtr(tmp);
+ num = dt_data->num;
+ den = dt_data->den;
+ events = dt_data->events;
+ basestr = _datetime_strings[dt_data->base];
+
+ if (num == 1) {
+ tmp = PyString_FromString(basestr);
+ }
+ else {
+ tmp = PyString_FromFormat("%d%s", num, basestr);
+ }
+ if (den != 1) {
+ res = PyString_FromFormat("/%d", den);
+ PyString_ConcatAndDel(&tmp, res);
+ }
+
+ res = PyString_FromString("[");
+ PyString_ConcatAndDel(&res, tmp);
+ PyString_ConcatAndDel(&res, PyString_FromString("]"));
+ if (events != 1) {
+ tmp = PyString_FromFormat("//%d", events);
+ PyString_ConcatAndDel(&res, tmp);
+ }
+ PyString_ConcatAndDel(&ret, res);
+ return ret;
+}
+
NPY_NO_EXPORT PyObject *
arraydescr_protocol_typestr_get(PyArray_Descr *self)
{
char basic_ = self->kind;
char endian = self->byteorder;
int size = self->elsize;
-
+ PyObject *ret;
+
if (endian == '=') {
endian = '<';
if (!PyArray_IsNativeByteOrder(endian)) {
@@ -1126,7 +1455,13 @@ arraydescr_protocol_typestr_get(PyArray_Descr *self)
if (self->type_num == PyArray_UNICODE) {
size >>= 2;
}
- return PyString_FromFormat("%c%c%d", endian, basic_, size);
+
+ ret = PyString_FromFormat("%c%c%d", endian, basic_, size);
+ if (PyDataType_ISDATETIME(self)) {
+ ret = _append_to_datetime_typestr(self, ret);
+ }
+ return ret;
+
}
static PyObject *
@@ -1165,6 +1500,9 @@ arraydescr_typename_get(PyArray_Descr *self)
p = PyString_FromFormat("%d", self->elsize * 8);
PyString_ConcatAndDel(&res, p);
}
+ if (PyDataType_ISDATETIME(self)) {
+ res = _append_to_datetime_typestr(self, res);
+ }
return res;
}
@@ -1301,6 +1639,16 @@ arraydescr_fields_get(PyArray_Descr *self)
}
static PyObject *
+arraydescr_metadata_get(PyArray_Descr *self)
+{
+ if (self->metadata == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ return PyDictProxy_New(self->metadata);
+}
+
+static PyObject *
arraydescr_hasobject_get(PyArray_Descr *self)
{
PyObject *res;
@@ -1407,6 +1755,9 @@ static PyGetSetDef arraydescr_getsets[] = {
{"fields",
(getter)arraydescr_fields_get,
NULL, NULL, NULL},
+ {"metadata",
+ (getter)arraydescr_metadata_get,
+ NULL, NULL, NULL},
{"names",
(getter)arraydescr_names_get,
(setter)arraydescr_names_set,
@@ -1417,21 +1768,42 @@ static PyGetSetDef arraydescr_getsets[] = {
{NULL, NULL, NULL, NULL, NULL},
};
+static int
+_invalid_metadata_check(PyObject *metadata)
+{
+ PyObject *res;
+
+ /* borrowed reference */
+ res = PyDict_GetItemString(metadata, NPY_METADATA_DTSTR);
+ if (res == NULL) return 0;
+
+ PyErr_SetString(PyExc_ValueError, "cannot set " NPY_METADATA_DTSTR \
+ " in dtype metadata");
+ return 1;
+}
+
static PyObject *
arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), PyObject *args, PyObject *kwds)
{
- PyObject *odescr;
+ PyObject *odescr, *ometadata=NULL;
PyArray_Descr *descr, *conv;
Bool align = FALSE;
Bool copy = FALSE;
- static char *kwlist[] = {"dtype", "align", "copy", NULL};
+ Bool copied = FALSE;
+ static char *kwlist[] = {"dtype", "align", "copy", "metadata", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O&",
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O&O!",
kwlist, &odescr,
PyArray_BoolConverter, &align,
- PyArray_BoolConverter, &copy)) {
+ PyArray_BoolConverter, &copy,
+ &PyDict_Type, &ometadata
+ )) {
return NULL;
}
+
+ if ((ometadata != NULL) && (_invalid_metadata_check(ometadata)))
+ return NULL;
+
if (align) {
if (!PyArray_DescrAlignConverter(odescr, &conv)) {
return NULL;
@@ -1445,10 +1817,64 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), PyObject *args, PyObject *kwds
descr = PyArray_DescrNew(conv);
Py_DECREF(conv);
conv = descr;
+ copied = TRUE;
+ }
+
+ if ((ometadata != NULL)) {
+ /* We need to be sure to make a new copy of the data-type and any
+ underlying dictionary */
+ if (!copied) {
+ descr = PyArray_DescrNew(conv);
+ Py_DECREF(conv);
+ conv = descr;
+ }
+ if ((conv->metadata != NULL)) {
+ /* Make a copy of the metadata before merging with ometadata
+ so that this data-type descriptor has it's own copy
+ */
+ odescr = conv->metadata; /* Save a reference */
+ conv->metadata = PyDict_Copy(odescr);
+ Py_DECREF(odescr); /* Decrement the old reference */
+
+ /* Update conv->metadata with anything new in metadata
+ keyword, but do not over-write anything already there
+ */
+ if (PyDict_Merge(conv->metadata, ometadata, 0) != 0) {
+ Py_DECREF(conv);
+ return NULL;
+ }
+ }
+ else {
+ /* Make a copy of the input dictionary */
+ conv->metadata = PyDict_Copy(ometadata);
+ }
}
return (PyObject *)conv;
}
+/* Return a tuple of (cleaned metadata dictionary,
+ tuple with (str, num, events))
+*/
+static PyObject *
+_get_pickleabletype_from_metadata(PyObject *metadata)
+{
+ PyObject *newdict;
+ PyObject *newtup, *dt_tuple;
+ PyObject *cobj;
+
+ newdict = PyDict_Copy(metadata);
+ PyDict_DelItemString(newdict, NPY_METADATA_DTSTR);
+ newtup = PyTuple_New(2);
+ PyTuple_SET_ITEM(newtup, 0, newdict);
+
+ cobj = PyDict_GetItemString(metadata, NPY_METADATA_DTSTR);
+ dt_tuple = _get_datetime_tuple_from_cobj(cobj);
+
+ PyTuple_SET_ITEM(newtup, 1, dt_tuple);
+
+ return newtup;
+}
+
/* return a tuple of (callable object, args, state). */
static PyObject *
@@ -1459,7 +1885,7 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args))
* change the format. Be sure to handle the old versions in
* arraydescr_setstate.
*/
- const int version = 3;
+ const int version = 4;
PyObject *ret, *mod, *obj;
PyObject *state;
char endian;
@@ -1507,7 +1933,7 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args))
endian = '>';
}
}
- state = PyTuple_New(8);
+ state = PyTuple_New(9);
PyTuple_SET_ITEM(state, 0, PyInt_FromLong(version));
PyTuple_SET_ITEM(state, 1, PyString_FromFormat("%c", endian));
PyTuple_SET_ITEM(state, 2, arraydescr_subdescr_get(self));
@@ -1536,6 +1962,25 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args))
PyTuple_SET_ITEM(state, 5, PyInt_FromLong(elsize));
PyTuple_SET_ITEM(state, 6, PyInt_FromLong(alignment));
PyTuple_SET_ITEM(state, 7, PyInt_FromLong(self->hasobject));
+ if (self->metadata) {
+ if (PyDataType_ISDATETIME(self)) {
+ PyObject *newobj;
+ /* Handle CObject in NPY_METADATA_DTSTR key separately */
+ /* newobj is a tuple of cleaned metadata dictionary
+ and tuple of date_time info (str, num, den, events) */
+ newobj = _get_pickleabletype_from_metadata(self->metadata);
+ PyTuple_SET_ITEM(state, 8, newobj);
+ }
+ else {
+ Py_INCREF(self->metadata);
+ PyTuple_SET_ITEM(state, 8, self->metadata);
+ }
+ }
+ else {
+ PyTuple_SET_ITEM(state, 8, Py_None);
+ Py_INCREF(Py_None);
+ }
+
PyTuple_SET_ITEM(ret, 2, state);
return ret;
}
@@ -1581,9 +2026,9 @@ static PyObject *
arraydescr_setstate(PyArray_Descr *self, PyObject *args)
{
int elsize = -1, alignment = -1;
- int version = 3;
+ int version = 4;
char endian;
- PyObject *subarray, *fields, *names = NULL;
+ PyObject *subarray, *fields, *names = NULL, *metadata=NULL;
int incref_names = 1;
int dtypeflags = 0;
@@ -1597,6 +2042,13 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args)
return NULL;
}
switch (PyTuple_GET_SIZE(PyTuple_GET_ITEM(args,0))) {
+ case 9:
+ if (!PyArg_ParseTuple(args, "(icOOOiiiO)", &version, &endian,
+ &subarray, &names, &fields, &elsize,
+ &alignment, &dtypeflags, &metadata)) {
+ return NULL;
+ }
+ break;
case 8:
if (!PyArg_ParseTuple(args, "(icOOOiii)", &version, &endian,
&subarray, &names, &fields, &elsize,
@@ -1635,7 +2087,7 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args)
* If we ever need another pickle format, increment the version
* number. But we should still be able to handle the old versions.
*/
- if (version < 0 || version > 3) {
+ if (version < 0 || version > 4) {
PyErr_Format(PyExc_ValueError,
"can't handle version %d of numpy.dtype pickle",
version);
@@ -1707,6 +2159,25 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args)
if (version < 3) {
self->hasobject = _descr_find_object(self);
}
+
+ Py_XDECREF(self->metadata);
+ if (PyDataType_ISDATETIME(self) && (metadata != Py_None) && (metadata != NULL)) {
+ PyObject *cobj;
+ self->metadata = PyTuple_GET_ITEM(metadata, 0);
+ Py_INCREF(self->metadata);
+ cobj = _convert_datetime_tuple_to_cobj(PyTuple_GET_ITEM(metadata, 1));
+ PyDict_SetItemString(self->metadata, NPY_METADATA_DTSTR, cobj);
+ Py_DECREF(cobj);
+ }
+ else {
+ /* We have a borrowed reference to metadata so no need
+ to alter reference count
+ */
+ if (metadata == Py_None) metadata = NULL;
+ self->metadata = metadata;
+ Py_XINCREF(metadata);
+ }
+
Py_INCREF(Py_None);
return Py_None;
}
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index c45952910..a793e0364 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -1957,6 +1957,36 @@ array_set_ops_function(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args), P
return oldops;
}
+static PyObject *
+array_set_datetimeparse_function(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
+{
+ PyObject *op = NULL;
+ static char *kwlist[] = {"f", NULL};
+ PyObject *_numpy_internal;
+
+ if(!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist,
+ &op)) {
+ return NULL;
+ }
+ /* reset the array_repr function to built-in */
+ if (op == Py_None) {
+ _numpy_internal = PyImport_ImportModule("numpy.core._internal");
+ if (_numpy_internal == NULL) return NULL;
+ op = PyObject_GetAttrString(_numpy_internal, "datetime_from_string");
+ }
+ else { /* Must balance reference count increment in both branches */
+ if (!PyCallable_Check(op)) {
+ PyErr_SetString(PyExc_TypeError, "Argument must be callable.");
+ return NULL;
+ }
+ Py_INCREF(op);
+ }
+ PyArray_SetDatetimeParseFunction(op);
+ Py_DECREF(op);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
/*NUMPY_API
* Where
@@ -2367,10 +2397,12 @@ static struct PyMethodDef array_module_methods[] = {
{"set_numeric_ops",
(PyCFunction)array_set_ops_function,
METH_VARARGS|METH_KEYWORDS, NULL},
+ {"set_datetimeparse_function",
+ (PyCFunction)array_set_datetimeparse_function,
+ METH_VARARGS|METH_KEYWORDS, NULL},
{"set_typeDict",
(PyCFunction)array_set_typeDict,
METH_VARARGS, NULL},
-
{"array",
(PyCFunction)_array_fromobject,
METH_VARARGS|METH_KEYWORDS, NULL},
@@ -2554,6 +2586,10 @@ setup_scalartypes(PyObject *NPY_UNUSED(dict))
SINGLE_INHERIT(LongLong, SignedInteger);
#endif
+ SINGLE_INHERIT(TimeInteger, SignedInteger);
+ SINGLE_INHERIT(Datetime, TimeInteger);
+ SINGLE_INHERIT(Timedelta, TimeInteger);
+
/*
fprintf(stderr,
"tp_free = %p, PyObject_Del = %p, int_tp_free = %p, base.tp_free = %p\n",
@@ -2692,16 +2728,23 @@ PyMODINIT_FUNC initmultiarray(void) {
if (PyErr_Occurred()) {
goto err;
}
+
/*
* PyExc_Exception should catch all the standard errors that are
- * now raised instead of the string exception "multiarray.error".
+ * now raised instead of the string exception "multiarray.error"
+
* This is for backward compatibility with existing code.
*/
PyDict_SetItemString (d, "error", PyExc_Exception);
- s = PyString_FromString("3.0");
+
+ s = PyString_FromString("3.1");
PyDict_SetItemString(d, "__version__", s);
Py_DECREF(s);
+ s = PyString_InternFromString(NPY_METADATA_DTSTR);
+ PyDict_SetItemString(d, "METADATA_DTSTR", s);
+ Py_DECREF(s);
+
#define ADDCONST(NAME) \
s = PyInt_FromLong(NPY_##NAME); \
PyDict_SetItemString(d, #NAME, s); \
diff --git a/numpy/core/src/multiarray/parse_datetime.c b/numpy/core/src/multiarray/parse_datetime.c
new file mode 100644
index 000000000..76ef9dcb3
--- /dev/null
+++ b/numpy/core/src/multiarray/parse_datetime.c
@@ -0,0 +1,862 @@
+
+#include <datetime.h>
+#include <time.h>
+
+/* For defaults and errors */
+#define NPY_FR_ERR -1
+
+/* Offset for number of days between Dec 31, 1969 and Jan 1, 0001
+* Assuming Gregorian calendar was always in effect (proleptic Gregorian calendar)
+*/
+
+/* Calendar Structure for Parsing Long -> Date */
+typedef struct {
+ int year, month, day;
+} ymdstruct;
+
+typedef struct {
+ int hour, min, sec;
+} hmsstruct;
+
+
+/*
+ ====================================================
+ == Beginning of section borrowed from mx.DateTime ==
+ ====================================================
+*/
+
+/*
+ Functions in the following section are borrowed from mx.DateTime version
+ 2.0.6, and hence this code is subject to the terms of the egenix public
+ license version 1.0.0
+*/
+
+#define Py_AssertWithArg(x,errortype,errorstr,a1) {if (!(x)) {PyErr_Format(errortype,errorstr,a1);goto onError;}}
+
+/* Table with day offsets for each month (0-based, without and with leap) */
+static int month_offset[2][13] = {
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+/* Table of number of days in a month (0-based, without and with leap) */
+static int days_in_month[2][12] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+/* Return 1/0 iff year points to a leap year in calendar. */
+static int
+is_leapyear(register long year)
+{
+ return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
+}
+
+
+/* Return the day of the week for the given absolute date. */
+/* Monday is 0 and Sunday is 6 */
+static int
+day_of_week(npy_longlong absdate)
+{
+ /* Add in four for the Thursday on Jan 1, 1970 (epoch offset)*/
+ absdate += 4;
+
+ if (absdate >= 0) {
+ return absdate % 7;
+ }
+ else {
+ return 6 + (absdate + 1) % 7;
+ }
+}
+
+/* Return the year offset, that is the absolute date of the day
+ 31.12.(year-1) since 31.12.1969 in the proleptic Gregorian calendar.
+*/
+static npy_longlong
+year_offset(register npy_longlong year)
+{
+ /* Note that 477 == 1969/4 - 1969/100 + 1969/400 */
+ year--;
+ if (year >= 0 || -1/4 == -1)
+ return (year-1969)*365 + year/4 - year/100 + year/400 - 477;
+ else
+ return (year-1969)*365 + (year-3)/4 - (year-99)/100 + (year-399)/400 - 477;
+}
+
+/* Modified version of mxDateTime function
+ * Returns absolute number of days since Jan 1, 1970
+ * assuming a proleptic Gregorian Calendar
+ * Raises a ValueError if out of range month or day
+ * day -1 is Dec 31, 1969, day 0 is Jan 1, 1970, day 1 is Jan 2, 1970
+ */
+static npy_longlong
+days_from_ymd(int year, int month, int day)
+{
+
+ /* Calculate the absolute date */
+ int leap;
+ npy_longlong yearoffset, absdate;
+
+ /* Is it a leap year ? */
+ leap = is_leapyear(year);
+
+ /* Negative month values indicate months relative to the years end */
+ if (month < 0) month += 13;
+ Py_AssertWithArg(month >= 1 && month <= 12,
+ PyExc_ValueError,
+ "month out of range (1-12): %i",
+ month);
+
+ /* Negative values indicate days relative to the months end */
+ if (day < 0) day += days_in_month[leap][month - 1] + 1;
+ Py_AssertWithArg(day >= 1 && day <= days_in_month[leap][month - 1],
+ PyExc_ValueError,
+ "day out of range: %i",
+ day);
+
+ /* Number of days between Dec 31, (year - 1) and Dec 31, 1969
+ * (can be negative).
+ */
+ yearoffset = year_offset(year);
+
+ if (PyErr_Occurred()) goto onError;
+
+ /* Calculate the number of days using yearoffset */
+ /* Jan 1, 1970 is day 0 and thus Dec. 31, 1969 is day -1 */
+ absdate = day-1 + month_offset[leap][month - 1] + yearoffset;
+
+ return absdate;
+
+ onError:
+ return 0;
+
+}
+
+/* Returns absolute seconds from an hour, minute, and second
+ */
+#define secs_from_hms(hour, min, sec) ((hour)*3600 + (min)*60 + (sec))
+
+/* Takes a number of days since Jan 1, 1970 (positive or negative)
+ * and returns the year. month, and day in the proleptic
+ * Gregorian calendar
+ *
+ * Examples:
+ *
+ * -1 returns 1969, 12, 31
+ * 0 returns 1970, 1, 1
+ * 1 returns 1970, 1, 2
+ */
+
+static ymdstruct
+days_to_ymdstruct(npy_datetime dlong)
+{
+ ymdstruct ymd;
+ register long year;
+ npy_longlong yearoffset;
+ int leap, dayoffset;
+ int month = 1, day = 1;
+ int *monthoffset;
+
+ dlong += 1;
+
+ /* Approximate year */
+ year = 1970 + dlong / 365.2425;
+
+ /* Apply corrections to reach the correct year */
+ while (1) {
+ /* Calculate the year offset */
+ yearoffset = year_offset(year);
+
+ /* Backward correction: absdate must be greater than the
+ yearoffset */
+ if (yearoffset >= dlong) {
+ year--;
+ continue;
+ }
+
+ dayoffset = dlong - yearoffset;
+ leap = is_leapyear(year);
+
+ /* Forward correction: non leap years only have 365 days */
+ if (dayoffset > 365 && !leap) {
+ year++;
+ continue;
+ }
+ break;
+ }
+
+ /* Now iterate to find the month */
+ monthoffset = month_offset[leap];
+ for (month = 1; month < 13; month++) {
+ if (monthoffset[month] >= dayoffset)
+ break;
+ }
+ day = dayoffset - month_offset[leap][month-1];
+
+ ymd.year = year;
+ ymd.month = month;
+ ymd.day = day;
+
+ return ymd;
+}
+
+/* Converts an integer number of seconds in a day to hours minutes seconds.
+ It assumes seconds is between 0 and 86399.
+ */
+
+static hmsstruct
+seconds_to_hmsstruct(npy_longlong dlong)
+{
+ int hour, minute, second;
+ hmsstruct hms;
+
+ hour = dlong / 3600;
+ minute = (dlong % 3600) / 60;
+ second = dlong - (hour*3600 + minute*60);
+
+ hms.hour = hour;
+ hms.min = minute;
+ hms.sec = second;
+
+ return hms;
+}
+
+/*
+ ====================================================
+ == End of section adapted from mx.DateTime ==
+ ====================================================
+*/
+
+
+/*==================================================
+// Parsing DateTime struct and returns a date-time number
+// =================================================
+
+ Structure is assumed to be already normalized
+*/
+
+/*NUMPY_API
+ * Create a datetime value from a filled datetime struct and resolution unit.
+ */
+NPY_NO_EXPORT npy_datetime
+PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d)
+{
+ npy_datetime ret;
+ npy_longlong days; /* The absolute number of days since Jan 1, 1970 */
+
+ if (fr > NPY_FR_M) days = days_from_ymd(d->year, d->month, d->day);
+
+ if (fr == NPY_FR_Y) {
+ ret = d->year - 1970;
+ }
+ else if (fr == NPY_FR_M) {
+ ret = (d->year - 1970) * 12 + d->month - 1;
+ }
+ else if (fr == NPY_FR_W) { /* This is just 7-days for now. */
+ if (days >= 0)
+ ret = days / 7;
+ else
+ ret = (days - 6) / 7;
+ }
+ else if (fr == NPY_FR_B) {
+ npy_longlong x;
+ int dotw = day_of_week(days);
+ if (dotw > 4) ret = 0; /* Invalid business day */
+ else {
+ if (days >= 0) { /* offset to adjust first week */
+ x = days - 4;
+ }
+ else {
+ x = days - 2;
+ }
+ ret = 2 + (x / 7) * 5 + x % 7;
+ }
+ }
+ else if (fr == NPY_FR_D) {
+ ret = days;
+ }
+ else if (fr == NPY_FR_h) {
+ ret = days * 24 + d->hour;
+ }
+ else if (fr == NPY_FR_m) {
+ ret = days * 1440 + d->hour * 60 + d->min;
+ }
+ else if (fr == NPY_FR_s) {
+ ret = days * 86400 + secs_from_hms(d->hour, d->min, d->sec);
+ }
+ else if (fr == NPY_FR_ms) {
+ ret = days * 86400000 + secs_from_hms(d->hour, d->min, d->sec) * 1000
+ + (d->us / 1000);
+ }
+ else if (fr == NPY_FR_us) {
+ npy_int64 num = 86400 * 1000;
+ num *= 1000;
+ ret = days * num + secs_from_hms(d->hour, d->min, d->sec) * 1000000
+ + d->us;
+ }
+ else if (fr == NPY_FR_ns) {
+ npy_int64 num = 86400 * 1000;
+ num *= 1000 * 1000;
+ ret = days * num + secs_from_hms(d->hour, d->min, d->sec) * 1000000000
+ + d->us * 1000 + (d->ps / 1000);
+ }
+ else if (fr == NPY_FR_ps) {
+ npy_int64 num2 = 1000 * 1000;
+ npy_int64 num1;
+
+ num2 *= 1000 * 1000;
+ num1 = 86400 * num2;
+ ret = days * num1 + secs_from_hms(d->hour, d->min, d->sec) * num2
+ + d->us * 1000000 + d->ps;
+ }
+ else if (fr == NPY_FR_fs) { /* only 2.6 hours */
+ npy_int64 num2 = 1000000;
+ num2 *= 1000000;
+ num2 *= 1000;
+
+ /* get number of seconds as a postive or negative number */
+ if (days >= 0) {
+ ret = secs_from_hms(d->hour, d->min, d->sec);
+ }
+ else {
+ ret = ((d->hour - 24)*3600 + d->min*60 + d->sec);
+ }
+ ret = ret * num2 + d->us * 1000000000 + d->ps * 1000 + (d->as / 1000);
+ }
+ else if (fr == NPY_FR_as) { /* only 9.2 secs */
+ npy_int64 num1, num2;
+
+ num1 = 1000000;
+ num1 *= 1000000;
+ num2 = num1 * 1000000;
+
+ if (days >= 0) {
+ ret = d->sec;
+ }
+ else {
+ ret = d->sec - 60;
+ }
+ ret = ret * num2 + d->us * num1 + d->ps * 1000000 + d->as;
+ }
+ else { /* Shouldn't get here */
+ PyErr_SetString(PyExc_ValueError, "invalid internal frequency");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* Uses Average values when frequency is Y, M, or B */
+
+#define _DAYS_PER_MONTH 30.436875
+#define _DAYS_PER_YEAR 365.2425
+
+/*NUMPY_API
+ * Create a timdelta value from a filled timedelta struct and resolution unit.
+ */
+NPY_NO_EXPORT npy_datetime
+PyArray_TimedeltaStructToTimedelta(NPY_DATETIMEUNIT fr, npy_timedeltastruct *d)
+{
+ npy_datetime ret;
+
+ if (fr == NPY_FR_Y) {
+ ret = d->day / _DAYS_PER_YEAR;
+ }
+ else if (fr == NPY_FR_M) {
+ ret = d->day / _DAYS_PER_MONTH;
+ }
+ else if (fr == NPY_FR_W) { /* This is just 7-days for now. */
+ if (d->day >= 0) {
+ ret = d->day / 7;
+ }
+ else {
+ ret = (d->day - 6) / 7;
+ }
+ }
+ else if (fr == NPY_FR_B) {
+ /* What is the meaning of a relative Business day?
+
+ This assumes you want to take the day difference and
+ convert it to business-day difference (removing 2 every 7).
+ */
+ ret = (d->day / 7) * 5 + d->day % 7;
+ }
+ else if (fr == NPY_FR_D) {
+ ret = d->day;
+ }
+ else if (fr == NPY_FR_h) {
+ ret = d->day + d->sec / 3600;
+ }
+ else if (fr == NPY_FR_m) {
+ ret = d->day * 1440 + d->sec / 60;
+ }
+ else if (fr == NPY_FR_s) {
+ ret = d->day * 86400 + d->sec;
+ }
+ else if (fr == NPY_FR_ms) {
+ ret = d->day * 86400000 + d->sec * 1000 + d->us / 1000;
+ }
+ else if (fr == NPY_FR_us) {
+ npy_int64 num = 86400000;
+ num *= 1000;
+ ret = d->day * num + d->sec * 1000000 + d->us;
+ }
+ else if (fr == NPY_FR_ns) {
+ npy_int64 num = 86400000;
+ num *= 1000000;
+ ret = d->day * num + d->sec * 1000000000 + d->us * 1000 + (d->ps / 1000);
+ }
+ else if (fr == NPY_FR_ps) {
+ npy_int64 num2, num1;
+
+ num2 = 1000000;
+ num2 *= 1000000;
+ num1 = 86400 * num2;
+
+ ret = d->day * num1 + d->sec * num2 + d->us * 1000000 + d->ps;
+ }
+ else if (fr == NPY_FR_fs) { /* only 2.6 hours */
+ npy_int64 num2 = 1000000000;
+ num2 *= 1000000;
+ ret = d->sec * num2 + d->us * 1000000000 + d->ps * 1000 + (d->as / 1000);
+ }
+ else if (fr == NPY_FR_as) { /* only 9.2 secs */
+ npy_int64 num1, num2;
+
+ num1 = 1000000;
+ num1 *= 1000000;
+ num2 = num1 * 1000000;
+ ret = d->sec * num2 + d->us * num1 + d->ps * 1000000 + d->as;
+ }
+ else { /* Shouldn't get here */
+ PyErr_SetString(PyExc_ValueError, "invalid internal frequency");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+
+/*NUMPY_API
+ * Fill the datetime struct from the value and resolution unit.
+ */
+NPY_NO_EXPORT void
+PyArray_DatetimeToDatetimeStruct(npy_datetime val, NPY_DATETIMEUNIT fr,
+ npy_datetimestruct *result)
+{
+ int year = 1970, month = 1, day = 1,
+ hour = 0, min = 0, sec = 0,
+ us = 0, ps = 0, as = 0;
+
+ npy_int64 tmp;
+ ymdstruct ymd;
+ hmsstruct hms;
+
+ /* Note that what looks like val / N and val % N for positive numbers maps to
+ [val - (N-1)] / N and [N-1 + (val+1) % N] for negative numbers (with the 2nd
+ value, the remainder, being positive in both cases).
+ */
+
+ if (fr == NPY_FR_Y) {
+ year = 1970 + val;
+ }
+ else if (fr == NPY_FR_M) {
+ if (val >= 0) {
+ year = 1970 + val / 12;
+ month = val % 12 + 1;
+ }
+ else {
+ year = 1969 + (val + 1) / 12;
+ month = 12 + (val + 1)% 12;
+ }
+ }
+ else if (fr == NPY_FR_W) {
+ /* A week is the same as 7 days */
+ ymd = days_to_ymdstruct(val * 7);
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ }
+ else if (fr == NPY_FR_B) { /* Number of business days since Thursday, 1-1-70 */
+ npy_longlong absdays;
+ /* A buisness day is M T W Th F (i.e. all but Sat and Sun.) */
+ /* Convert the business day to the number of actual days.
+
+ Must convert [0,1,2,3,4,5,6,7,...] to
+ [0,1,4,5,6,7,8,11,...]
+ and [...,-9,-8,-7,-6,-5,-4,-3,-2,-1,0] to
+ [...,-13,-10,-9,-8,-7,-6,-3,-2,-1,0]
+ */
+ if (val >= 0) {
+ absdays = 7 * ((val + 3) / 5) + ((val + 3) % 5) - 3;
+ }
+ else { /* Recall how C computes / and % with negative numbers */
+ absdays = 7 * ((val - 1) / 5) + ((val - 1) % 5) + 1;
+ }
+ ymd = days_to_ymdstruct(absdays);
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ }
+ else if (fr == NPY_FR_D) {
+ ymd = days_to_ymdstruct(val);
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ }
+ else if (fr == NPY_FR_h) {
+ if (val >= 0) {
+ ymd = days_to_ymdstruct(val / 24);
+ hour = val % 24;
+ }
+ else {
+ ymd = days_to_ymdstruct((val - 23) / 24);
+ hour = 23 + (val + 1) % 24;
+ }
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ }
+ else if (fr == NPY_FR_m) {
+ if (val >= 0) {
+ ymd = days_to_ymdstruct(val / 1440);
+ min = val % 1440;
+ }
+ else {
+ ymd = days_to_ymdstruct((val - 1439) / 1440);
+ min = 1439 + (val + 1) % 1440;
+ }
+ hms = seconds_to_hmsstruct(min * 60);
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ hour = hms.hour;
+ min = hms.min;
+ }
+ else if (fr == NPY_FR_s) {
+ if (val >= 0) {
+ ymd = days_to_ymdstruct(val / 86400);
+ sec = val % 86400;
+ }
+ else {
+ ymd = days_to_ymdstruct((val - 86399) / 86400);
+ sec = 86399 + (val + 1) % 86400;
+ }
+ hms = seconds_to_hmsstruct(val);
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ hour = hms.hour;
+ min = hms.min;
+ sec = hms.sec;
+ }
+ else if (fr == NPY_FR_ms) {
+ if (val >= 0) {
+ ymd = days_to_ymdstruct(val / 86400000);
+ tmp = val % 86400000;
+ }
+ else {
+ ymd = days_to_ymdstruct((val - 86399999) / 86400000);
+ tmp = 86399999 + (val + 1) % 86399999;
+ }
+ hms = seconds_to_hmsstruct(tmp / 1000);
+ us = (tmp % 1000)*1000;
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ hour = hms.hour;
+ min = hms.min;
+ sec = hms.sec;
+ }
+ else if (fr == NPY_FR_us) {
+ npy_int64 num1, num2;
+ num1 = 86400000;
+ num1 *= 1000;
+ num2 = num1 - 1;
+ if (val >= 0) {
+ ymd = days_to_ymdstruct(val / num1);
+ tmp = val % num1;
+ }
+ else {
+ ymd = days_to_ymdstruct((val - num2)/ num1);
+ tmp = num2 + (val + 1) % num1;
+ }
+ hms = seconds_to_hmsstruct(tmp / 1000000);
+ us = tmp % 1000000;
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ hour = hms.hour;
+ min = hms.min;
+ sec = hms.sec;
+ }
+ else if (fr == NPY_FR_ns) {
+ npy_int64 num1, num2, num3;
+ num1 = 86400000;
+ num1 *= 1000000000;
+ num2 = num1 - 1;
+ num3 = 1000000;
+ num3 *= 1000000;
+ if (val >= 0) {
+ ymd = days_to_ymdstruct(val / num1);
+ tmp = val % num1;
+ }
+ else {
+ ymd = days_to_ymdstruct((val - num2)/ num1);
+ tmp = num2 + (val + 1) % num1;
+ }
+ hms = seconds_to_hmsstruct(tmp / 1000000000);
+ tmp = tmp % 1000000000;
+ us = tmp / 1000;
+ ps = (tmp % 1000) * 1000;
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ hour = hms.hour;
+ min = hms.min;
+ sec = hms.sec;
+ }
+ else if (fr == NPY_FR_ps) {
+ npy_int64 num1, num2, num3;
+ num3 = 1000000000;
+ num3 *= 1000;
+ num1 = 86400 * num3;
+ num2 = num1 - 1;
+
+ if (val >= 0) {
+ ymd = days_to_ymdstruct(val / num1);
+ tmp = val % num1;
+ }
+ else {
+ ymd = days_to_ymdstruct((val - num2)/ num1);
+ tmp = num2 + (val + 1) % num1;
+ }
+ hms = seconds_to_hmsstruct(tmp / num3);
+ tmp = tmp % num3;
+ us = tmp / 1000000;
+ ps = tmp % 1000000;
+ year = ymd.year;
+ month = ymd.month;
+ day = ymd.day;
+ hour = hms.hour;
+ min = hms.min;
+ sec = hms.sec;
+ }
+ else if (fr == NPY_FR_fs) { /* entire range is only += 2.6 hours */
+ npy_int64 num1, num2;
+ num1 = 1000000000;
+ num1 *= 1000;
+ num2 = num1 * 1000;
+
+ if (val >= 0) {
+ sec = val / num2;
+ tmp = val % num2;
+ hms = seconds_to_hmsstruct(sec);
+ hour = hms.hour;
+ min = hms.min;
+ sec = hms.sec;
+ }
+ else { /* tmp (number of fs) will be positive after this segment */
+ year = 1969;
+ day = 31;
+ month = 12;
+ sec = (val - (num2-1))/num2;
+ tmp = (num2-1) + (val + 1) % num2;
+ if (sec == 0) { /* we are at the last second */
+ hour = 23;
+ min = 59;
+ sec = 59;
+ }
+ else {
+ hour = 24 + (sec - 3599)/3600;
+ sec = 3599 + (sec+1)%3600;
+ min = sec / 60;
+ sec = sec % 60;
+ }
+ }
+ us = tmp / 1000000000;
+ tmp = tmp % 1000000000;
+ ps = tmp / 1000;
+ as = (tmp % 1000) * 1000;
+ }
+ else if (fr == NPY_FR_as) { /* entire range is only += 9.2 seconds */
+ npy_int64 num1, num2, num3;
+ num1 = 1000000;
+ num2 = num1 * 1000000;
+ num3 = num2 * 1000000;
+ if (val >= 0) {
+ hour = 0;
+ min = 0;
+ sec = val / num3;
+ tmp = val % num3;
+ }
+ else {
+ year = 1969;
+ day = 31;
+ month = 12;
+ hour = 23;
+ min = 59;
+ sec = 60 + (val - (num3-1)) / num3;
+ tmp = (num3-1) + (val+1) % num3;
+ }
+ us = tmp / num2;
+ tmp = tmp % num2;
+ ps = tmp / num1;
+ as = tmp % num1;
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError, "invalid internal time resolution");
+ }
+
+ result->year = year;
+ result->month = month;
+ result->day = day;
+ result->hour = hour;
+ result->min = min;
+ result->sec = sec;
+ result->us = us;
+ result->ps = ps;
+ result->as = as;
+
+ return;
+}
+
+/* FIXME: Overflow is not handled at all */
+/* To convert from Years, Months, and Business Days, multiplication by the average is done
+ */
+
+/*NUMPY_API
+ * Fill the timedelta struct from the timedelta value and resolution unit.
+ */
+NPY_NO_EXPORT void
+PyArray_TimedeltaToTimedeltaStruct(npy_timedelta val, NPY_DATETIMEUNIT fr,
+ npy_timedeltastruct *result)
+{
+ npy_longlong day=0;
+ int sec=0, us=0, ps=0, as=0;
+ npy_bool negative=0;
+
+ /* Note that what looks like val / N and val % N for positive numbers maps to
+ [val - (N-1)] / N and [N-1 + (val+1) % N] for negative numbers (with the 2nd
+ value, the remainder, being positive in both cases).
+ */
+
+ if (val < 0) {
+ val = -val;
+ negative = 1;
+ }
+ if (fr == NPY_FR_Y) {
+ day = val * _DAYS_PER_YEAR;
+ }
+ else if (fr == NPY_FR_M) {
+ day = val * _DAYS_PER_MONTH;
+ }
+ else if (fr == NPY_FR_W) {
+ day = val * 7;
+ }
+ else if (fr == NPY_FR_B) { /* Number of business days since Thursday, 1-1-70 */
+ day = (val * 7) / 5;
+ }
+ else if (fr == NPY_FR_D) {
+ day = val;
+ }
+ else if (fr == NPY_FR_h) {
+ day = val / 24;
+ sec = (val % 24)*3600;
+ }
+ else if (fr == NPY_FR_m) {
+ day = val / 1440;
+ sec = (val % 1440)*60;
+ }
+ else if (fr == NPY_FR_s) {
+ day = val / 86400;
+ sec = val % 86400;
+ }
+ else if (fr == NPY_FR_ms) {
+ day = val / 86400000;
+ val = val % 86400000;
+ sec = val / 1000;
+ us = (val % 1000)*1000;
+ }
+ else if (fr == NPY_FR_us) {
+ npy_int64 num1;
+ num1 = 86400000;
+ num1 *= 1000;
+ day = val / num1;
+ us = val % num1;
+ sec = us / 1000000;
+ us = us % 1000000;
+ }
+ else if (fr == NPY_FR_ns) {
+ npy_int64 num1;
+ num1 = 86400000;
+ num1 *= 1000000;
+ day = val / num1;
+ val = val % num1;
+ sec = val / 1000000000;
+ val = val % 1000000000;
+ us = val / 1000;
+ ps = (val % 1000) * 1000;
+ }
+ else if (fr == NPY_FR_ps) {
+ npy_int64 num1, num2;
+ num2 = 1000000000;
+ num2 *= 1000;
+ num1 = 86400 * num2;
+
+ day = val / num1;
+ ps = val % num1;
+ sec = ps / num2;
+ ps = ps % num2;
+ us = ps / 1000000;
+ ps = ps % 1000000;
+ }
+ else if (fr == NPY_FR_fs) { /* entire range is only += 9.2 hours */
+ npy_int64 num1, num2;
+ num1 = 1000000000;
+ num2 = num1 * 1000000;
+
+ day = 0;
+ sec = val / num2;
+ val = val % num2;
+ us = val / num1;
+ val = val % num1;
+ ps = val / 1000;
+ as = (val % 1000) * 1000;
+ }
+ else if (fr == NPY_FR_as) { /* entire range is only += 2.6 seconds */
+ npy_int64 num1, num2, num3;
+ num1 = 1000000;
+ num2 = num1 * 1000000;
+ num3 = num2 * 1000000;
+ day = 0;
+ sec = val / num3;
+ as = val % num3;
+ us = as / num2;
+ as = as % num2;
+ ps = as / num1;
+ as = as % num1;
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError, "invalid internal time resolution");
+ }
+
+ if (negative) {
+ result->day = -day;
+ result->sec = -sec;
+ result->us = -us;
+ result->ps = -ps;
+ result->as = -as;
+ }
+ else {
+ result->day = day;
+ result->sec = sec;
+ result->us = us;
+ result->ps = ps;
+ result->as = as;
+ }
+ return;
+
+}
+
diff --git a/numpy/core/src/multiarray/scalarapi.c b/numpy/core/src/multiarray/scalarapi.c
index 035e0863f..b66d29ae7 100644
--- a/numpy/core/src/multiarray/scalarapi.c
+++ b/numpy/core/src/multiarray/scalarapi.c
@@ -11,7 +11,7 @@
#include "config.h"
-#include "common.h"
+#include "ctors.h"
#include "descriptor.h"
#include "scalartypes.h"
@@ -60,6 +60,8 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr)
CASE(CDOUBLE, CDouble);
CASE(CLONGDOUBLE, CLongDouble);
CASE(OBJECT, Object);
+ CASE(DATETIME, Datetime);
+ CASE(TIMEDELTA, Timedelta);
#undef CASE
case NPY_STRING:
return (void *)PyString_AS_STRING(scalar);
@@ -87,6 +89,8 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr)
_IFCASE(Int);
_IFCASE(Long);
_IFCASE(LongLong);
+ _IFCASE(Datetime);
+ _IFCASE(Timedelta);
}
else {
/* Unsigned Integer */
@@ -604,6 +608,16 @@ PyArray_Scalar(void *data, PyArray_Descr *descr, PyObject *base)
if (obj == NULL) {
return NULL;
}
+ if PyTypeNum_ISDATETIME(type_num) {
+ /* We need to copy the resolution information over to the scalar */
+ /* Get the void * from the metadata dictionary */
+ PyObject *cobj;
+ PyArray_DatetimeMetaData *dt_data;
+ cobj = PyDict_GetItemString(descr->metadata, NPY_METADATA_DTSTR);
+ dt_data = PyCObject_AsVoidPtr(cobj);
+ memcpy(&(((PyDatetimeScalarObject *)obj)->obmeta), dt_data,
+ sizeof(PyArray_DatetimeMetaData));
+ }
if PyTypeNum_ISFLEXIBLE(type_num) {
if (type_num == PyArray_STRING) {
destptr = PyString_AS_STRING(obj);
diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src
index aba376be5..e50106866 100644
--- a/numpy/core/src/multiarray/scalartypes.c.src
+++ b/numpy/core/src/multiarray/scalartypes.c.src
@@ -32,9 +32,9 @@ NPY_NO_EXPORT PyBoolScalarObject _PyArrayScalar_BoolValues[] = {
/**begin repeat
* #name = number, integer, signedinteger, unsignedinteger, inexact,
- * floating, complexfloating, flexible, character#
+ * floating, complexfloating, flexible, character, timeinteger#
* #NAME = Number, Integer, SignedInteger, UnsignedInteger, Inexact,
- * Floating, ComplexFloating, Flexible, Character#
+ * Floating, ComplexFloating, Flexible, Character, TimeInteger#
*/
NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = {
#if defined(NPY_PY3K)
@@ -1908,12 +1908,12 @@ object_arrtype_dealloc(PyObject *v)
/**begin repeat
* #name = byte, short, int, long, longlong, ubyte, ushort, uint, ulong,
* ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble,
- * string, unicode, object#
+ * string, unicode, object, datetime, timedelta#
* #TYPE = BYTE, SHORT, INT, LONG, LONGLONG, UBYTE, USHORT, UINT, ULONG,
* ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE,
- * STRING, UNICODE, OBJECT#
- * #work = 0,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,z,z,0#
- * #default = 0*16,1*2,2#
+ * STRING, UNICODE, OBJECT, DATETIME, TIMEDELTA#
+ * #work = 0,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,z,z,0,0,0#
+ * #default = 0*16,1*2,2,0*2#
*/
#define _NPY_UNUSED2_1
@@ -2312,7 +2312,50 @@ ulonglong_arrtype_hash(PyObject *obj)
}
return x;
}
+
+#endif
+
+
+/**begin repeat
+ * #lname=datetime, timedelta#
+ * #name=Datetime,Timedelta#
+ */
+#if SIZEOF_LONG==SIZEOF_DATETIME
+static long
+@lname@_arrtype_hash(PyObject *obj)
+{
+ long x = (long)(((Py@name@ScalarObject *)obj)->obval);
+ if (x == -1) {
+ x = -2;
+ }
+ return x;
+}
+#elif SIZEOF_LONGLONG==SIZEOF_DATETIME
+static long
+@lname@_arrtype_hash(PyObject *obj)
+{
+ long y;
+ longlong x = (((Py@name@ScalarObject *)obj)->obval);
+
+ if ((x <= LONG_MAX)) {
+ y = (long) x;
+ }
+ else {
+ union Mask {
+ long hashvals[2];
+ longlong v;
+ } both;
+
+ both.v = x;
+ y = both.hashvals[0] + (1000003)*both.hashvals[1];
+ }
+ if (y == -1) {
+ y = -2;
+ }
+ return y;
+}
#endif
+/**end repeat**/
@@ -2729,9 +2772,10 @@ NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = {
/**begin repeat
* #NAME = Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong,
- * ULongLong, Float, Double, LongDouble#
- * #name = int*5, uint*5, float*3#
- * #CNAME = (CHAR, SHORT, INT, LONG, LONGLONG)*2, FLOAT, DOUBLE, LONGDOUBLE#
+ * ULongLong, Float, Double, LongDouble, Datetime, Timedelta#
+ * #name = int*5, uint*5, float*3, datetime, timedelta#
+ * #CNAME = (CHAR, SHORT, INT, LONG, LONGLONG)*2, FLOAT, DOUBLE, LONGDOUBLE,
+ * DATETIME, TIMEDELTA#
*/
#if BITSOF_@CNAME@ == 8
#define _THIS_SIZE "8"
@@ -2969,7 +3013,7 @@ initialize_numeric_types(void)
/**begin repeat
* #NAME= Number, Integer, SignedInteger, UnsignedInteger, Inexact,
- * Floating, ComplexFloating, Flexible, Character#
+ * Floating, ComplexFloating, Flexible, Character, TimeInteger#
*/
Py@NAME@ArrType_Type.tp_flags = BASEFLAGS;
/**end repeat**/
@@ -2977,10 +3021,10 @@ initialize_numeric_types(void)
/**begin repeat
* #name = bool, byte, short, int, long, longlong, ubyte, ushort, uint,
* ulong, ulonglong, float, double, longdouble, cfloat, cdouble,
- * clongdouble, string, unicode, void, object#
+ * clongdouble, string, unicode, void, object, datetime, timedelta#
* #NAME = Bool, Byte, Short, Int, Long, LongLong, UByte, UShort, UInt,
* ULong, ULongLong, Float, Double, LongDouble, CFloat, CDouble,
- * CLongDouble, String, Unicode, Void, Object#
+ * CLongDouble, String, Unicode, Void, Object, Datetime, Timedelta#
*/
Py@NAME@ArrType_Type.tp_flags = BASEFLAGS;
Py@NAME@ArrType_Type.tp_new = @name@_arrtype_new;
@@ -2989,9 +3033,11 @@ initialize_numeric_types(void)
/**begin repeat
* #name = bool, byte, short, ubyte, ushort, uint, ulong, ulonglong,
- * float, longdouble, cfloat, clongdouble, void, object#
+ * float, longdouble, cfloat, clongdouble, void, object, datetime,
+ * timedelta#
* #NAME = Bool, Byte, Short, UByte, UShort, UInt, ULong, ULongLong,
- * Float, LongDouble, CFloat, CLongDouble, Void, Object#
+ * Float, LongDouble, CFloat, CLongDouble, Void, Object, Datetime,
+ * Timedelta#
*/
Py@NAME@ArrType_Type.tp_hash = @name@_arrtype_hash;
/**end repeat**/
@@ -3069,7 +3115,9 @@ static PyTypeObject *typeobjects[] = {
&PyObjectArrType_Type,
&PyStringArrType_Type,
&PyUnicodeArrType_Type,
- &PyVoidArrType_Type
+ &PyVoidArrType_Type,
+ &PyDatetimeArrType_Type,
+ &PyTimedeltaArrType_Type
};
NPY_NO_EXPORT int
diff --git a/numpy/core/src/multiarray/testcalcs.py b/numpy/core/src/multiarray/testcalcs.py
new file mode 100644
index 000000000..138877d87
--- /dev/null
+++ b/numpy/core/src/multiarray/testcalcs.py
@@ -0,0 +1,71 @@
+from scipy import weave
+
+class YMD(object):
+ year = 0
+ month = 0
+ days = 0
+
+
+month_offset = [
+ [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 ],
+ [ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 ]
+]
+
+days_in_month = [
+ [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ],
+ [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
+]
+
+def is_leapyear(year):
+ return (year % 4 == 0) & ((year % 100 != 0) | (year % 400 == 0))
+
+
+# Return the year offset, that is the absolute date of the day
+# 31.12.(year-1) since 31.12.1969 in the proleptic Gregorian calendar.
+
+def year_offset(year):
+ code = """
+ year-=1970;
+ if ((year+1969) >= 0 || -1/4 == -1)
+ return_val = year*365 + year/4 - year/100 + year/400;
+ else
+ return_val = year*365 + (year-3)/4 - (year-99)/100 + (year-399)/400;
+ """
+ return weave.inline(code,['year'])
+
+
+def days_from_ymd(year, month, day):
+
+ leap = is_leapyear(year)
+
+ # Negative month values indicate months relative to the years end */
+ if (month < 0): month += 13
+ if not (month >= 1 and month<=12):
+ raise ValueError, "month out of range (1-21): %d" % month
+
+ # Negative values indicate days relative to the months end */
+ if (day < 0): day += days_in_month[leap][month - 1] + 1
+ if not (day >= 1 and day <= days_in_month[leap][month-1]):
+ raise ValueError, "day out of range: %d" % day
+
+ # Number of days between Dec 31, (year - 1) and Dec 31, 1969
+ # (can be negative).
+ #
+ yearoffset = year_offset(year);
+
+ # Calculate the number of days using yearoffset */
+ # Jan 1, 1970 is day 0 and thus Dec. 31, 1969 is day -1 */
+ absdate = day-1 + month_offset[leap][month - 1] + yearoffset;
+
+ return absdate;
+
+
+def ymd_from_days(days):
+ ymd = YMD()
+
+ year = 1970 + days / 365.2425
+
+
+
+
+
diff --git a/numpy/core/src/umath/funcs.inc.src b/numpy/core/src/umath/funcs.inc.src
index 9dd4a0cab..840146f07 100644
--- a/numpy/core/src/umath/funcs.inc.src
+++ b/numpy/core/src/umath/funcs.inc.src
@@ -1,3 +1,5 @@
+/* -*- c -*- */
+
/*
* This file is for the definitions of the non-c99 functions used in ufuncs.
* All the complex ufuncs are defined here along with a smattering of real and
diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src
index 01f971ae6..10cfa8716 100644
--- a/numpy/core/src/umath/loops.c.src
+++ b/numpy/core/src/umath/loops.c.src
@@ -798,6 +798,173 @@ U@TYPE@_remainder(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(f
/*
*****************************************************************************
+ ** DATETIME LOOPS **
+ *****************************************************************************
+ */
+
+/**begin repeat
+ * #type = datetime, timedelta#
+ * #TYPE = DATETIME, TIMEDELTA#
+ * #ftype = double, double#
+ */
+
+#define @TYPE@_fmax @TYPE@_maximum
+#define @TYPE@_fmin @TYPE@_minimum
+
+NPY_NO_EXPORT void
+@TYPE@_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data))
+{
+ OUTPUT_LOOP {
+ *((@type@ *)op1) = 1;
+ }
+}
+
+NPY_NO_EXPORT void
+@TYPE@_negative(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *((@type@ *)op1) = (@type@)(-(@type@)in1);
+ }
+}
+
+NPY_NO_EXPORT void
+@TYPE@_logical_not(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *((Bool *)op1) = !in1;
+ }
+}
+
+
+/**begin repeat1
+ * #kind = equal, not_equal, greater, greater_equal, less, less_equal,
+ * logical_and, logical_or#
+ * #OP = ==, !=, >, >=, <, <=, &&, ||#
+ */
+NPY_NO_EXPORT void
+@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ const @type@ in2 = *(@type@ *)ip2;
+ *((Bool *)op1) = in1 @OP@ in2;
+ }
+}
+/**end repeat1**/
+
+NPY_NO_EXPORT void
+@TYPE@_logical_xor(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ const @type@ in2 = *(@type@ *)ip2;
+ *((Bool *)op1)= (in1 && !in2) || (!in1 && in2);
+ }
+}
+
+/**begin repeat1
+ * #kind = maximum, minimum#
+ * #OP = >, <#
+ **/
+NPY_NO_EXPORT void
+@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ const @type@ in2 = *(@type@ *)ip2;
+ *((@type@ *)op1) = (in1 @OP@ in2) ? in1 : in2;
+ }
+}
+/**end repeat1**/
+
+NPY_NO_EXPORT void
+@TYPE@_absolute(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *((@type@ *)op1) = (in1 >= 0) ? in1 : -in1;
+ }
+}
+
+NPY_NO_EXPORT void
+@TYPE@_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *((@type@ *)op1) = in1 > 0 ? 1 : (in1 < 0 ? -1 : 0);
+ }
+}
+
+/**end repeat**/
+
+/* FIXME: implement the following correctly using the metadata: data is the
+ sequence of ndarrays in the same order as args.
+ */
+NPY_NO_EXPORT void
+DATETIME_Mm_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data))
+{
+ BINARY_LOOP {
+ const datetime in1 = *(datetime *)ip1;
+ const timedelta in2 = *(timedelta *)ip2;
+ *((datetime *)op1) = in1 + in2;
+ }
+}
+
+NPY_NO_EXPORT void
+DATETIME_mM_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const timedelta in1 = *(timedelta *)ip1;
+ const datetime in2 = *(datetime *)ip2;
+ *((datetime *)op1) = in1 + in2;
+ }
+}
+
+NPY_NO_EXPORT void
+TIMEDELTA_mm_m_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const timedelta in1 = *(timedelta *)ip1;
+ const timedelta in2 = *(timedelta *)ip2;
+ *((timedelta *)op1) = in1 + in2;
+ }
+}
+
+NPY_NO_EXPORT void
+DATETIME_Mm_M_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const datetime in1 = *(datetime *)ip1;
+ const timedelta in2 = *(timedelta *)ip2;
+ *((datetime *)op1) = in1 - in2;
+ }
+}
+
+NPY_NO_EXPORT void
+DATETIME_MM_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const datetime in1 = *(datetime *)ip1;
+ const datetime in2 = *(datetime *)ip2;
+ *((timedelta *)op1) = in1 - in2;
+ }
+}
+
+NPY_NO_EXPORT void
+TIMEDELTA_mm_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const timedelta in1 = *(timedelta *)ip1;
+ const timedelta in2 = *(timedelta *)ip2;
+ *((timedelta *)op1) = in1 - in2;
+ }
+}
+
+
+/*
+ *****************************************************************************
** FLOAT LOOPS **
*****************************************************************************
*/
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 5a075b4f2..94152ccb7 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -42,6 +42,9 @@
/* ---------------------------------------------------------------- */
+static int
+_does_loop_use_arrays(void *data);
+
/*
* fpstatus is the ufunc_formatted hardware status
* errmask is the handling mask specified by the user.
@@ -210,6 +213,8 @@ _lowest_type(char intype)
case PyArray_INT:
case PyArray_LONG:
case PyArray_LONGLONG:
+ case PyArray_DATETIME:
+ case PyArray_TIMEDELTA:
return PyArray_BYTE;
/* case PyArray_UBYTE */
case PyArray_USHORT:
@@ -1231,8 +1236,8 @@ construct_arrays(PyUFuncLoopObject *loop, PyObject *args, PyArrayObject **mps,
/* We don't do strings */
if (flexible && !object) {
- loop->notimplemented = 1;
- return nargs;
+ loop->notimplemented = 1;
+ return nargs;
}
/*
@@ -1528,14 +1533,21 @@ construct_arrays(PyUFuncLoopObject *loop, PyObject *args, PyArrayObject **mps,
loop->meth = BUFFER_UFUNCLOOP;
loop->needbuffer[i] = 1;
}
- if (!loop->obj
+ if (!(loop->obj & UFUNC_OBJ_ISOBJECT)
&& ((mps[i]->descr->type_num == PyArray_OBJECT)
|| (arg_types[i] == PyArray_OBJECT))) {
- loop->obj = 1;
+ loop->obj = UFUNC_OBJ_ISOBJECT|UFUNC_OBJ_NEEDS_API;
+ }
+ if (!(loop->obj & UFUNC_OBJ_NEEDS_API)
+ && ((mps[i]->descr->type_num == PyArray_DATETIME)
+ || (mps[i]->descr->type_num == PyArray_TIMEDELTA)
+ || (arg_types[i] == PyArray_DATETIME)
+ || (arg_types[i] == PyArray_TIMEDELTA))) {
+ loop->obj = UFUNC_OBJ_NEEDS_API;
}
}
- if (self->core_enabled && loop->obj) {
+ if (self->core_enabled && (loop->obj & UFUNC_OBJ_ISOBJECT)) {
PyErr_SetString(PyExc_TypeError,
"Object type not allowed in ufunc with signature");
return -1;
@@ -1741,7 +1753,7 @@ construct_arrays(PyUFuncLoopObject *loop, PyObject *args, PyArrayObject **mps,
PyErr_NoMemory();
return -1;
}
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
memset(loop->buffer[0], 0, memsize);
}
castptr = loop->buffer[0] + loop->bufsize*cnt + scbufsize*scnt;
@@ -1775,13 +1787,18 @@ construct_arrays(PyUFuncLoopObject *loop, PyObject *args, PyArrayObject **mps,
else {
loop->bufptr[i] = loop->buffer[i];
}
- if (!loop->objfunc && loop->obj) {
+ if (!loop->objfunc && (loop->obj & UFUNC_OBJ_ISOBJECT)) {
if (arg_types[i] == PyArray_OBJECT) {
loop->objfunc = 1;
}
}
}
}
+
+ if (_does_loop_use_arrays(loop->funcdata)) {
+ loop->funcdata = (void*)mps;
+ }
+
return nargs;
}
@@ -2102,7 +2119,7 @@ PyUFunc_GenericFunction(PyUFuncObject *self, PyObject *args, PyObject *kwds,
for (i = 0; i <self->nargs; i++) {
copyswapn[i] = mps[i]->descr->f->copyswapn;
mpselsize[i] = mps[i]->descr->elsize;
- pyobject[i] = (loop->obj
+ pyobject[i] = ((loop->obj & UFUNC_OBJ_ISOBJECT)
&& (mps[i]->descr->type_num == PyArray_OBJECT));
laststrides[i] = iters[i]->strides[loop->lastdim];
if (steps[i] && laststrides[i] != mpselsize[i]) {
@@ -2482,7 +2499,14 @@ construct_reduce(PyUFuncObject *self, PyArrayObject **arr, PyArrayObject *out,
/* Determine if object arrays are involved */
if (otype == PyArray_OBJECT || aar->descr->type_num == PyArray_OBJECT) {
- loop->obj = 1;
+ loop->obj = UFUNC_OBJ_ISOBJECT | UFUNC_OBJ_NEEDS_API;
+ }
+ else if ((otype == PyArray_DATETIME)
+ || (aar->descr->type_num == PyArray_DATETIME)
+ || (otype == PyArray_TIMEDELTA)
+ || (aar->descr->type_num == PyArray_TIMEDELTA))
+ {
+ loop->obj = UFUNC_OBJ_NEEDS_API;
}
else {
loop->obj = 0;
@@ -2633,7 +2657,7 @@ construct_reduce(PyUFuncObject *self, PyArrayObject **arr, PyArrayObject *out,
if (loop->buffer == NULL) {
goto fail;
}
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
memset(loop->buffer, 0, _size);
}
loop->castbuf = loop->buffer + loop->bufsize*aar->descr->elsize;
@@ -2649,7 +2673,7 @@ construct_reduce(PyUFuncObject *self, PyArrayObject **arr, PyArrayObject *out,
if (loop->buffer == NULL) {
goto fail;
}
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
memset(loop->buffer, 0, _size);
}
loop->bufptr[1] = loop->buffer;
@@ -2693,7 +2717,7 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
case ZERO_EL_REDUCELOOP:
/* fprintf(stderr, "ZERO..%d\n", loop->size); */
for (i = 0; i < loop->size; i++) {
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_INCREF(*((PyObject **)loop->idptr));
}
memmove(loop->bufptr[0], loop->idptr, loop->outsize);
@@ -2703,7 +2727,7 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
case ONE_EL_REDUCELOOP:
/*fprintf(stderr, "ONEDIM..%d\n", loop->size); */
while (loop->index < loop->size) {
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_INCREF(*((PyObject **)loop->it->dataptr));
}
memmove(loop->bufptr[0], loop->it->dataptr, loop->outsize);
@@ -2716,7 +2740,7 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
/*fprintf(stderr, "NOBUFFER..%d\n", loop->size); */
while (loop->index < loop->size) {
/* Copy first element to output */
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_INCREF(*((PyObject **)loop->it->dataptr));
}
memmove(loop->bufptr[0], loop->it->dataptr, loop->outsize);
@@ -2752,7 +2776,7 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
arr->descr->f->copyswap(loop->buffer, loop->inptr,
loop->swap, NULL);
loop->cast(loop->buffer, loop->castbuf, 1, NULL, NULL);
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_XINCREF(*((PyObject **)loop->castbuf));
}
memcpy(loop->bufptr[0], loop->castbuf, loop->outsize);
@@ -2835,7 +2859,7 @@ PyUFunc_Accumulate(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
/* Accumulate */
/* fprintf(stderr, "ZERO..%d\n", loop->size); */
for (i = 0; i < loop->size; i++) {
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_INCREF(*((PyObject **)loop->idptr));
}
memcpy(loop->bufptr[0], loop->idptr, loop->outsize);
@@ -2846,7 +2870,7 @@ PyUFunc_Accumulate(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
/* Accumulate */
/* fprintf(stderr, "ONEDIM..%d\n", loop->size); */
while (loop->index < loop->size) {
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_INCREF(*((PyObject **)loop->it->dataptr));
}
memmove(loop->bufptr[0], loop->it->dataptr, loop->outsize);
@@ -2860,7 +2884,7 @@ PyUFunc_Accumulate(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
/* fprintf(stderr, "NOBUFFER..%d\n", loop->size); */
while (loop->index < loop->size) {
/* Copy first element to output */
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_INCREF(*((PyObject **)loop->it->dataptr));
}
memmove(loop->bufptr[0], loop->it->dataptr, loop->outsize);
@@ -2899,7 +2923,7 @@ PyUFunc_Accumulate(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
arr->descr->f->copyswap(loop->buffer, loop->inptr,
loop->swap, NULL);
loop->cast(loop->buffer, loop->castbuf, 1, NULL, NULL);
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_XINCREF(*((PyObject **)loop->castbuf));
}
memcpy(loop->bufptr[0], loop->castbuf, loop->outsize);
@@ -3024,7 +3048,7 @@ PyUFunc_Reduceat(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *ind,
ptr = (intp *)ind->data;
for (i = 0; i < nn; i++) {
loop->bufptr[1] = loop->it->dataptr + (*ptr)*loop->steps[1];
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_XINCREF(*((PyObject **)loop->bufptr[1]));
}
memcpy(loop->bufptr[0], loop->bufptr[1], loop->outsize);
@@ -3055,7 +3079,7 @@ PyUFunc_Reduceat(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *ind,
while (loop->index < loop->size) {
ptr = (intp *)ind->data;
for (i = 0; i < nn; i++) {
- if (loop->obj) {
+ if (loop->obj & UFUNC_OBJ_ISOBJECT) {
Py_XINCREF(*((PyObject **)loop->idptr));
}
memcpy(loop->bufptr[0], loop->idptr, loop->outsize);
@@ -3445,6 +3469,9 @@ ufunc_generic_call(PyUFuncObject *self, PyObject *args, PyObject *kwds)
* PyErr_SetString(PyExc_TypeError,"");
* return NULL;
*/
+ /* This is expected by at least the ndarray rich_comparisons
+ to allow for additional handling for strings.
+ */
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
@@ -3734,6 +3761,27 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
return (PyObject *)self;
}
+/* Specify that the loop specified by the given index should use the array of
+ * input and arrays as the data pointer to the loop.
+ */
+/*UFUNC_API*/
+NPY_NO_EXPORT int
+PyUFunc_SetUsesArraysAsData(void **data, size_t i)
+{
+ data[i] = (void*)PyUFunc_SetUsesArraysAsData;
+ return 0;
+}
+
+/* Return 1 if the given data pointer for the loop specifies that it needs the
+ * arrays as the data pointer.
+ */
+static int
+_does_loop_use_arrays(void *data)
+{
+ return (data == PyUFunc_SetUsesArraysAsData);
+}
+
+
/*
* This is the first-part of the CObject structure.
*
diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py
index 60d2b16bc..d3f6d8654 100644
--- a/numpy/core/tests/test_dtype.py
+++ b/numpy/core/tests/test_dtype.py
@@ -81,5 +81,23 @@ class TestMonsterType(TestCase):
('yi', np.dtype((a, (3, 2))))])
self.failUnless(hash(c) == hash(d))
+class TestMetadata(TestCase):
+ def test_no_metadata(self):
+ d = np.dtype(int)
+ self.assertEqual(d.metadata, None)
+
+ def test_metadata_takes_dict(self):
+ d = np.dtype(int, metadata={'datum': 1})
+ self.assertEqual(d.metadata, {'datum': 1})
+
+ def test_metadata_rejects_nondict(self):
+ self.assertRaises(TypeError, np.dtype, int, metadata='datum')
+ self.assertRaises(TypeError, np.dtype, int, metadata=1)
+ self.assertRaises(TypeError, np.dtype, int, metadata=None)
+
+ def test_nested_metadata(self):
+ d = np.dtype([('a', np.dtype(int, metadata={'datum': 1}))])
+ self.assertEqual(d['a'].metadata, {'datum': 1})
+
if __name__ == "__main__":
run_module_suite()
diff --git a/numpy/lib/tests/test_arrayterator.py b/numpy/lib/tests/test_arrayterator.py
index 421569651..c600d45eb 100644
--- a/numpy/lib/tests/test_arrayterator.py
+++ b/numpy/lib/tests/test_arrayterator.py
@@ -41,3 +41,7 @@ def test():
# Check that all elements are iterated correctly
assert list(c.flat) == list(d.flat)
+
+if __name__ == '__main__':
+ from numpy.testing import run_module_suite
+ run_module_suite()