summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wiebe <mwiebe@enthought.com>2011-06-07 13:21:12 -0500
committerMark Wiebe <mwiebe@enthought.com>2011-06-07 13:21:12 -0500
commita8274369fcbc3332067a8555782cb40cc4684c83 (patch)
tree00bf53d4e3951cd9e9588edf9876925482dea2e0
parentfd9ef72daaa66803fd3109502fb584bbda6f407c (diff)
parent283b2e712bd52e6661f2dc338eb14caae2f5a8da (diff)
downloadnumpy-a8274369fcbc3332067a8555782cb40cc4684c83.tar.gz
Merge branch 'datetime-fixes'
-rw-r--r--numpy/core/_internal.py30
-rw-r--r--numpy/core/_mx_datetime_parser.py962
-rw-r--r--numpy/core/arrayprint.py50
-rw-r--r--numpy/core/code_generators/generate_umath.py144
-rw-r--r--numpy/core/code_generators/numpy_api.py3
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h71
-rw-r--r--numpy/core/include/numpy/npy_3kcompat.h4
-rw-r--r--numpy/core/include/numpy/ufuncobject.h43
-rw-r--r--numpy/core/numeric.py2
-rw-r--r--numpy/core/numerictypes.py14
-rw-r--r--numpy/core/src/multiarray/_datetime.h337
-rw-r--r--numpy/core/src/multiarray/arrayobject.c14
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src460
-rw-r--r--numpy/core/src/multiarray/conversion_utils.c213
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c123
-rw-r--r--numpy/core/src/multiarray/ctors.c13
-rw-r--r--numpy/core/src/multiarray/datetime.c4264
-rw-r--r--numpy/core/src/multiarray/descriptor.c424
-rw-r--r--numpy/core/src/multiarray/dtype_transfer.c262
-rw-r--r--numpy/core/src/multiarray/lowlevel_strided_loops.c.src1
-rw-r--r--numpy/core/src/multiarray/methods.c2
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c310
-rw-r--r--numpy/core/src/multiarray/scalarapi.c10
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src348
-rw-r--r--numpy/core/src/umath/loops.c.src243
-rw-r--r--numpy/core/src/umath/loops.h144
-rw-r--r--numpy/core/src/umath/loops.h.src69
-rw-r--r--numpy/core/src/umath/ufunc_object.c1447
-rw-r--r--numpy/core/src/umath/ufunc_object.h71
-rw-r--r--numpy/core/src/umath/umathmodule.c.src32
-rw-r--r--numpy/core/tests/test_arrayprint.py2
-rw-r--r--numpy/core/tests/test_datetime.py929
-rw-r--r--numpy/core/tests/test_dtype.py8
-rw-r--r--numpy/core/tests/test_regression.py2
-rw-r--r--numpy/lib/tests/test_type_check.py8
-rw-r--r--numpy/lib/type_check.py46
-rw-r--r--numpy/ma/core.py4
37 files changed, 8121 insertions, 2988 deletions
diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py
index 5298f412b..a5b6d117a 100644
--- a/numpy/core/_internal.py
+++ b/numpy/core/_internal.py
@@ -166,36 +166,6 @@ def _split(input):
return newlist
-format_datetime = re.compile(asbytes(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 == asbytes('M8') or typecode == asbytes('datetime64'))
- defaults = [asbytes('us'), 1, 1, 1]
- names = ['baseunit', 'num', 'den', 'events']
- func = [bytes, 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(asbytes(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
deleted file mode 100644
index d8db18793..000000000
--- a/numpy/core/_mx_datetime_parser.py
+++ /dev/null
@@ -1,962 +0,0 @@
-#-*- 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 d0b899901..8595d6f02 100644
--- a/numpy/core/arrayprint.py
+++ b/numpy/core/arrayprint.py
@@ -15,7 +15,7 @@ __docformat__ = 'restructuredtext'
import sys
import numerictypes as _nt
from umath import maximum, minimum, absolute, not_equal, isnan, isinf
-from multiarray import format_longfloat
+from multiarray import format_longfloat, datetime_as_string
from fromnumeric import ravel
@@ -72,7 +72,8 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
- 'bool'
- 'int'
- - 'timeint' : a `numpy.timeinteger`
+ - 'timedelta' : a `numpy.timedelta64`
+ - 'datetime' : a `numpy.datetime64`
- 'float'
- 'longfloat' : 128-bit floats
- 'complexfloat'
@@ -83,7 +84,7 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
Other keys that can be used to set a group of types at once are::
- 'all' : sets all types
- - 'int_kind' : sets 'int' and 'timeint'
+ - 'int_kind' : sets 'int'
- 'float_kind' : sets 'float' and 'longfloat'
- 'complex_kind' : sets 'complexfloat' and 'longcomplexfloat'
- 'str_kind' : sets 'str' and 'numpystr'
@@ -239,12 +240,13 @@ def _array2string(a, max_line_width, precision, suppress_small, separator=' ',
formatdict = {'bool' : _boolFormatter,
'int' : IntegerFormat(data),
- 'timeint' : str,
'float' : FloatFormat(data, precision, suppress_small),
'longfloat' : LongFloatFormat(precision),
'complexfloat' : ComplexFormat(data, precision,
suppress_small),
'longcomplexfloat' : LongComplexFormat(precision),
+ 'datetime' : DatetimeFormat(True, None, -1),
+ 'timedelta' : TimedeltaFormat(data),
'numpystr' : repr,
'str' : str}
if formatter is not None:
@@ -253,7 +255,7 @@ def _array2string(a, max_line_width, precision, suppress_small, separator=' ',
for key in formatdict.keys():
formatdict[key] = formatter['all']
if 'int_kind' in fkeys:
- for key in ['int', 'timeint']:
+ for key in ['int']:
formatdict[key] = formatter['int_kind']
if 'float_kind' in fkeys:
for key in ['float', 'longfloat']:
@@ -280,8 +282,8 @@ def _array2string(a, max_line_width, precision, suppress_small, separator=' ',
if issubclass(dtypeobj, _nt.bool_):
format_function = formatdict['bool']
elif issubclass(dtypeobj, _nt.integer):
- if issubclass(dtypeobj, _nt.timeinteger):
- format_function = formatdict['timeint']
+ if issubclass(dtypeobj, _nt.timedelta64):
+ format_function = formatdict['timedelta']
else:
format_function = formatdict['int']
elif issubclass(dtypeobj, _nt.floating):
@@ -296,6 +298,8 @@ def _array2string(a, max_line_width, precision, suppress_small, separator=' ',
format_function = formatdict['complexfloat']
elif issubclass(dtypeobj, (_nt.unicode_, _nt.string_)):
format_function = formatdict['numpystr']
+ elif issubclass(dtypeobj, _nt.datetime64):
+ format_function = formatdict['datetime']
else:
format_function = formatdict['str']
@@ -361,7 +365,8 @@ def array2string(a, max_line_width=None, precision=None,
- 'bool'
- 'int'
- - 'timeint' : a `numpy.timeinteger`
+ - 'timedelta' : a `numpy.timedelta64`
+ - 'datetime' : a `numpy.datetime64`
- 'float'
- 'longfloat' : 128-bit floats
- 'complexfloat'
@@ -372,7 +377,7 @@ def array2string(a, max_line_width=None, precision=None,
Other keys that can be used to set a group of types at once are::
- 'all' : sets all types
- - 'int_kind' : sets 'int' and 'timeint'
+ - 'int_kind' : sets 'int'
- 'float_kind' : sets 'float' and 'longfloat'
- 'complex_kind' : sets 'complexfloat' and 'longcomplexfloat'
- 'str_kind' : sets 'str' and 'numpystr'
@@ -691,3 +696,30 @@ class ComplexFormat(object):
else:
i = i + 'j'
return r + i
+
+class DatetimeFormat(object):
+ def __init__(self, uselocaltime=True, overrideunit=None, tzoffset=-1):
+ self.local = uselocaltime
+ self.unit = overrideunit
+ self.tzoffset = -1
+
+ def __call__(self, x):
+ return "'%s'" % datetime_as_string(x,
+ local=self.local,
+ unit=self.unit,
+ tzoffset=self.tzoffset)
+
+class TimedeltaFormat(object):
+ def __init__(self, data):
+ if data.dtype.kind == 'm':
+ v = data.view('i8')
+ max_str_len = max(len(str(maximum.reduce(v))),
+ len(str(minimum.reduce(v))))
+ self.format = '%' + str(max_str_len) + 'd'
+
+ def __call__(self, x):
+ if _MININT < x < _MAXINT:
+ return self.format % x.astype('i8')
+ else:
+ return "%s" % x
+
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py
index 9382b1fae..b018e1948 100644
--- a/numpy/core/code_generators/generate_umath.py
+++ b/numpy/core/code_generators/generate_umath.py
@@ -12,10 +12,10 @@ 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()
-
+# Sentinel value to specify using the full type description in the
+# function name
+class FullTypeDescr(object):
+ pass
class TypeDescription(object):
"""Type signature for a ufunc.
@@ -24,7 +24,7 @@ class TypeDescription(object):
----------
type : str
Character representing the nominal type.
- func_data : str or None or UsesArraysAsData, optional
+ func_data : str or None or FullTypeDescr, optional
The string representing the expression to insert into the data array, if
any.
in_ : str or None, optional
@@ -100,7 +100,7 @@ class Ufunc(object):
docstring: docstring for the ufunc
type_descriptions: list of TypeDescription objects
"""
- def __init__(self, nin, nout, identity, docstring,
+ def __init__(self, nin, nout, identity, docstring, typereso,
*type_descriptions):
self.nin = nin
self.nout = nout
@@ -108,6 +108,7 @@ class Ufunc(object):
identity = None_
self.identity = identity
self.docstring = docstring
+ self.typereso = typereso
self.type_descriptions = []
for td in type_descriptions:
self.type_descriptions.extend(td)
@@ -193,6 +194,7 @@ O = 'O'
P = 'P'
ints = 'bBhHiIlLqQ'
times = 'Mm'
+timedeltaonly = 'm'
intsO = ints + O
bints = '?' + ints
bintsO = bints + O
@@ -209,12 +211,14 @@ allP = bints+times+flts+cmplxP
nobool = all[1:]
noobj = all[:-3]+all[-2:]
nobool_or_obj = all[1:-3]+all[-2:]
+nobool_or_datetime = all[1:-2]+all[-1:]
intflt = ints+flts
intfltcmplx = ints+flts+cmplx
nocmplx = bints+times+flts
nocmplxO = nocmplx+O
nocmplxP = nocmplx+P
notimes_or_obj = bints + inexact
+nodatetime_or_obj = bints + inexact
# Find which code corresponds to int64.
int64 = ''
@@ -233,58 +237,80 @@ defdict = {
'add' :
Ufunc(2, 1, Zero,
docstrings.get('numpy.core.umath.add'),
+ 'PyUFunc_AdditionTypeResolution',
TD(notimes_or_obj),
- [TypeDescription('M', UsesArraysAsData, 'Mm', 'M'),
- TypeDescription('m', UsesArraysAsData, 'mm', 'm'),
- TypeDescription('M', UsesArraysAsData, 'mM', 'M'),
+ [TypeDescription('M', FullTypeDescr, 'Mm', 'M'),
+ TypeDescription('m', FullTypeDescr, 'mm', 'm'),
+ TypeDescription('M', FullTypeDescr, 'mM', 'M'),
],
TD(O, f='PyNumber_Add'),
),
'subtract' :
Ufunc(2, 1, Zero,
docstrings.get('numpy.core.umath.subtract'),
+ 'PyUFunc_SubtractionTypeResolution',
TD(notimes_or_obj),
- [TypeDescription('M', UsesArraysAsData, 'Mm', 'M'),
- TypeDescription('m', UsesArraysAsData, 'mm', 'm'),
- TypeDescription('M', UsesArraysAsData, 'MM', 'm'),
+ [TypeDescription('M', FullTypeDescr, 'Mm', 'M'),
+ TypeDescription('m', FullTypeDescr, 'mm', 'm'),
+ TypeDescription('M', FullTypeDescr, 'MM', 'm'),
],
TD(O, f='PyNumber_Subtract'),
),
'multiply' :
Ufunc(2, 1, One,
docstrings.get('numpy.core.umath.multiply'),
+ 'PyUFunc_MultiplicationTypeResolution',
TD(notimes_or_obj),
+ [TypeDescription('m', FullTypeDescr, 'm' + int64, 'm'),
+ TypeDescription('m', FullTypeDescr, int64 + 'm', 'm'),
+ TypeDescription('m', FullTypeDescr, 'md', 'm'),
+ TypeDescription('m', FullTypeDescr, 'dm', 'm'),
+ ],
TD(O, f='PyNumber_Multiply'),
),
'divide' :
Ufunc(2, 1, One,
docstrings.get('numpy.core.umath.divide'),
+ 'PyUFunc_DivisionTypeResolution',
TD(intfltcmplx),
+ [TypeDescription('m', FullTypeDescr, 'm' + int64, 'm'),
+ TypeDescription('m', FullTypeDescr, 'md', 'm'),
+ ],
TD(O, f='PyNumber_Divide'),
),
'floor_divide' :
Ufunc(2, 1, One,
docstrings.get('numpy.core.umath.floor_divide'),
+ 'PyUFunc_DivisionTypeResolution',
TD(intfltcmplx),
+ [TypeDescription('m', FullTypeDescr, 'm' + int64, 'm'),
+ TypeDescription('m', FullTypeDescr, 'md', 'm'),
+ ],
TD(O, f='PyNumber_FloorDivide'),
),
'true_divide' :
Ufunc(2, 1, One,
docstrings.get('numpy.core.umath.true_divide'),
+ 'PyUFunc_DivisionTypeResolution',
TD('bBhH', out='d'),
TD('iIlLqQ', out='d'),
TD(flts+cmplx),
+ [TypeDescription('m', FullTypeDescr, 'm' + int64, 'm'),
+ TypeDescription('m', FullTypeDescr, 'md', 'm'),
+ ],
TD(O, f='PyNumber_TrueDivide'),
),
'conjugate' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.conjugate'),
+ None,
TD(ints+flts+cmplx),
TD(P, f='conjugate'),
),
'fmod' :
Ufunc(2, 1, Zero,
docstrings.get('numpy.core.umath.fmod'),
+ None,
TD(ints),
TD(flts, f='fmod', astype={'e':'f'}),
TD(P, f='fmod'),
@@ -292,24 +318,28 @@ defdict = {
'square' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.square'),
+ None,
TD(ints+inexact),
TD(O, f='Py_square'),
),
'reciprocal' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.reciprocal'),
+ None,
TD(ints+inexact),
TD(O, f='Py_reciprocal'),
),
'ones_like' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.ones_like'),
+ 'PyUFunc_SimpleUnaryOperationTypeResolution',
TD(noobj),
TD(O, f='Py_get_one'),
),
'power' :
Ufunc(2, 1, One,
docstrings.get('numpy.core.umath.power'),
+ None,
TD(ints),
TD(inexact, f='pow', astype={'e':'f'}),
TD(O, f='npy_ObjectPower'),
@@ -317,378 +347,443 @@ defdict = {
'absolute' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.absolute'),
- TD(bints+flts+times),
+ 'PyUFunc_AbsoluteTypeResolution',
+ TD(bints+flts+timedeltaonly),
TD(cmplx, out=('f', 'd', 'g')),
TD(O, f='PyNumber_Absolute'),
),
'_arg' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath._arg'),
+ None,
TD(cmplx, out=('f', 'd', 'g')),
),
'negative' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.negative'),
- TD(bints+flts+times),
+ 'PyUFunc_SimpleUnaryOperationTypeResolution',
+ TD(bints+flts+timedeltaonly),
TD(cmplx, f='neg'),
TD(O, f='PyNumber_Negative'),
),
'sign' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.sign'),
- TD(nobool),
+ 'PyUFunc_SimpleUnaryOperationTypeResolution',
+ TD(nobool_or_datetime),
),
'greater' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.greater'),
+ 'PyUFunc_SimpleBinaryComparisonTypeResolution',
TD(all, out='?'),
),
'greater_equal' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.greater_equal'),
+ None,
TD(all, out='?'),
),
'less' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.less'),
+ 'PyUFunc_SimpleBinaryComparisonTypeResolution',
TD(all, out='?'),
),
'less_equal' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.less_equal'),
+ 'PyUFunc_SimpleBinaryComparisonTypeResolution',
TD(all, out='?'),
),
'equal' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.equal'),
+ 'PyUFunc_SimpleBinaryComparisonTypeResolution',
TD(all, out='?'),
),
'not_equal' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.not_equal'),
+ 'PyUFunc_SimpleBinaryComparisonTypeResolution',
TD(all, out='?'),
),
'logical_and' :
Ufunc(2, 1, One,
docstrings.get('numpy.core.umath.logical_and'),
- TD(noobj, out='?'),
+ 'PyUFunc_SimpleBinaryComparisonTypeResolution',
+ TD(nodatetime_or_obj, out='?'),
TD(P, f='logical_and'),
),
'logical_not' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.logical_not'),
- TD(noobj, out='?'),
+ None,
+ TD(nodatetime_or_obj, out='?'),
TD(P, f='logical_not'),
),
'logical_or' :
Ufunc(2, 1, Zero,
docstrings.get('numpy.core.umath.logical_or'),
- TD(noobj, out='?'),
+ 'PyUFunc_SimpleBinaryComparisonTypeResolution',
+ TD(nodatetime_or_obj, out='?'),
TD(P, f='logical_or'),
),
'logical_xor' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.logical_xor'),
- TD(noobj, out='?'),
+ 'PyUFunc_SimpleBinaryComparisonTypeResolution',
+ TD(nodatetime_or_obj, out='?'),
TD(P, f='logical_xor'),
),
'maximum' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.maximum'),
+ 'PyUFunc_SimpleBinaryOperationTypeResolution',
TD(noobj),
TD(O, f='npy_ObjectMax')
),
'minimum' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.minimum'),
+ 'PyUFunc_SimpleBinaryOperationTypeResolution',
TD(noobj),
TD(O, f='npy_ObjectMin')
),
'fmax' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.fmax'),
+ 'PyUFunc_SimpleBinaryOperationTypeResolution',
TD(noobj),
TD(O, f='npy_ObjectMax')
),
'fmin' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.fmin'),
+ 'PyUFunc_SimpleBinaryOperationTypeResolution',
TD(noobj),
TD(O, f='npy_ObjectMin')
),
'logaddexp' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.logaddexp'),
+ None,
TD(flts, f="logaddexp", astype={'e':'f'})
),
'logaddexp2' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.logaddexp2'),
+ None,
TD(flts, f="logaddexp2", astype={'e':'f'})
),
-# FIXME: decide if the times should have the bitwise operations.
'bitwise_and' :
Ufunc(2, 1, One,
docstrings.get('numpy.core.umath.bitwise_and'),
+ None,
TD(bints),
TD(O, f='PyNumber_And'),
),
'bitwise_or' :
Ufunc(2, 1, Zero,
docstrings.get('numpy.core.umath.bitwise_or'),
+ None,
TD(bints),
TD(O, f='PyNumber_Or'),
),
'bitwise_xor' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.bitwise_xor'),
+ None,
TD(bints),
TD(O, f='PyNumber_Xor'),
),
'invert' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.invert'),
+ None,
TD(bints),
TD(O, f='PyNumber_Invert'),
),
'left_shift' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.left_shift'),
+ None,
TD(ints),
TD(O, f='PyNumber_Lshift'),
),
'right_shift' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.right_shift'),
+ None,
TD(ints),
TD(O, f='PyNumber_Rshift'),
),
'degrees' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.degrees'),
+ None,
TD(fltsP, f='degrees', astype={'e':'f'}),
),
'rad2deg' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.rad2deg'),
+ None,
TD(fltsP, f='rad2deg', astype={'e':'f'}),
),
'radians' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.radians'),
+ None,
TD(fltsP, f='radians', astype={'e':'f'}),
),
'deg2rad' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.deg2rad'),
+ None,
TD(fltsP, f='deg2rad', astype={'e':'f'}),
),
'arccos' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.arccos'),
+ None,
TD(inexact, f='acos', astype={'e':'f'}),
TD(P, f='arccos'),
),
'arccosh' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.arccosh'),
+ None,
TD(inexact, f='acosh', astype={'e':'f'}),
TD(P, f='arccosh'),
),
'arcsin' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.arcsin'),
+ None,
TD(inexact, f='asin', astype={'e':'f'}),
TD(P, f='arcsin'),
),
'arcsinh' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.arcsinh'),
+ None,
TD(inexact, f='asinh', astype={'e':'f'}),
TD(P, f='arcsinh'),
),
'arctan' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.arctan'),
+ None,
TD(inexact, f='atan', astype={'e':'f'}),
TD(P, f='arctan'),
),
'arctanh' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.arctanh'),
+ None,
TD(inexact, f='atanh', astype={'e':'f'}),
TD(P, f='arctanh'),
),
'cos' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.cos'),
+ None,
TD(inexact, f='cos', astype={'e':'f'}),
TD(P, f='cos'),
),
'sin' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.sin'),
+ None,
TD(inexact, f='sin', astype={'e':'f'}),
TD(P, f='sin'),
),
'tan' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.tan'),
+ None,
TD(inexact, f='tan', astype={'e':'f'}),
TD(P, f='tan'),
),
'cosh' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.cosh'),
+ None,
TD(inexact, f='cosh', astype={'e':'f'}),
TD(P, f='cosh'),
),
'sinh' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.sinh'),
+ None,
TD(inexact, f='sinh', astype={'e':'f'}),
TD(P, f='sinh'),
),
'tanh' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.tanh'),
+ None,
TD(inexact, f='tanh', astype={'e':'f'}),
TD(P, f='tanh'),
),
'exp' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.exp'),
+ None,
TD(inexact, f='exp', astype={'e':'f'}),
TD(P, f='exp'),
),
'exp2' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.exp2'),
+ None,
TD(inexact, f='exp2', astype={'e':'f'}),
TD(P, f='exp2'),
),
'expm1' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.expm1'),
+ None,
TD(inexact, f='expm1', astype={'e':'f'}),
TD(P, f='expm1'),
),
'log' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.log'),
+ None,
TD(inexact, f='log', astype={'e':'f'}),
TD(P, f='log'),
),
'log2' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.log2'),
+ None,
TD(inexact, f='log2', astype={'e':'f'}),
TD(P, f='log2'),
),
'log10' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.log10'),
+ None,
TD(inexact, f='log10', astype={'e':'f'}),
TD(P, f='log10'),
),
'log1p' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.log1p'),
+ None,
TD(inexact, f='log1p', astype={'e':'f'}),
TD(P, f='log1p'),
),
'sqrt' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.sqrt'),
+ None,
TD(inexact, f='sqrt', astype={'e':'f'}),
TD(P, f='sqrt'),
),
'ceil' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.ceil'),
+ None,
TD(flts, f='ceil', astype={'e':'f'}),
TD(P, f='ceil'),
),
'trunc' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.trunc'),
+ None,
TD(flts, f='trunc', astype={'e':'f'}),
TD(P, f='trunc'),
),
'fabs' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.fabs'),
+ None,
TD(flts, f='fabs', astype={'e':'f'}),
TD(P, f='fabs'),
),
'floor' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.floor'),
+ None,
TD(flts, f='floor', astype={'e':'f'}),
TD(P, f='floor'),
),
'rint' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.rint'),
+ None,
TD(inexact, f='rint', astype={'e':'f'}),
TD(P, f='rint'),
),
'arctan2' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.arctan2'),
+ None,
TD(flts, f='atan2', astype={'e':'f'}),
TD(P, f='arctan2'),
),
'remainder' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.remainder'),
+ None,
TD(intflt),
TD(O, f='PyNumber_Remainder'),
),
'hypot' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.hypot'),
+ None,
TD(flts, f='hypot', astype={'e':'f'}),
TD(P, f='hypot'),
),
'isnan' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isnan'),
+ None,
TD(inexact, out='?'),
),
'isinf' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isinf'),
+ None,
TD(inexact, out='?'),
),
'isfinite' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isfinite'),
+ None,
TD(inexact, out='?'),
),
'signbit' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.signbit'),
+ None,
TD(flts, out='?'),
),
'copysign' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.copysign'),
+ None,
TD(flts),
),
'nextafter' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.nextafter'),
+ None,
TD(flts),
),
'spacing' :
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.spacing'),
+ None,
TD(flts),
),
'modf' :
Ufunc(1, 2, None,
docstrings.get('numpy.core.umath.modf'),
+ None,
TD(flts),
),
}
@@ -751,7 +846,7 @@ def make_arrays(funcdict):
thedict = chartotype1 # one input and one output
for t in uf.type_descriptions:
- if t.func_data not in (None, UsesArraysAsData):
+ if t.func_data not in (None, FullTypeDescr):
funclist.append('NULL')
astype = ''
if not t.astype is None:
@@ -773,11 +868,10 @@ def make_arrays(funcdict):
datalist.append('(void *)NULL')
#datalist.append('(void *)%s' % t.func_data)
sub += 1
- elif t.func_data is UsesArraysAsData:
+ elif t.func_data is FullTypeDescr:
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')
tname = english_upper(chartoname[t.type])
@@ -828,6 +922,10 @@ r"""f = PyUFunc_FromFuncAndData(%s_functions, %s_data, %s_signatures, %d,
uf.nin, uf.nout,
uf.identity,
name, docstring))
+ if uf.typereso != None:
+ mlist.append(
+ r"((PyUFuncObject *)f)->type_resolution_function = &%s;" %
+ uf.typereso)
mlist.append(r"""PyDict_SetItemString(dictionary, "%s", f);""" % name)
mlist.append(r"""Py_DECREF(f);""")
code3list.append('\n'.join(mlist))
diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py
index db2c368dd..a789ae683 100644
--- a/numpy/core/code_generators/numpy_api.py
+++ b/numpy/core/code_generators/numpy_api.py
@@ -361,6 +361,9 @@ ufunc_funcs_api = {
'PyUFunc_ee_e': 36,
'PyUFunc_ee_e_As_ff_f': 37,
'PyUFunc_ee_e_As_dd_d': 38,
+ # End 1.6 API
+ 'PyUFunc_DefaultTypeResolution': 39,
+ 'PyUFunc_ValidateCasting': 40,
}
# List of all the dicts which define the C API
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 2a21a779e..5bed8d70c 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -213,21 +213,37 @@ typedef enum {
NPY_RAISE=2
} NPY_CLIPMODE;
+/* The special not-a-time (NaT) value */
+#define NPY_DATETIME_NAT NPY_MIN_INT64
+/*
+ * Theoretical maximum length of a DATETIME ISO 8601 string
+ * YEAR: 21 (64-bit year)
+ * MONTH: 3
+ * DAY: 3
+ * HOURS: 3
+ * MINUTES: 3
+ * SECONDS: 3
+ * ATTOSECONDS: 1 + 3*6
+ * TIMEZONE: 5
+ * NULL TERMINATOR: 1
+ */
+#define NPY_DATETIME_MAX_ISO8601_STRLEN (21+3*5+1+3*6+6+1)
+
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_FR_Y, /* Years */
+ NPY_FR_M, /* Months */
+ NPY_FR_W, /* Weeks */
+ NPY_FR_B, /* Business days (weekdays, doesn't account for holidays) */
+ NPY_FR_D, /* Days */
+ NPY_FR_h, /* hours */
+ NPY_FR_m, /* minutes */
+ NPY_FR_s, /* seconds */
+ NPY_FR_ms,/* milliseconds */
+ NPY_FR_us,/* microseconds */
+ NPY_FR_ns,/* nanoseconds */
+ NPY_FR_ps,/* picoseconds */
+ NPY_FR_fs,/* femtoseconds */
+ NPY_FR_as /* attoseconds */
} NPY_DATETIMEUNIT;
#define NPY_DATETIME_NUMUNITS (NPY_FR_as + 1)
@@ -676,23 +692,32 @@ typedef struct {
typedef struct {
NPY_DATETIMEUNIT base;
int num;
- int den; /*
- * Converted to 1 on input for now -- an
- * input-only mechanism
- */
+ /*
+ * 'den' is unused, kept here for ABI compatibility with 1.6.
+ * TODO: Remove for 2.0.
+ */
+ int den;
int events;
} PyArray_DatetimeMetaData;
+/*
+ * This structure contains an exploded view of a date-time value.
+ * NaT is represented by year == NPY_DATETIME_NAT.
+ */
typedef struct {
- npy_longlong year;
- int month, day, hour, min, sec, us, ps, as;
+ npy_int64 year;
+ npy_int32 month, day, hour, min, sec, us, ps, as;
+ npy_int32 event;
} npy_datetimestruct;
+/* TO BE REMOVED - NOT USED INTERNALLY. */
typedef struct {
- npy_longlong day;
- int sec, us, ps, as;
+ npy_int64 day;
+ npy_int32 sec, us, ps, as;
+ npy_int32 event;
} npy_timedeltastruct;
+/* TO BE REMOVED - NOT USED INTERNALLY. */
#if PY_VERSION_HEX >= 0x03000000
#define PyDataType_GetDatetimeMetaData(descr) \
((descr->metadata == NULL) ? NULL : \
@@ -802,7 +827,7 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
/*
* Size of internal buffers used for alignment Make BUFSIZE a multiple
- * of sizeof(cdouble) -- ususally 16 so that ufunc buffers are aligned
+ * of sizeof(cdouble) -- usually 16 so that ufunc buffers are aligned
*/
#define NPY_MIN_BUFSIZE ((int)sizeof(cdouble))
#define NPY_MAX_BUFSIZE (((int)sizeof(cdouble))*1000000)
diff --git a/numpy/core/include/numpy/npy_3kcompat.h b/numpy/core/include/numpy/npy_3kcompat.h
index 02355e852..7ff9c8f48 100644
--- a/numpy/core/include/numpy/npy_3kcompat.h
+++ b/numpy/core/include/numpy/npy_3kcompat.h
@@ -348,7 +348,7 @@ NpyCapsule_Check(PyObject *ptr)
return PyCapsule_CheckExact(ptr);
}
-static void
+static NPY_INLINE void
simple_capsule_dtor(PyObject *cap)
{
PyArray_free(PyCapsule_GetPointer(cap, NULL));
@@ -387,7 +387,7 @@ NpyCapsule_Check(PyObject *ptr)
return PyCObject_Check(ptr);
}
-static void
+static NPY_INLINE void
simple_capsule_dtor(void *ptr)
{
PyArray_free(ptr);
diff --git a/numpy/core/include/numpy/ufuncobject.h b/numpy/core/include/numpy/ufuncobject.h
index 34cd72707..ae8f06827 100644
--- a/numpy/core/include/numpy/ufuncobject.h
+++ b/numpy/core/include/numpy/ufuncobject.h
@@ -9,7 +9,41 @@ extern "C" {
typedef void (*PyUFuncGenericFunction) (char **, npy_intp *, npy_intp *, void *);
-typedef struct {
+/* Forward declaration for the type resolution function */
+struct _tagPyUFuncObject;
+
+/*
+ * Given the operands for calling a ufunc, should determine the
+ * calculation input and output data types and return an inner loop function.
+ * This function should validate that the casting rule is being followed,
+ * and fail if it is not.
+ *
+ * ufunc: The ufunc object.
+ * casting: The 'casting' parameter provided to the ufunc.
+ * operands: An array of length (ufunc->nin + ufunc->nout),
+ * with the output parameters possibly NULL.
+ * type_tup: Either NULL, or the type_tup passed to the ufunc.
+ * out_dtypes: An array which should be populated with new
+ * references to (ufunc->nin + ufunc->nout) new
+ * dtypes, one for each input and output.
+ * out_innerloop: Should be populated with the correct ufunc inner
+ * loop for the given type.
+ * out_innerloopdata: Should be populated with the void* data to
+ * be passed into the out_innerloop function.
+ *
+ * Should return 0 on success, -1 on failure (with exception set),
+ * or -2 if Py_NotImplemented should be returned.
+ */
+typedef int (PyUFunc_TypeResolutionFunc)(
+ struct _tagPyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+
+typedef struct _tagPyUFuncObject {
PyObject_HEAD
/*
* nin: Number of inputs
@@ -70,6 +104,13 @@ typedef struct {
int *core_offsets;
/* signature string for printing purpose */
char *core_signature;
+
+ /*
+ * A function which resolves the types and returns an inner loop.
+ * This is used by the regular ufunc, the reduction operations
+ * have a different set of rules.
+ */
+ PyUFunc_TypeResolutionFunc *type_resolution_function;
} PyUFuncObject;
#include "arrayobject.h"
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py
index 55ab74168..67235e4d1 100644
--- a/numpy/core/numeric.py
+++ b/numpy/core/numeric.py
@@ -1332,7 +1332,7 @@ def array_repr(arr, max_line_width=None, precision=None, suppress_small=None):
if typeless and arr.size:
return cName + "(%s)" % lst
else:
- typename=arr.dtype.name
+ typename="'%s'" % arr.dtype.name
lf = ''
if issubclass(arr.dtype.type, flexible):
if arr.dtype.names:
diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py
index 7bdfd98c1..6d655cb17 100644
--- a/numpy/core/numerictypes.py
+++ b/numpy/core/numerictypes.py
@@ -39,8 +39,9 @@ Exported symbols include:
longfloat, clongfloat,
- datetime_, timedelta_, (these inherit from timeinteger which inherits
- from signedinteger)
+ datetime_
+ timedelta_, (this inherits from from signedinteger, as it is
+ a signed integer with an associated time unit)
As part of the type-hierarchy: xx -- is bit-width
@@ -91,9 +92,10 @@ Exported symbols include:
__all__ = ['sctypeDict', 'sctypeNA', 'typeDict', 'typeNA', 'sctypes',
'ScalarType', 'obj2sctype', 'cast', 'nbytes', 'sctype2char',
'maximum_sctype', 'issctype', 'typecodes', 'find_common_type',
- 'issubdtype']
+ 'issubdtype','datetime_data','datetime_as_string']
-from numpy.core.multiarray import typeinfo, ndarray, array, empty, dtype
+from numpy.core.multiarray import typeinfo, ndarray, array, \
+ empty, dtype, datetime_data, datetime_as_string
import types as _types
import sys
@@ -257,6 +259,10 @@ def bitname(obj):
char = 'O'
base = 'object'
bits = 0
+ elif name=='datetime64':
+ char = 'M'
+ elif name=='timedelta64':
+ char = 'm'
if sys.version_info[0] >= 3:
if name=='bytes_':
diff --git a/numpy/core/src/multiarray/_datetime.h b/numpy/core/src/multiarray/_datetime.h
index 9be7e5673..a91953787 100644
--- a/numpy/core/src/multiarray/_datetime.h
+++ b/numpy/core/src/multiarray/_datetime.h
@@ -2,17 +2,338 @@
#define _NPY_PRIVATE__DATETIME_H_
NPY_NO_EXPORT void
-PyArray_DatetimeToDatetimeStruct(npy_datetime val, NPY_DATETIMEUNIT fr,
- npy_datetimestruct *result);
+numpy_pydatetime_import();
+/*
+ * This function returns the a new reference to the
+ * capsule with the datetime metadata.
+ */
+NPY_NO_EXPORT PyObject *
+get_datetime_metacobj_from_dtype(PyArray_Descr *dtype);
+
+/*
+ * This function returns a pointer to the DateTimeMetaData
+ * contained within the provided datetime dtype.
+ */
+NPY_NO_EXPORT PyArray_DatetimeMetaData *
+get_datetime_metadata_from_dtype(PyArray_Descr *dtype);
+
+/*
+ * Both type1 and type2 must be either NPY_DATETIME or NPY_TIMEDELTA.
+ * Applies the type promotion rules between the two types, returning
+ * the promoted type.
+ */
+NPY_NO_EXPORT PyArray_Descr *
+datetime_type_promotion(PyArray_Descr *type1, PyArray_Descr *type2);
+
+/*
+ * Converts a datetime from a datetimestruct to a datetime based
+ * on some metadata.
+ */
+NPY_NO_EXPORT int
+convert_datetimestruct_to_datetime(PyArray_DatetimeMetaData *meta,
+ const npy_datetimestruct *dts,
+ npy_datetime *out);
+
+/*
+ * Parses the metadata string into the metadata C structure.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+parse_datetime_metadata_from_metastr(char *metastr, Py_ssize_t len,
+ PyArray_DatetimeMetaData *out_meta);
+
+
+/*
+ * This function returns a reference to a capsule
+ * which contains the datetime metadata parsed from a metadata
+ * string. 'metastr' should be NULL-terminated, and len should
+ * contain its string length.
+ */
+NPY_NO_EXPORT PyObject *
+parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len);
+
+/*
+ * Converts a datetype dtype string into a dtype descr object.
+ * The "type" string should be NULL-terminated, and len should
+ * contain its string length.
+ */
+NPY_NO_EXPORT PyArray_Descr *
+parse_dtype_from_datetime_typestr(char *typestr, Py_ssize_t len);
+
+/*
+ * Creates a new NPY_TIMEDELTA dtype, copying the datetime metadata
+ * from the given dtype.
+ */
+NPY_NO_EXPORT PyArray_Descr *
+timedelta_dtype_with_copied_meta(PyArray_Descr *dtype);
+
+/*
+ * Converts a substring given by 'str' and 'len' into
+ * a date time unit enum value. The 'metastr' parameter
+ * is used for error messages, and may be NULL.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT NPY_DATETIMEUNIT
+parse_datetime_unit_from_string(char *str, Py_ssize_t len, char *metastr);
+
+/*
+ * Translate divisors into multiples of smaller units.
+ * 'metastr' is used for the error message if the divisor doesn't work,
+ * and can be NULL if the metadata didn't come from a string.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta,
+ int den, char *metastr);
+
+/*
+ * Determines whether the 'divisor' metadata divides evenly into
+ * the 'dividend' metadata.
+ */
+NPY_NO_EXPORT npy_bool
+datetime_metadata_divides(
+ PyArray_Descr *dividend,
+ PyArray_Descr *divisor,
+ int strict_with_nonlinear_units);
+
+/*
+ * Computes the GCD of the two date-time metadata values. Raises
+ * an exception if there is no reasonable GCD, such as with
+ * years and days.
+ *
+ * Returns a capsule with the GCD metadata.
+ */
+NPY_NO_EXPORT PyObject *
+compute_datetime_metadata_greatest_common_divisor(
+ PyArray_Descr *type1,
+ PyArray_Descr *type2,
+ int strict_with_nonlinear_units);
+
+/*
+ * Computes the conversion factor to convert data with 'src_meta' metadata
+ * into data with 'dst_meta' metadata, not taking into account the events.
+ *
+ * To convert a npy_datetime or npy_timedelta, first the event number needs
+ * to be divided away, then it needs to be scaled by num/denom, and
+ * finally the event number can be added back in.
+ *
+ * If overflow occurs, both out_num and out_denom are set to 0, but
+ * no error is set.
+ */
+NPY_NO_EXPORT void
+get_datetime_conversion_factor(PyArray_DatetimeMetaData *src_meta,
+ PyArray_DatetimeMetaData *dst_meta,
+ npy_int64 *out_num, npy_int64 *out_denom);
+
+/*
+ * Given an the capsule datetime metadata object,
+ * returns a tuple for pickling and other purposes.
+ */
+NPY_NO_EXPORT PyObject *
+convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta);
+
+/*
+ * Converts a metadata tuple into a datetime metadata C struct.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple,
+ PyArray_DatetimeMetaData *out_meta);
+
+/*
+ * Given a tuple representing datetime metadata,
+ * returns a capsule datetime metadata object.
+ */
+NPY_NO_EXPORT PyObject *
+convert_datetime_metadata_tuple_to_metacobj(PyObject *tuple);
+
+/*
+ * Converts an input object into datetime metadata. The input
+ * may be either a string or a tuple.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+convert_pyobject_to_datetime_metadata(PyObject *obj,
+ PyArray_DatetimeMetaData *out_meta);
+
+/*
+ * 'ret' is a PyUString containing the datetime string, and this
+ * function appends the metadata string to it.
+ *
+ * If 'skip_brackets' is true, skips the '[]' when events == 1.
+ *
+ * This function steals the reference 'ret'
+ */
+NPY_NO_EXPORT PyObject *
+append_metastr_to_string(PyArray_DatetimeMetaData *meta,
+ int skip_brackets,
+ PyObject *ret);
+
+/*
+ * Provides a string length to use for converting datetime
+ * objects with the given local and unit settings.
+ */
+NPY_NO_EXPORT int
+get_datetime_iso_8601_strlen(int local, NPY_DATETIMEUNIT base);
+
+
+/*
+ * Parses (almost) standard ISO 8601 date strings. The differences are:
+ *
+ * + The date "20100312" is parsed as the year 20100312, not as
+ * equivalent to "2010-03-12". The '-' in the dates are not optional.
+ * + Only seconds may have a decimal point, with up to 18 digits after it
+ * (maximum attoseconds precision).
+ * + Either a 'T' as in ISO 8601 or a ' ' may be used to separate
+ * the date and the time. Both are treated equivalently.
+ *
+ * 'str' must be a NULL-terminated string, and 'len' must be its length.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+parse_iso_8601_date(char *str, int len, npy_datetimestruct *out);
+
+/*
+ * Converts an npy_datetimestruct to an (almost) ISO 8601
+ * NULL-terminated string.
+ *
+ * If 'local' is non-zero, it produces a string in local time with
+ * a +-#### timezone offset, otherwise it uses timezone Z (UTC).
+ *
+ * 'base' restricts the output to that unit. Set 'base' to
+ * -1 to auto-detect a base after which all the values are zero.
+ *
+ * 'tzoffset' is used if 'local' is enabled, and 'tzoffset' is
+ * set to a value other than -1. This is a manual override for
+ * the local time zone to use, as an offset in minutes.
+ *
+ * Returns 0 on success, -1 on failure (for example if the output
+ * string was too short).
+ */
+NPY_NO_EXPORT int
+make_iso_8601_date(npy_datetimestruct *dts, char *outstr, int outlen,
+ int local, NPY_DATETIMEUNIT base, int tzoffset);
+
+
+/*
+ * Tests for and converts a Python datetime.datetime or datetime.date
+ * object into a NumPy npy_datetimestruct.
+ *
+ * Returns -1 on error, 0 on success, and 1 (with no error set)
+ * if obj doesn't have the neeeded date or datetime attributes.
+ */
+NPY_NO_EXPORT int
+convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out);
+
+/*
+ * Converts a PyObject * into a datetime, in any of the input forms supported.
+ *
+ * Returns -1 on error, 0 on success.
+ */
+NPY_NO_EXPORT int
+convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj,
+ npy_datetime *out);
+
+/*
+ * Converts a PyObject * into a timedelta, in any of the forms supported
+ *
+ * Returns -1 on error, 0 on success.
+ */
+NPY_NO_EXPORT int
+convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj,
+ npy_timedelta *out);
+
+
+/*
+ * Converts a datetime into a PyObject *.
+ *
+ * For days or coarser, returns a datetime.date.
+ * For microseconds or coarser, returns a datetime.datetime.
+ * For units finer than microseconds, returns an integer.
+ */
+NPY_NO_EXPORT PyObject *
+convert_datetime_to_pyobject(npy_datetime dt, PyArray_DatetimeMetaData *meta);
+
+/*
+ * Converts a timedelta into a PyObject *.
+ *
+ * Not-a-time is returned as the string "NaT".
+ * For microseconds or coarser, returns a datetime.timedelta.
+ * For units finer than microseconds, returns an integer.
+ */
+NPY_NO_EXPORT PyObject *
+convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta);
+
+/*
+ * Converts a datetime based on the given metadata into a datetimestruct
+ */
+NPY_NO_EXPORT int
+convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
+ npy_datetime dt,
+ npy_datetimestruct *out);
+
+/*
+ * Converts a datetime from a datetimestruct to a datetime based
+ * on some metadata. The date is assumed to be valid.
+ *
+ * TODO: If meta->num is really big, there could be overflow
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+convert_datetimestruct_to_datetime(PyArray_DatetimeMetaData *meta,
+ const npy_datetimestruct *dts,
+ npy_datetime *out);
+
+/*
+ * Adjusts a datetimestruct based on a seconds offset. Assumes
+ * the current values are valid.
+ */
+NPY_NO_EXPORT void
+add_seconds_to_datetimestruct(npy_datetimestruct *dts, int seconds);
+
+/*
+ * Adjusts a datetimestruct based on a minutes offset. Assumes
+ * the current values are valid.
+ */
NPY_NO_EXPORT void
-PyArray_TimedeltaToTimedeltaStruct(npy_timedelta val, NPY_DATETIMEUNIT fr,
- npy_timedeltastruct *result);
+add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes);
+
+/*
+ * Returns true if the datetime metadata matches
+ */
+NPY_NO_EXPORT npy_bool
+has_equivalent_datetime_metadata(PyArray_Descr *type1, PyArray_Descr *type2);
-NPY_NO_EXPORT npy_datetime
-PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d);
+/*
+ * Casts a single datetime from having src_meta metadata into
+ * dst_meta metadata.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+cast_datetime_to_datetime(PyArray_DatetimeMetaData *src_meta,
+ PyArray_DatetimeMetaData *dst_meta,
+ npy_datetime src_dt,
+ npy_datetime *dst_dt);
-NPY_NO_EXPORT npy_datetime
-PyArray_TimedeltaStructToTimedelta(NPY_DATETIMEUNIT fr, npy_timedeltastruct *d);
+/*
+ * Casts a single timedelta from having src_meta metadata into
+ * dst_meta metadata.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+cast_timedelta_to_timedelta(PyArray_DatetimeMetaData *src_meta,
+ PyArray_DatetimeMetaData *dst_meta,
+ npy_timedelta src_dt,
+ npy_timedelta *dst_dt);
#endif
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c
index a3008c888..ba4b2fc59 100644
--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -340,8 +340,7 @@ array_repr_builtin(PyArrayObject *self, int repr)
max_n = PyArray_NBYTES(self)*4*sizeof(char) + 7;
if ((string = (char *)_pya_malloc(max_n)) == NULL) {
- PyErr_SetString(PyExc_MemoryError, "out of memory");
- return NULL;
+ return PyErr_NoMemory();
}
if (repr) {
@@ -380,7 +379,6 @@ 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.
@@ -407,17 +405,13 @@ PyArray_SetStringFunction(PyObject *op, int repr)
}
/*NUMPY_API
- * Set the date time print function to be a Python function.
+ * This function is scheduled to be removed
+ *
+ * TO BE REMOVED - NOT USED INTERNALLY.
*/
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;
}
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index fde95c4cb..e55c7c133 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -1,7 +1,6 @@
/* -*- c -*- */
#define PY_SSIZE_T_CLEAN
#include "Python.h"
-#include "datetime.h"
#include "structmember.h"
#define _MULTIARRAYMODULE
@@ -106,8 +105,6 @@ MyPyLong_AsUnsigned@Type@ (PyObject *obj)
*/
-static char * _SEQUENCE_MESSAGE = "error setting an array element with a sequence";
-
/**begin repeat
*
* #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, LONG, UINT, ULONG,
@@ -764,459 +761,102 @@ fail:
return -1;
}
-/*
- * Acknowledgement: Example code contributed by Marty Fuhr sponsored by
- * Google Summer of Code 2009 was used to integrate and adapt the mxDateTime
- * parser
- */
-
-/* #include "datetime.c" --- now included in multiarray_onefile */
-
-
-/* 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));
- /* This resets meta->events for recursive call */
- meta->events = 1;
- 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);
-}
-
+static PyObject *
+DATETIME_getitem(char *ip, PyArrayObject *ap) {
+ npy_datetime dt;
+ PyArray_DatetimeMetaData *meta = NULL;
-NPY_NO_EXPORT PyObject *
-PyTimeDelta_FromInt64(timedelta val, PyArray_Descr *descr)
-{
- PyArray_DatetimeMetaData *meta;
- meta = PyDataType_GetDatetimeMetaData(descr);
+ /* Get the datetime units metadata */
+ meta = get_datetime_metadata_from_dtype(PyArray_DESCR(ap));
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));
- /* This resets meta->events for recursive call */
- meta->events = 1;
- 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);
+ if ((ap == NULL) || PyArray_ISBEHAVED_RO(ap)) {
+ dt = *((npy_datetime *)ip);
}
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;
+ ap->descr->f->copyswap(&dt, ip, !PyArray_ISNOTSWAPPED(ap), ap);
}
- 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);
+ return convert_datetime_to_pyobject(dt, meta);
}
-/*
- * 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;
- int events;
-
- if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) {
- PyErr_SetString(PyExc_ValueError,
- "need a 2-tuple on setting if events > 1");
- return -1;
- }
- /* Alter the dictionary and call again */
- /* FIXME: not thread safe */
- events = meta->events;
- meta->events = 1;
- tmp = PyDateTime_AsInt64(PyTuple_GET_ITEM(obj, 0), descr);
- meta->events = events;
- if (PyErr_Occurred()) {
- return -1;
- }
- /* FIXME: Check for overflow */
- tmp *= 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;
+static PyObject *
+TIMEDELTA_getitem(char *ip, PyArrayObject *ap) {
+ npy_timedelta td;
+ PyArray_DatetimeMetaData *meta = NULL;
- meta = PyDataType_GetDatetimeMetaData(descr);
+ /* Get the datetime units metadata */
+ meta = get_datetime_metadata_from_dtype(PyArray_DESCR(ap));
if (meta == NULL) {
- PyErr_SetString(PyExc_RuntimeError,
- "metadata not set for descriptor");
- return -1;
- }
-
- if (meta->events > 1) {
- timedelta tmp;
- int events;
-
- if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) {
- PyErr_SetString(PyExc_ValueError,
- "need a 2-tuple on setting if events > 1");
- return -1;
- }
- /* Alter the dictionary and call again (not thread safe) */
- events = meta->events;
- meta->events = 1;
- tmp = PyTimeDelta_AsInt64(PyTuple_GET_ITEM(obj, 0), descr);
- meta->events = events;
- if (PyErr_Occurred()) {
- return -1;
- }
- /* FIXME: Check for overflow */
- tmp *= events;
- tmp += MyPyLong_AsLongLong(PyTuple_GET_ITEM(obj, 1));
- if (PyErr_Occurred()) {
- return -1;
- }
- return tmp;
+ return NULL;
}
- 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);
+ td = *((npy_timedelta *)ip);
}
else {
- ap->descr->f->copyswap(&t1, ip, !PyArray_ISNOTSWAPPED(ap), ap);
- return PyDateTime_FromInt64((datetime)t1, ap->descr);
+ ap->descr->f->copyswap(&td, ip, !PyArray_ISNOTSWAPPED(ap), ap);
}
-}
-
-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);
- }
+ return convert_timedelta_to_pyobject(td, meta);
}
-/* FIXME:
- * This needs to take
- * 1) Integers and Longs (anything that can be converted to an Int)
- * 2) Strings (ISO-style dates)
- * 3) Datetime Scalars (that it converts based on scalar dtype.
- * 4) Datetime and Date objects
- * Plus a tuple for meta->events > 1
- *
- * 3) is partially implemented, 4) is implemented
- */
-
static int
DATETIME_setitem(PyObject *op, char *ov, PyArrayObject *ap) {
/* ensure alignment */
- datetime temp;
+ npy_datetime temp = 0;
+ PyArray_DatetimeMetaData *meta = NULL;
- if (PyArray_IsScalar(op, Datetime)) {
- /* This needs to convert based on type */
- temp = ((PyDatetimeScalarObject *)op)->obval;
+ /* Get the datetime units metadata */
+ meta = get_datetime_metadata_from_dtype(PyArray_DESCR(ap));
+ if (meta == NULL) {
+ return -1;
}
-#if defined(NPY_PY3K)
- else if (PyUString_Check(op)) {
-#else
- else if (PyUString_Check(op) || PyUnicode_Check(op)) {
-#endif
- /* FIXME: Converts to DateTime first and therefore does not handle extended notation */
- /* import _mx_datetime_parser
- * res = _mx_datetime_parser(name)
- * Convert from datetime to Int
- */
- PyObject *res, *module;
- module = PyImport_ImportModule("numpy.core._mx_datetime_parser");
- if (module == NULL) { return -1; }
- res = PyObject_CallMethod(module, "datetime_from_string", "O", op);
- Py_DECREF(module);
- if (res == NULL) { return -1; }
- temp = PyDateTime_AsInt64(res, ap->descr);
- Py_DECREF(res);
- if (PyErr_Occurred()) return -1;
- }
- else if (PyInt_Check(op)) {
- temp = PyInt_AS_LONG(op);
- }
- else if (PyLong_Check(op)) {
- temp = PyLong_AsLongLong(op);
- }
- else {
- temp = PyDateTime_AsInt64(op, ap->descr);
- }
- if (PyErr_Occurred()) {
- if (PySequence_Check(op)) {
- PyErr_Clear();
- PyErr_SetString(PyExc_ValueError, _SEQUENCE_MESSAGE);
- }
+ /* Convert the object into a NumPy datetime */
+ if (convert_pyobject_to_datetime(meta, op, &temp) < 0) {
return -1;
}
- if (ap == NULL || PyArray_ISBEHAVED(ap))
- *((datetime *)ov)=temp;
+
+ /* Copy the value into the output */
+ if (ap == NULL || PyArray_ISBEHAVED(ap)) {
+ *((npy_datetime *)ov)=temp;
+ }
else {
ap->descr->f->copyswap(ov, &temp, !PyArray_ISNOTSWAPPED(ap), ap);
}
+
return 0;
}
-/* FIXME: This needs to take
- * 1) Integers and Longs (anything that can be converted to an Int)
- * 2) Timedelta scalar objects (with resolution conversion)
- * 3) Python Timedelta objects
- *
- * Plus a tuple for meta->events > 1
- */
-
static int
TIMEDELTA_setitem(PyObject *op, char *ov, PyArrayObject *ap) {
/* ensure alignment */
- timedelta temp;
+ npy_timedelta temp = 0;
+ PyArray_DatetimeMetaData *meta = NULL;
- if (PyArray_IsScalar(op, Timedelta)) {
- temp = ((PyTimedeltaScalarObject *)op)->obval;
- }
- else if (PyInt_Check(op)) {
- temp = PyInt_AS_LONG(op);
- }
- else if (PyLong_Check(op)) {
- temp = PyLong_AsLongLong(op);
- }
- else {
- temp = PyTimeDelta_AsInt64(op, ap->descr);
+ /* Get the datetime units metadata */
+ meta = get_datetime_metadata_from_dtype(PyArray_DESCR(ap));
+ if (meta == NULL) {
+ return -1;
}
- if (PyErr_Occurred()) {
- if (PySequence_Check(op)) {
- PyErr_Clear();
- PyErr_SetString(PyExc_ValueError, _SEQUENCE_MESSAGE);
- }
+
+ /* Convert the object into a NumPy datetime */
+ if (convert_pyobject_to_timedelta(meta, op, &temp) < 0) {
return -1;
}
- if (ap == NULL || PyArray_ISBEHAVED(ap))
- *((timedelta *)ov)=temp;
+
+ /* Copy the value into the output */
+ if (ap == NULL || PyArray_ISBEHAVED(ap)) {
+ *((npy_timedelta *)ov)=temp;
+ }
else {
ap->descr->f->copyswap(ov, &temp, !PyArray_ISNOTSWAPPED(ap), ap);
}
+
return 0;
}
@@ -3796,7 +3436,6 @@ _init_datetime_descr(PyArray_Descr *descr)
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;
/* FIXME
@@ -4128,7 +3767,6 @@ 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/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c
index e8ac99273..12f2c731b 100644
--- a/numpy/core/src/multiarray/conversion_utils.c
+++ b/numpy/core/src/multiarray/conversion_utils.c
@@ -628,119 +628,148 @@ PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals)
NPY_NO_EXPORT int
PyArray_TypestrConvert(int itemsize, int gentype)
{
- int newtype = gentype;
+ int newtype = NPY_NOTYPE;
- if (gentype == PyArray_GENBOOLLTR) {
- if (itemsize == 1) {
- newtype = PyArray_BOOL;
- }
- else {
- newtype = PyArray_NOTYPE;
- }
- }
- else if (gentype == PyArray_SIGNEDLTR) {
- switch(itemsize) {
- case 1:
- newtype = PyArray_INT8;
- break;
- case 2:
- newtype = PyArray_INT16;
- break;
- case 4:
- newtype = PyArray_INT32;
- break;
- case 8:
- newtype = PyArray_INT64;
+ switch (gentype) {
+ case NPY_GENBOOLLTR:
+ if (itemsize == 1) {
+ newtype = NPY_BOOL;
+ }
break;
+
+ case NPY_SIGNEDLTR:
+ switch(itemsize) {
+ case 1:
+ newtype = NPY_INT8;
+ break;
+ case 2:
+ newtype = NPY_INT16;
+ break;
+ case 4:
+ newtype = NPY_INT32;
+ break;
+ case 8:
+ newtype = NPY_INT64;
+ break;
#ifdef PyArray_INT128
- case 16:
- newtype = PyArray_INT128;
- break;
+ case 16:
+ newtype = NPY_INT128;
+ break;
#endif
- default:
- newtype = PyArray_NOTYPE;
- }
- }
- else if (gentype == PyArray_UNSIGNEDLTR) {
- switch(itemsize) {
- case 1:
- newtype = PyArray_UINT8;
- break;
- case 2:
- newtype = PyArray_UINT16;
- break;
- case 4:
- newtype = PyArray_UINT32;
- break;
- case 8:
- newtype = PyArray_UINT64;
+ }
break;
+
+ case NPY_UNSIGNEDLTR:
+ switch(itemsize) {
+ case 1:
+ newtype = NPY_UINT8;
+ break;
+ case 2:
+ newtype = NPY_UINT16;
+ break;
+ case 4:
+ newtype = NPY_UINT32;
+ break;
+ case 8:
+ newtype = NPY_UINT64;
+ break;
#ifdef PyArray_INT128
- case 16:
- newtype = PyArray_UINT128;
- break;
+ case 16:
+ newtype = NPY_UINT128;
+ break;
#endif
- default:
- newtype = PyArray_NOTYPE;
- break;
- }
- }
- else if (gentype == PyArray_FLOATINGLTR) {
- switch(itemsize) {
- case 2:
- newtype = PyArray_FLOAT16;
- break;
- case 4:
- newtype = PyArray_FLOAT32;
- break;
- case 8:
- newtype = PyArray_FLOAT64;
+ }
break;
+
+ case NPY_FLOATINGLTR:
+ switch(itemsize) {
+ case 2:
+ newtype = NPY_FLOAT16;
+ break;
+ case 4:
+ newtype = NPY_FLOAT32;
+ break;
+ case 8:
+ newtype = NPY_FLOAT64;
+ break;
#ifdef PyArray_FLOAT80
- case 10:
- newtype = PyArray_FLOAT80;
- break;
+ case 10:
+ newtype = NPY_FLOAT80;
+ break;
#endif
#ifdef PyArray_FLOAT96
- case 12:
- newtype = PyArray_FLOAT96;
- break;
+ case 12:
+ newtype = NPY_FLOAT96;
+ break;
#endif
#ifdef PyArray_FLOAT128
- case 16:
- newtype = PyArray_FLOAT128;
- break;
+ case 16:
+ newtype = NPY_FLOAT128;
+ break;
#endif
- default:
- newtype = PyArray_NOTYPE;
- }
- }
- else if (gentype == PyArray_COMPLEXLTR) {
- switch(itemsize) {
- case 8:
- newtype = PyArray_COMPLEX64;
- break;
- case 16:
- newtype = PyArray_COMPLEX128;
+ }
break;
+
+ case NPY_COMPLEXLTR:
+ switch(itemsize) {
+ case 8:
+ newtype = NPY_COMPLEX64;
+ break;
+ case 16:
+ newtype = NPY_COMPLEX128;
+ break;
#ifdef PyArray_FLOAT80
- case 20:
- newtype = PyArray_COMPLEX160;
- break;
+ case 20:
+ newtype = NPY_COMPLEX160;
+ break;
#endif
#ifdef PyArray_FLOAT96
- case 24:
- newtype = PyArray_COMPLEX192;
- break;
+ case 24:
+ newtype = NPY_COMPLEX192;
+ break;
#endif
#ifdef PyArray_FLOAT128
- case 32:
- newtype = PyArray_COMPLEX256;
- break;
+ case 32:
+ newtype = NPY_COMPLEX256;
+ break;
#endif
- default:
- newtype = PyArray_NOTYPE;
- }
+ }
+ break;
+
+ case NPY_OBJECTLTR:
+ if (PyErr_WarnEx(PyExc_DeprecationWarning,
+ "DType strings 'O4' and 'O8' are deprecated "
+ "because they are platform specific. Use "
+ "'O' instead", 0) == 0 &&
+ (itemsize == 4 || itemsize == 8)) {
+ newtype = NPY_OBJECT;
+ }
+ break;
+
+ case NPY_STRINGLTR:
+ case NPY_STRINGLTR2:
+ newtype = NPY_STRING;
+ break;
+
+ case NPY_UNICODELTR:
+ newtype = NPY_UNICODE;
+ break;
+
+ case NPY_VOIDLTR:
+ newtype = NPY_VOID;
+ break;
+
+ case NPY_DATETIMELTR:
+ if (itemsize == 8) {
+ newtype = NPY_DATETIME;
+ }
+ break;
+
+ case NPY_TIMEDELTALTR:
+ if (itemsize == 8) {
+ newtype = NPY_TIMEDELTA;
+ }
+ break;
}
return newtype;
}
diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c
index ee22ba646..73d65c12d 100644
--- a/numpy/core/src/multiarray/convert_datatype.c
+++ b/numpy/core/src/multiarray/convert_datatype.c
@@ -16,6 +16,7 @@
#include "mapping.h"
#include "convert_datatype.h"
+#include "_datetime.h"
/*NUMPY_API
* For backward compatibility
@@ -234,20 +235,30 @@ PyArray_CanCastTo(PyArray_Descr *from, PyArray_Descr *to)
ret = (npy_bool) PyArray_CanCastSafely(fromtype, totype);
if (ret) {
/* Check String and Unicode more closely */
- if (fromtype == PyArray_STRING) {
- if (totype == PyArray_STRING) {
+ if (fromtype == NPY_STRING) {
+ if (totype == NPY_STRING) {
ret = (from->elsize <= to->elsize);
}
- else if (totype == PyArray_UNICODE) {
+ else if (totype == NPY_UNICODE) {
ret = (from->elsize << 2 <= to->elsize);
}
}
- else if (fromtype == PyArray_UNICODE) {
- if (totype == PyArray_UNICODE) {
+ else if (fromtype == NPY_UNICODE) {
+ if (totype == NPY_UNICODE) {
ret = (from->elsize <= to->elsize);
}
}
/*
+ * For datetime/timedelta, only treat casts moving towards
+ * more precision as safe.
+ */
+ else if (fromtype == NPY_DATETIME && totype == NPY_DATETIME) {
+ return datetime_metadata_divides(from, to, 0);
+ }
+ else if (fromtype == NPY_TIMEDELTA && totype == NPY_TIMEDELTA) {
+ return datetime_metadata_divides(from, to, 1);
+ }
+ /*
* TODO: If totype is STRING or unicode
* see if the length is long enough to hold the
* stringified value of the object.
@@ -289,9 +300,12 @@ dtype_kind_to_ordering(char kind)
/* Object kind */
case 'O':
return 9;
- /* Anything else - ideally shouldn't happen... */
+ /*
+ * Anything else, like datetime, is special cased to
+ * not fit in this hierarchy
+ */
default:
- return 10;
+ return -1;
}
}
@@ -359,17 +373,35 @@ PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to,
return ret;
}
- switch (casting) {
- case NPY_NO_CASTING:
- return (from->elsize == to->elsize) &&
- PyArray_ISNBO(from->byteorder) ==
- PyArray_ISNBO(to->byteorder);
- case NPY_EQUIV_CASTING:
- return (from->elsize == to->elsize);
- case NPY_SAFE_CASTING:
- return (from->elsize <= to->elsize);
+ switch (from->type_num) {
+ case NPY_DATETIME:
+ case NPY_TIMEDELTA:
+ switch (casting) {
+ case NPY_NO_CASTING:
+ return PyArray_ISNBO(from->byteorder) ==
+ PyArray_ISNBO(to->byteorder) &&
+ has_equivalent_datetime_metadata(from, to);
+ case NPY_EQUIV_CASTING:
+ return has_equivalent_datetime_metadata(from, to);
+ case NPY_SAFE_CASTING:
+ return datetime_metadata_divides(from, to,
+ from->type_num == NPY_TIMEDELTA);
+ default:
+ return 1;
+ }
+ break;
default:
- return 1;
+ switch (casting) {
+ case NPY_NO_CASTING:
+ return PyArray_EquivTypes(from, to);
+ case NPY_EQUIV_CASTING:
+ return (from->elsize == to->elsize);
+ case NPY_SAFE_CASTING:
+ return (from->elsize <= to->elsize);
+ default:
+ return 1;
+ }
+ break;
}
}
/* If safe or same-kind casts are allowed */
@@ -381,9 +413,15 @@ PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to,
/*
* Also allow casting from lower to higher kinds, according
* to the ordering provided by dtype_kind_to_ordering.
+ * Some kinds, like datetime, don't fit in the hierarchy,
+ * and are special cased as -1.
*/
- return dtype_kind_to_ordering(from->kind) <=
- dtype_kind_to_ordering(to->kind);
+ int from_order, to_order;
+
+ from_order = dtype_kind_to_ordering(from->kind);
+ to_order = dtype_kind_to_ordering(to->kind);
+
+ return from_order != -1 && from_order <= to_order;
}
else {
return 0;
@@ -542,7 +580,10 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2)
/* If they're built-in types, use the promotion table */
if (type_num1 < NPY_NTYPES && type_num2 < NPY_NTYPES) {
ret_type_num = _npy_type_promotion_table[type_num1][type_num2];
- /* The table doesn't handle string/unicode/void, check the result */
+ /*
+ * The table doesn't handle string/unicode/void/datetime/timedelta,
+ * so check the result
+ */
if (ret_type_num >= 0) {
return PyArray_DescrFromType(ret_type_num);
}
@@ -647,10 +688,15 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2)
}
switch (type_num1) {
- /* BOOL can convert to anything */
+ /* BOOL can convert to anything except datetime/void */
case NPY_BOOL:
- Py_INCREF(type2);
- return type2;
+ if (type_num2 != NPY_DATETIME && type_num2 != NPY_VOID) {
+ Py_INCREF(type2);
+ return type2;
+ }
+ else {
+ break;
+ }
/* For strings and unicodes, take the larger size */
case NPY_STRING:
if (type_num2 == NPY_STRING) {
@@ -713,13 +759,25 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2)
return type1;
}
break;
+ case NPY_DATETIME:
+ case NPY_TIMEDELTA:
+ if (type_num2 == NPY_DATETIME || type_num2 == NPY_TIMEDELTA) {
+ return datetime_type_promotion(type1, type2);
+ }
+ break;
}
switch (type_num2) {
- /* BOOL can convert to anything */
+ /* BOOL can convert to almost anything */
case NPY_BOOL:
- Py_INCREF(type1);
- return type1;
+ if (type_num1 != NPY_DATETIME && type_num1 != NPY_TIMEDELTA &&
+ type_num1 != NPY_VOID) {
+ Py_INCREF(type1);
+ return type1;
+ }
+ else {
+ break;
+ }
case NPY_STRING:
/* Allow NUMBER -> STRING */
if (PyTypeNum_ISNUMBER(type_num1)) {
@@ -733,6 +791,19 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2)
return type2;
}
break;
+ case NPY_DATETIME:
+ if (PyTypeNum_ISINTEGER(type_num1)) {
+ Py_INCREF(type2);
+ return type2;
+ }
+ break;
+ case NPY_TIMEDELTA:
+ if (PyTypeNum_ISINTEGER(type_num1) ||
+ PyTypeNum_ISFLOAT(type_num1)) {
+ Py_INCREF(type2);
+ return type2;
+ }
+ break;
}
/* For equivalent types we can return either */
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index bbbf91f36..c4980d6c0 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -950,6 +950,9 @@ PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
return NULL;
}
PyArray_DESCR_REPLACE(descr);
+ if (descr == NULL) {
+ return NULL;
+ }
if (descr->type_num == NPY_STRING) {
sd = descr->elsize = 1;
}
@@ -2787,11 +2790,11 @@ PyArray_CopyInto(PyArrayObject *dst, PyArrayObject *src)
/*NUMPY_API
- PyArray_CheckAxis
-
- check that axis is valid
- convert 0-d arrays to 1-d arrays
-*/
+ * PyArray_CheckAxis
+ *
+ * check that axis is valid
+ * convert 0-d arrays to 1-d arrays
+ */
NPY_NO_EXPORT PyObject *
PyArray_CheckAxis(PyArrayObject *arr, int *axis, int flags)
{
diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c
index ad1d98270..b84a14fde 100644
--- a/numpy/core/src/multiarray/datetime.c
+++ b/numpy/core/src/multiarray/datetime.c
@@ -5,34 +5,53 @@
#include <time.h>
#define _MULTIARRAYMODULE
-#define NPY_NO_PREFIX
-#include <numpy/ndarrayobject.h>
+#include <numpy/arrayobject.h>
#include "npy_config.h"
#include "numpy/npy_3kcompat.h"
+#include "numpy/arrayscalars.h"
#include "_datetime.h"
-/* For defaults and errors */
-#define NPY_FR_ERR -1
+/*
+ * Imports the PyDateTime functions so we can create these objects.
+ * This is called during module initialization
+ */
+NPY_NO_EXPORT void
+numpy_pydatetime_import()
+{
+ PyDateTime_IMPORT;
+}
-/* Offset for number of days between Dec 31, 1969 and Jan 1, 0001
-* Assuming Gregorian calendar was always in effect (proleptic Gregorian calendar)
-*/
+static int
+is_leapyear(npy_int64 year);
-/* Calendar Structure for Parsing Long -> Date */
-typedef struct {
- int year, month, day;
-} ymdstruct;
-typedef struct {
- int hour, min, sec;
-} hmsstruct;
+/* For defaults and errors */
+#define NPY_FR_ERR -1
+/* Exported as DATETIMEUNITS in multiarraymodule.c */
+NPY_NO_EXPORT char *_datetime_strings[] = {
+ NPY_STR_Y,
+ NPY_STR_M,
+ NPY_STR_W,
+ NPY_STR_B,
+ NPY_STR_D,
+ NPY_STR_h,
+ NPY_STR_m,
+ NPY_STR_s,
+ NPY_STR_ms,
+ NPY_STR_us,
+ NPY_STR_ns,
+ NPY_STR_ps,
+ NPY_STR_fs,
+ NPY_STR_as
+};
/*
====================================================
+ }
== Beginning of section borrowed from mx.DateTime ==
====================================================
*/
@@ -43,28 +62,12 @@ typedef struct {
* 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(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
@@ -84,304 +87,336 @@ day_of_week(npy_longlong absdate)
}
/*
- * 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(npy_longlong year)
+ ====================================================
+ == End of section adapted from mx.DateTime ==
+ ====================================================
+*/
+
+
+static int
+is_leapyear(npy_int64 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;
+ return (year & 0x3) == 0 && /* year % 4 == 0 */
+ ((year % 100) != 0 ||
+ (year % 400) == 0);
}
/*
- * 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
+ * Calculates the days offset from the 1970 epoch.
*/
-static npy_longlong
-days_from_ymd(int year, int month, int day)
+static npy_int64
+get_datetimestruct_days(const npy_datetimestruct *dts)
{
+ int i, month;
+ npy_int64 year, days = 0;
+ int *month_lengths;
- /* 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);
+ year = dts->year - 1970;
+ days = year * 365;
- /* 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;
+ /* Adjust for leap years */
+ if (days >= 0) {
+ /*
+ * 1968 is the closest leap year before 1970.
+ * Exclude the current year, so add 1.
+ */
+ year += 1;
+ /* Add one day for each 4 years */
+ days += year / 4;
+ /* 1900 is the closest previous year divisible by 100 */
+ year += 68;
+ /* Subtract one day for each 100 years */
+ days -= year / 100;
+ /* 1600 is the closest previous year divisible by 400 */
+ year += 300;
+ /* Add one day for each 400 years */
+ days += year / 400;
+ }
+ else {
+ /*
+ * 1972 is the closest later year after 1970.
+ * Include the current year, so subtract 2.
+ */
+ year -= 2;
+ /* Subtract one day for each 4 years */
+ days += year / 4;
+ /* 2000 is the closest later year divisible by 100 */
+ year -= 28;
+ /* Add one day for each 100 years */
+ days -= year / 100;
+ /* 2000 is also the closest later year divisible by 400 */
+ /* Subtract one day for each 400 years */
+ days += year / 400;
+ }
- /*
- * 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;
+ month_lengths = days_in_month[is_leapyear(dts->year)];
+ month = dts->month - 1;
- return absdate;
+ /* Add the months */
+ for (i = 0; i < month; ++i) {
+ days += month_lengths[i];
+ }
- onError:
- return 0;
+ /* Add the days */
+ days += dts->day - 1;
+ return days;
}
-/* Returns absolute seconds from an hour, minute, and second
- */
-#define secs_from_hms(hour, min, sec, multiplier) (\
- ((hour)*3600 + (min)*60 + (sec)) * (npy_int64)(multiplier)\
-)
-
/*
- * 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
+ * Modifies '*days_' to be the day offset within the year,
+ * and returns the year.
*/
-
-static ymdstruct
-days_to_ymdstruct(npy_datetime dlong)
+static npy_int64
+days_to_yearsdays(npy_int64 *days_)
{
- ymdstruct ymd;
- long year;
- npy_longlong yearoffset;
- int leap, dayoffset;
- int month = 1, day = 1;
- int *monthoffset;
+ const npy_int64 days_per_400years = (400*365 + 100 - 4 + 1);
+ /* Adjust so it's relative to the year 2000 (divisible by 400) */
+ npy_int64 days = (*days_) - (365*30 + 7), year;
- 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 down the 400 year cycle to get the year and day within the year */
+ if (days >= 0) {
+ year = 400 * (days / days_per_400years);
+ days = days % days_per_400years;
+ }
+ else {
+ year = 400 * ((days - (days_per_400years - 1)) / days_per_400years);
+ days = days % days_per_400years;
+ if (days < 0) {
+ days += days_per_400years;
}
- break;
}
- /* Now iterate to find the month */
- monthoffset = month_offset[leap];
- for (month = 1; month < 13; month++) {
- if (monthoffset[month] >= dayoffset)
- break;
+ /* Work out the year/day within the 400 year cycle */
+ if (days >= 366) {
+ year += 100 * ((days-1) / (100*365 + 25 - 1));
+ days = (days-1) % (100*365 + 25 - 1);
+ if (days >= 365) {
+ year += 4 * ((days+1) / (4*365 + 1));
+ days = (days+1) % (4*365 + 1);
+ if (days >= 366) {
+ year += (days-1) / 365;
+ days = (days-1) % 365;
+ }
+ }
}
- day = dayoffset - month_offset[leap][month-1];
- ymd.year = year;
- ymd.month = month;
- ymd.day = day;
-
- return ymd;
+ *days_ = days;
+ return year + 2000;
}
/*
- * Converts an integer number of seconds in a day to hours minutes seconds.
- * It assumes seconds is between 0 and 86399.
+ * Fills in the year, month, day in 'dts' based on the days
+ * offset from 1970.
*/
-
-static hmsstruct
-seconds_to_hmsstruct(npy_longlong dlong)
+static void
+set_datetimestruct_days(npy_int64 days, npy_datetimestruct *dts)
{
- int hour, minute, second;
- hmsstruct hms;
+ int *month_lengths, i;
- hour = dlong / 3600;
- minute = (dlong % 3600) / 60;
- second = dlong - (hour*3600 + minute*60);
+ dts->year = days_to_yearsdays(&days);
- hms.hour = hour;
- hms.min = minute;
- hms.sec = second;
-
- return hms;
+ month_lengths = days_in_month[is_leapyear(dts->year)];
+ for (i = 0; i < 12; ++i) {
+ if (days < month_lengths[i]) {
+ dts->month = i + 1;
+ dts->day = days + 1;
+ return;
+ }
+ else {
+ days -= month_lengths[i];
+ }
+ }
}
/*
- ====================================================
- == 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.
+ * Converts a datetime from a datetimestruct to a datetime based
+ * on some metadata. The date is assumed to be valid.
+ *
+ * TODO: If meta->num is really big, there could be overflow
+ *
+ * Returns 0 on success, -1 on failure.
*/
-NPY_NO_EXPORT npy_datetime
-PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d)
+NPY_NO_EXPORT int
+convert_datetimestruct_to_datetime(PyArray_DatetimeMetaData *meta,
+ const npy_datetimestruct *dts,
+ npy_datetime *out)
{
npy_datetime ret;
- npy_longlong days = 0; /* The absolute number of days since Jan 1, 1970 */
+ NPY_DATETIMEUNIT base = meta->base;
- if (fr > NPY_FR_M) {
- days = days_from_ymd(d->year, d->month, d->day);
+ /* If the datetimestruct is NaT, return NaT */
+ if (dts->year == NPY_DATETIME_NAT) {
+ *out = NPY_DATETIME_NAT;
+ return 0;
}
- if (fr == NPY_FR_Y) {
- ret = d->year - 1970;
+
+ if (dts->event < 0 || dts->event >= meta->events) {
+ PyErr_Format(PyExc_ValueError,
+ "NumPy datetime event %d is outside range [0,%d)",
+ (int)dts->event, (int)meta->events);
+ return -1;
}
- else if (fr == NPY_FR_M) {
- ret = (d->year - 1970) * 12 + d->month - 1;
+
+ if (base == NPY_FR_Y) {
+ /* Truncate to the year */
+ ret = dts->year - 1970;
}
- 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 (base == NPY_FR_M) {
+ /* Truncate to the month */
+ ret = 12 * (dts->year - 1970) + (dts->month - 1);
+ }
+ else {
+ /* Otherwise calculate the number of days to start */
+ npy_int64 days = get_datetimestruct_days(dts);
+ int dotw;
+
+ switch (base) {
+ case NPY_FR_W:
+ /* Truncate to weeks */
+ if (days >= 0) {
+ ret = days / 7;
+ }
+ else {
+ ret = (days - 6) / 7;
+ }
+ break;
+ case NPY_FR_B:
+ /* TODO: this needs work... */
+ dotw = day_of_week(days);
+
+ if (dotw > 4) {
+ /* Invalid business day */
+ ret = 0;
+ }
+ else {
+ npy_int64 x;
+ if (days >= 0) {
+ /* offset to adjust first week */
+ x = days - 4;
+ }
+ else {
+ x = days - 2;
+ }
+ ret = 2 + (x / 7) * 5 + x % 7;
+ }
+ break;
+ case NPY_FR_D:
+ ret = days;
+ break;
+ case NPY_FR_h:
+ ret = days * 24 +
+ dts->hour;
+ break;
+ case NPY_FR_m:
+ ret = (days * 24 +
+ dts->hour) * 60 +
+ dts->min;
+ break;
+ case NPY_FR_s:
+ ret = ((days * 24 +
+ dts->hour) * 60 +
+ dts->min) * 60 +
+ dts->sec;
+ break;
+ case NPY_FR_ms:
+ ret = (((days * 24 +
+ dts->hour) * 60 +
+ dts->min) * 60 +
+ dts->sec) * 1000 +
+ dts->us / 1000;
+ break;
+ case NPY_FR_us:
+ ret = (((days * 24 +
+ dts->hour) * 60 +
+ dts->min) * 60 +
+ dts->sec) * 1000000 +
+ dts->us;
+ break;
+ case NPY_FR_ns:
+ ret = ((((days * 24 +
+ dts->hour) * 60 +
+ dts->min) * 60 +
+ dts->sec) * 1000000 +
+ dts->us) * 1000 +
+ dts->ps / 1000;
+ break;
+ case NPY_FR_ps:
+ ret = ((((days * 24 +
+ dts->hour) * 60 +
+ dts->min) * 60 +
+ dts->sec) * 1000000 +
+ dts->us) * 1000000 +
+ dts->ps;
+ break;
+ case NPY_FR_fs:
+ /* only 2.6 hours */
+ ret = (((((days * 24 +
+ dts->hour) * 60 +
+ dts->min) * 60 +
+ dts->sec) * 1000000 +
+ dts->us) * 1000000 +
+ dts->ps) * 1000 +
+ dts->as / 1000;
+ break;
+ case NPY_FR_as:
+ /* only 9.2 secs */
+ ret = (((((days * 24 +
+ dts->hour) * 60 +
+ dts->min) * 60 +
+ dts->sec) * 1000000 +
+ dts->us) * 1000000 +
+ dts->ps) * 1000000 +
+ dts->as;
+ break;
+ default:
+ /* Something got corrupted */
+ PyErr_SetString(PyExc_ValueError,
+ "NumPy datetime metadata with corrupt unit value");
+ return -1;
}
}
- else if (fr == NPY_FR_B) {
- npy_longlong x;
- int dotw = day_of_week(days);
- if (dotw > 4) {
- /* Invalid business day */
- ret = 0;
+ /* Divide by the multiplier */
+ if (meta->num > 1) {
+ if (ret >= 0) {
+ ret /= meta->num;
}
else {
- if (days >= 0) {
- /* offset to adjust first week */
- x = days - 4;
- }
- else {
- x = days - 2;
- }
- ret = 2 + (x / 7) * 5 + x % 7;
+ ret = (ret - meta->num + 1) / meta->num;
}
}
- 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 * (npy_int64)(86400) +
- secs_from_hms(d->hour, d->min, d->sec, 1);
- }
- else if (fr == NPY_FR_ms) {
- ret = days * (npy_int64)(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 *= (npy_int64)(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 *= (npy_int64)(1000 * 1000);
- ret = days * num + secs_from_hms(d->hour, d->min, d->sec, 1000000000)
- + d->us * (npy_int64)(1000) + (d->ps / 1000);
- }
- else if (fr == NPY_FR_ps) {
- npy_int64 num2 = 1000 * 1000;
- npy_int64 num1;
- num2 *= (npy_int64)(1000 * 1000);
- num1 = (npy_int64)(86400) * num2;
- ret = days * num1 + secs_from_hms(d->hour, d->min, d->sec, num2)
- + d->us * (npy_int64)(1000000) + d->ps;
+ /* Add in the event number if needed */
+ if (meta->events > 1) {
+ /* Multiply by the number of events and put in the event number */
+ ret = ret * meta->events + dts->event;
}
- else if (fr == NPY_FR_fs) {
- /* only 2.6 hours */
- npy_int64 num2 = 1000000;
- num2 *= (npy_int64)(1000000);
- num2 *= (npy_int64)(1000);
- /* get number of seconds as a postive or negative number */
- if (days >= 0) {
- ret = secs_from_hms(d->hour, d->min, d->sec, 1);
- }
- else {
- ret = ((d->hour - 24)*3600 + d->min*60 + d->sec);
- }
- ret = ret * num2 + d->us * (npy_int64)(1000000000)
- + d->ps * (npy_int64)(1000) + (d->as / 1000);
- }
- else if (fr == NPY_FR_as) {
- /* only 9.2 secs */
- npy_int64 num1, num2;
+ *out = ret;
- num1 = 1000000;
- num1 *= (npy_int64)(1000000);
- num2 = num1 * (npy_int64)(1000000);
+ return 0;
+}
- if (days >= 0) {
- ret = d->sec;
- }
- else {
- ret = d->sec - 60;
- }
- ret = ret * num2 + d->us * num1 + d->ps * (npy_int64)(1000000)
- + d->as;
- }
- else {
- /* Shouldn't get here */
- PyErr_SetString(PyExc_ValueError, "invalid internal frequency");
- ret = -1;
+/*NUMPY_API
+ * Create a datetime value from a filled datetime struct and resolution unit.
+ *
+ * TO BE REMOVED - NOT USED INTERNALLY.
+ */
+NPY_NO_EXPORT npy_datetime
+PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d)
+{
+ npy_datetime ret;
+ PyArray_DatetimeMetaData meta;
+
+ /* Set up a dummy metadata for the conversion */
+ meta.base = fr;
+ meta.num = 1;
+ meta.events = 1;
+
+ if (convert_datetimestruct_to_datetime(&meta, d, &ret) < 0) {
+ /* The caller then needs to check PyErr_Occurred() */
+ return -1;
}
return ret;
@@ -394,6 +429,8 @@ PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d)
/*NUMPY_API
* Create a timdelta value from a filled timedelta struct and resolution unit.
+ *
+ * TO BE REMOVED - NOT USED INTERNALLY.
*/
NPY_NO_EXPORT npy_datetime
PyArray_TimedeltaStructToTimedelta(NPY_DATETIMEUNIT fr, npy_timedeltastruct *d)
@@ -486,306 +523,306 @@ PyArray_TimedeltaStructToTimedelta(NPY_DATETIMEUNIT fr, npy_timedeltastruct *d)
return ret;
}
-
-
-/*NUMPY_API
- * Fill the datetime struct from the value and resolution unit.
+/*
+ * Converts a datetime based on the given metadata into a datetimestruct
*/
-NPY_NO_EXPORT void
-PyArray_DatetimeToDatetimeStruct(npy_datetime val, NPY_DATETIMEUNIT fr,
- npy_datetimestruct *result)
+NPY_NO_EXPORT int
+convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
+ npy_datetime dt,
+ npy_datetimestruct *out)
{
- int year = 1970, month = 1, day = 1,
- hour = 0, min = 0, sec = 0,
- us = 0, ps = 0, as = 0;
+ npy_int64 absdays;
+ npy_int64 perday;
+
+ /* Initialize the output to all zeros */
+ memset(out, 0, sizeof(npy_datetimestruct));
+ out->year = 1970;
+ out->month = 1;
+ out->day = 1;
+
+ /* NaT is signaled in the year */
+ if (dt == NPY_DATETIME_NAT) {
+ out->year = NPY_DATETIME_NAT;
+ return 0;
+ }
+
+ /* Extract the event number */
+ if (meta->events > 1) {
+ out->event = dt % meta->events;
+ dt = dt / meta->events;
+ if (out->event < 0) {
+ out->event += meta->events;
+ --dt;
+ }
+ }
- npy_int64 tmp;
- ymdstruct ymd;
- hmsstruct hms;
+ /* TODO: Change to a mechanism that avoids the potential overflow */
+ dt *= meta->num;
/*
- * 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).
+ * Note that care must be taken with the / and % operators
+ * for negative values.
*/
- 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(sec);
- 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) * (npy_int64)(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 *= (npy_int64)(1000);
- num1 = (npy_int64)(86400) * num3;
- num2 = num1 - 1;
+ switch (meta->base) {
+ case NPY_FR_Y:
+ out->year = 1970 + dt;
+ break;
- 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 *= (npy_int64)(1000);
- num2 = num1 * (npy_int64)(1000);
+ case NPY_FR_M:
+ if (dt >= 0) {
+ out->year = 1970 + dt / 12;
+ out->month = dt % 12 + 1;
+ }
+ else {
+ out->year = 1969 + (dt + 1) / 12;
+ out->month = 12 + (dt + 1)% 12;
+ }
+ break;
- 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;
+ case NPY_FR_W:
+ /* A week is 7 days */
+ set_datetimestruct_days(dt * 7, out);
+ break;
+
+ case NPY_FR_B:
+ /* TODO: fix up business days */
+ /* Number of business days since Thursday, 1-1-70 */
+ /*
+ * A business 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 (dt >= 0) {
+ absdays = 7 * ((dt + 3) / 5) + ((dt + 3) % 5) - 3;
}
else {
- hour = 24 + (sec - 3599)/3600;
- sec = 3599 + (sec+1)%3600;
- min = sec / 60;
- sec = sec % 60;
+ /* Recall how C computes / and % with negative numbers */
+ absdays = 7 * ((dt - 1) / 5) + ((dt - 1) % 5) + 1;
}
- }
- us = tmp / 1000000000;
- tmp = tmp % 1000000000;
- ps = tmp / 1000;
- as = (tmp % 1000) * (npy_int64)(1000);
- }
- else if (fr == NPY_FR_as) {
- /* entire range is only += 9.2 seconds */
- npy_int64 num1, num2, num3;
- num1 = 1000000;
- num2 = num1 * (npy_int64)(1000000);
- num3 = num2 * (npy_int64)(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");
+ set_datetimestruct_days(absdays, out);
+ break;
+
+ case NPY_FR_D:
+ set_datetimestruct_days(dt, out);
+ break;
+
+ case NPY_FR_h:
+ perday = 24LL;
+
+ if (dt >= 0) {
+ set_datetimestruct_days(dt / perday, out);
+ dt = dt % perday;
+ }
+ else {
+ set_datetimestruct_days((dt - (perday-1)) / perday, out);
+ dt = (perday-1) + (dt + 1) % perday;
+ }
+ out->hour = dt;
+ break;
+
+ case NPY_FR_m:
+ perday = 24LL * 60;
+
+ if (dt >= 0) {
+ set_datetimestruct_days(dt / perday, out);
+ dt = dt % perday;
+ }
+ else {
+ set_datetimestruct_days((dt - (perday-1)) / perday, out);
+ dt = (perday-1) + (dt + 1) % perday;
+ }
+ out->hour = dt / 60;
+ out->min = dt % 60;
+ break;
+
+ case NPY_FR_s:
+ perday = 24LL * 60 * 60;
+
+ if (dt >= 0) {
+ set_datetimestruct_days(dt / perday, out);
+ dt = dt % perday;
+ }
+ else {
+ set_datetimestruct_days((dt - (perday-1)) / perday, out);
+ dt = (perday-1) + (dt + 1) % perday;
+ }
+ out->hour = dt / (60*60);
+ out->min = (dt / 60) % 60;
+ out->sec = dt % 60;
+ break;
+
+ case NPY_FR_ms:
+ perday = 24LL * 60 * 60 * 1000;
+
+ if (dt >= 0) {
+ set_datetimestruct_days(dt / perday, out);
+ dt = dt % perday;
+ }
+ else {
+ set_datetimestruct_days((dt - (perday-1)) / perday, out);
+ dt = (perday-1) + (dt + 1) % perday;
+ }
+ out->hour = dt / (60*60*1000LL);
+ out->min = (dt / (60*1000LL)) % 60;
+ out->sec = (dt / 1000LL) % 60;
+ out->us = (dt % 1000LL) * 1000;
+ break;
+
+ case NPY_FR_us:
+ perday = 24LL * 60LL * 60LL * 1000LL * 1000LL;
+
+ if (dt >= 0) {
+ set_datetimestruct_days(dt / perday, out);
+ dt = dt % perday;
+ }
+ else {
+ set_datetimestruct_days((dt - (perday-1)) / perday, out);
+ dt = (perday-1) + (dt + 1) % perday;
+ }
+ out->hour = dt / (60*60*1000000LL);
+ out->min = (dt / (60*1000000LL)) % 60;
+ out->sec = (dt / 1000000LL) % 60;
+ out->us = dt % 1000000LL;
+ break;
+
+ case NPY_FR_ns:
+ perday = 24LL * 60LL * 60LL * 1000LL * 1000LL * 1000LL;
+
+ if (dt >= 0) {
+ set_datetimestruct_days(dt / perday, out);
+ dt = dt % perday;
+ }
+ else {
+ set_datetimestruct_days((dt - (perday-1)) / perday, out);
+ dt = (perday-1) + (dt + 1) % perday;
+ }
+ out->hour = dt / (60*60*1000000000LL);
+ out->min = (dt / (60*1000000000LL)) % 60;
+ out->sec = (dt / 1000000000LL) % 60;
+ out->us = (dt / 1000LL) % 1000000LL;
+ out->ps = (dt % 1000LL) * 1000;
+ break;
+
+ case NPY_FR_ps:
+ perday = 24LL * 60 * 60 * 1000 * 1000 * 1000 * 1000;
+
+ if (dt >= 0) {
+ set_datetimestruct_days(dt / perday, out);
+ dt = dt % perday;
+ }
+ else {
+ set_datetimestruct_days((dt - (perday-1)) / perday, out);
+ dt = (perday-1) + (dt + 1) % perday;
+ }
+ out->hour = dt / (60*60*1000000000000LL);
+ out->min = (dt / (60*1000000000000LL)) % 60;
+ out->sec = (dt / 1000000000000LL) % 60;
+ out->us = (dt / 1000000LL) % 1000000LL;
+ out->ps = dt % 1000000LL;
+ break;
+
+ case NPY_FR_fs:
+ /* entire range is only +- 2.6 hours */
+ if (dt >= 0) {
+ out->hour = dt / (60*60*1000000000000000LL);
+ out->min = (dt / (60*1000000000000000LL)) % 60;
+ out->sec = (dt / 1000000000000000LL) % 60;
+ out->us = (dt / 1000000000LL) % 1000000LL;
+ out->ps = (dt / 1000LL) % 1000000LL;
+ out->as = (dt % 1000LL) * 1000;
+ }
+ else {
+ npy_datetime minutes;
+
+ minutes = dt / (60*1000000000000000LL);
+ dt = dt % (60*1000000000000000LL);
+ if (dt < 0) {
+ dt += (60*1000000000000000LL);
+ --minutes;
+ }
+ /* Offset the negative minutes */
+ add_minutes_to_datetimestruct(out, minutes);
+ out->sec = (dt / 1000000000000000LL) % 60;
+ out->us = (dt / 1000000000LL) % 1000000LL;
+ out->ps = (dt / 1000LL) % 1000000LL;
+ out->as = (dt % 1000LL) * 1000;
+ }
+ break;
+
+ case NPY_FR_as:
+ /* entire range is only +- 9.2 seconds */
+ if (dt >= 0) {
+ out->sec = (dt / 1000000000000000000LL) % 60;
+ out->us = (dt / 1000000000000LL) % 1000000LL;
+ out->ps = (dt / 1000000LL) % 1000000LL;
+ out->as = dt % 1000000LL;
+ }
+ else {
+ npy_datetime seconds;
+
+ seconds = dt / 1000000000000000000LL;
+ dt = dt % 1000000000000000000LL;
+ if (dt < 0) {
+ dt += 1000000000000000000LL;
+ --seconds;
+ }
+ /* Offset the negative seconds */
+ add_seconds_to_datetimestruct(out, seconds);
+ out->us = (dt / 1000000000000LL) % 1000000LL;
+ out->ps = (dt / 1000000LL) % 1000000LL;
+ out->as = dt % 1000000LL;
+ }
+ break;
+
+ default:
+ PyErr_SetString(PyExc_RuntimeError,
+ "NumPy datetime metadata is corrupted with invalid "
+ "base unit");
+ return -1;
}
- 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 0;
+}
+
+
+/*NUMPY_API
+ * Fill the datetime struct from the value and resolution unit.
+ *
+ * TO BE REMOVED - NOT USED INTERNALLY.
+ */
+NPY_NO_EXPORT void
+PyArray_DatetimeToDatetimeStruct(npy_datetime val, NPY_DATETIMEUNIT fr,
+ npy_datetimestruct *result)
+{
+ PyArray_DatetimeMetaData meta;
+
+ /* Set up a dummy metadata for the conversion */
+ meta.base = fr;
+ meta.num = 1;
+ meta.events = 1;
+
+ if (convert_datetime_to_datetimestruct(&meta, val, result) < 0) {
+ /* The caller needs to check PyErr_Occurred() */
+ return;
+ }
return;
}
/*
* FIXME: Overflow is not handled at all
- * To convert from Years, Months, and Business Days, multiplication by the average is done
+ * 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.
+ *
+ * TO BE REMOVED - NOT USED INTERNALLY.
*/
NPY_NO_EXPORT void
PyArray_TimedeltaToTimedeltaStruct(npy_timedelta val, NPY_DATETIMEUNIT fr,
@@ -796,9 +833,10 @@ PyArray_TimedeltaToTimedeltaStruct(npy_timedelta val, NPY_DATETIMEUNIT fr,
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).
+ * 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) {
@@ -920,3 +958,3117 @@ PyArray_TimedeltaToTimedeltaStruct(npy_timedelta val, NPY_DATETIMEUNIT fr,
}
return;
}
+
+/*
+ * This function returns the a new reference to the
+ * capsule with the datetime metadata.
+ */
+NPY_NO_EXPORT PyObject *
+get_datetime_metacobj_from_dtype(PyArray_Descr *dtype)
+{
+ PyObject *metacobj;
+
+ /* Check that the dtype has metadata */
+ if (dtype->metadata == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Datetime type object is invalid, lacks metadata");
+ return NULL;
+ }
+
+ /* Check that the dtype has unit metadata */
+ metacobj = PyDict_GetItemString(dtype->metadata, NPY_METADATA_DTSTR);
+ if (metacobj == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Datetime type object is invalid, lacks unit metadata");
+ return NULL;
+ }
+
+ Py_INCREF(metacobj);
+ return metacobj;
+}
+
+/*
+ * This function returns a pointer to the DateTimeMetaData
+ * contained within the provided datetime dtype.
+ */
+NPY_NO_EXPORT PyArray_DatetimeMetaData *
+get_datetime_metadata_from_dtype(PyArray_Descr *dtype)
+{
+ PyObject *metacobj;
+ PyArray_DatetimeMetaData *meta = NULL;
+
+ metacobj = get_datetime_metacobj_from_dtype(dtype);
+ if (metacobj == NULL) {
+ return NULL;
+ }
+
+ /* Check that the dtype has an NpyCapsule for the metadata */
+ meta = (PyArray_DatetimeMetaData *)NpyCapsule_AsVoidPtr(metacobj);
+ if (meta == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Datetime type object is invalid, unit metadata is corrupt");
+ return NULL;
+ }
+
+ return meta;
+}
+
+/*
+ * Converts a substring given by 'str' and 'len' into
+ * a date time unit multiplier + enum value, which are populated
+ * into out_meta. Other metadata is left along.
+ *
+ * 'metastr' is only used in the error message, and may be NULL.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+parse_datetime_extended_unit_from_string(char *str, Py_ssize_t len,
+ char *metastr,
+ PyArray_DatetimeMetaData *out_meta)
+{
+ char *substr = str, *substrend = NULL;
+ int den = 1;
+
+ /* First comes an optional integer multiplier */
+ out_meta->num = (int)strtol(substr, &substrend, 10);
+ if (substr == substrend) {
+ out_meta->num = 1;
+ }
+ substr = substrend;
+
+ /* Next comes the unit itself, followed by either '/' or the string end */
+ substrend = substr;
+ while (substrend-str < len && *substrend != '/') {
+ ++substrend;
+ }
+ if (substr == substrend) {
+ goto bad_input;
+ }
+ out_meta->base = parse_datetime_unit_from_string(substr,
+ substrend-substr, metastr);
+ if (out_meta->base == -1) {
+ return -1;
+ }
+ substr = substrend;
+
+ /* Next comes an optional integer denominator */
+ if (substr-str < len && *substr == '/') {
+ substr++;
+ den = (int)strtol(substr, &substrend, 10);
+ /* If the '/' exists, there must be a number followed by ']' */
+ if (substr == substrend || *substrend != ']') {
+ goto bad_input;
+ }
+ substr = substrend + 1;
+ }
+ else if (substr-str != len) {
+ goto bad_input;
+ }
+
+ if (den != 1) {
+ if (convert_datetime_divisor_to_multiple(
+ out_meta, den, metastr) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+
+bad_input:
+ if (metastr != NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "Invalid datetime metadata string \"%s\" at position %d",
+ metastr, (int)(substr-metastr));
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "Invalid datetime metadata string \"%s\"",
+ str);
+ }
+
+ return -1;
+}
+
+/*
+ * Parses the metadata string into the metadata C structure.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+parse_datetime_metadata_from_metastr(char *metastr, Py_ssize_t len,
+ PyArray_DatetimeMetaData *out_meta)
+{
+ char *substr = metastr, *substrend = NULL;
+
+ /* The metadata string must start with a '[' */
+ if (len < 3 || *substr++ != '[') {
+ goto bad_input;
+ }
+
+ substrend = substr;
+ while (*substrend != '\0' && *substrend != ']') {
+ ++substrend;
+ }
+ if (*substrend == '\0' || substr == substrend) {
+ substr = substrend;
+ goto bad_input;
+ }
+
+ /* Parse the extended unit inside the [] */
+ if (parse_datetime_extended_unit_from_string(substr, substrend-substr,
+ metastr, out_meta) < 0) {
+ return -1;
+ }
+
+ substr = substrend+1;
+
+ /* Finally comes an optional number of events */
+ if (substr[0] == '/' && substr[1] == '/') {
+ substr += 2;
+
+ out_meta->events = (int)strtol(substr, &substrend, 10);
+ if (substr == substrend || *substrend != '\0') {
+ goto bad_input;
+ }
+ }
+ else if (*substr != '\0') {
+ goto bad_input;
+ }
+ else {
+ out_meta->events = 1;
+ }
+
+ return 0;
+
+bad_input:
+ if (substr != metastr) {
+ PyErr_Format(PyExc_TypeError,
+ "Invalid datetime metadata string \"%s\" at position %d",
+ metastr, (int)(substr-metastr));
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "Invalid datetime metadata string \"%s\"",
+ metastr);
+ }
+
+ return -1;
+}
+
+NPY_NO_EXPORT PyObject *
+parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len)
+{
+ PyArray_DatetimeMetaData *dt_data;
+
+ dt_data = PyArray_malloc(sizeof(PyArray_DatetimeMetaData));
+ if (dt_data == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ /* If there's no metastr, use the default */
+ if (len == 0) {
+ dt_data->num = 1;
+ dt_data->base = NPY_DATETIME_DEFAULTUNIT;
+ dt_data->events = 1;
+ }
+ else {
+ if (parse_datetime_metadata_from_metastr(metastr, len, dt_data) < 0) {
+ PyArray_free(dt_data);
+ return NULL;
+ }
+ }
+
+ return NpyCapsule_FromVoidPtr((void *)dt_data, simple_capsule_dtor);
+}
+
+/*
+ * Converts a datetype dtype string into a dtype descr object.
+ * The "type" string should be NULL-terminated.
+ */
+NPY_NO_EXPORT PyArray_Descr *
+parse_dtype_from_datetime_typestr(char *typestr, Py_ssize_t len)
+{
+ PyArray_Descr *dtype = NULL;
+ char *metastr = NULL;
+ int is_timedelta = 0;
+ Py_ssize_t metalen = 0;
+ PyObject *metacobj = NULL;
+
+ if (len < 2) {
+ PyErr_Format(PyExc_TypeError,
+ "Invalid datetime typestr \"%s\"",
+ typestr);
+ return NULL;
+ }
+
+ /*
+ * First validate that the root is correct,
+ * and get the metadata string address
+ */
+ if (typestr[0] == 'm' && typestr[1] == '8') {
+ is_timedelta = 1;
+ metastr = typestr + 2;
+ metalen = len - 2;
+ }
+ else if (typestr[0] == 'M' && typestr[1] == '8') {
+ is_timedelta = 0;
+ metastr = typestr + 2;
+ metalen = len - 2;
+ }
+ else if (len >= 11 && strncmp(typestr, "timedelta64", 11) == 0) {
+ is_timedelta = 1;
+ metastr = typestr + 11;
+ metalen = len - 11;
+ }
+ else if (len >= 10 && strncmp(typestr, "datetime64", 10) == 0) {
+ is_timedelta = 0;
+ metastr = typestr + 10;
+ metalen = len - 10;
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "Invalid datetime typestr \"%s\"",
+ typestr);
+ return NULL;
+ }
+
+ /* Create a default datetime or timedelta */
+ if (is_timedelta) {
+ dtype = PyArray_DescrNewFromType(NPY_TIMEDELTA);
+ }
+ else {
+ dtype = PyArray_DescrNewFromType(NPY_DATETIME);
+ }
+ if (dtype == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Remove any reference to old metadata dictionary
+ * And create a new one for this new dtype
+ */
+ Py_XDECREF(dtype->metadata);
+ dtype->metadata = PyDict_New();
+ if (dtype->metadata == NULL) {
+ Py_DECREF(dtype);
+ return NULL;
+ }
+
+ /* Parse the metadata string into a metadata capsule */
+ metacobj = parse_datetime_metacobj_from_metastr(metastr, metalen);
+ if (metacobj == NULL) {
+ Py_DECREF(dtype);
+ return NULL;
+ }
+
+ /* Set the metadata object in the dictionary. */
+ if (PyDict_SetItemString(dtype->metadata, NPY_METADATA_DTSTR,
+ metacobj) < 0) {
+ Py_DECREF(dtype);
+ Py_DECREF(metacobj);
+ return NULL;
+ }
+ Py_DECREF(metacobj);
+
+ return dtype;
+}
+
+/*
+ * Creates a new NPY_TIMEDELTA dtype, copying the datetime metadata
+ * from the given dtype.
+ */
+NPY_NO_EXPORT PyArray_Descr *
+timedelta_dtype_with_copied_meta(PyArray_Descr *dtype)
+{
+ PyArray_Descr *ret;
+ PyObject *metacobj;
+
+ ret = PyArray_DescrNewFromType(NPY_TIMEDELTA);
+ if (ret == NULL) {
+ return NULL;
+ }
+ Py_XDECREF(ret->metadata);
+ ret->metadata = PyDict_New();
+ if (ret->metadata == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ metacobj = get_datetime_metacobj_from_dtype(dtype);
+ if (metacobj == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ if (PyDict_SetItemString(ret->metadata, NPY_METADATA_DTSTR,
+ metacobj) < 0) {
+ Py_DECREF(metacobj);
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+static NPY_DATETIMEUNIT _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.
+ * 'metastr' is used for the error message if the divisor doesn't work,
+ * and can be NULL if the metadata didn't come from a string.
+ *
+ * This function only affects the 'base' and 'num' values in the metadata.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta,
+ int den, char *metastr)
+{
+ int i, num, ind;
+ NPY_DATETIMEUNIT *totry;
+ NPY_DATETIMEUNIT *baseunit;
+ int q, r;
+
+ ind = ((int)meta->base - (int)NPY_FR_Y)*2;
+ totry = _multiples_table[ind];
+ baseunit = _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 = _multiples_table[ind + 1];
+ baseunit[0] = meta->base + 1;
+ baseunit[1] = meta->base + 2;
+ if (meta->base == NPY_DATETIME_NUMUNITS - 2) {
+ num = 1;
+ }
+ if (meta->base == NPY_DATETIME_NUMUNITS - 1) {
+ num = 0;
+ }
+ }
+
+ for (i = 0; i < num; i++) {
+ q = totry[i] / den;
+ r = totry[i] % den;
+ if (r == 0) {
+ break;
+ }
+ }
+ if (i == num) {
+ if (metastr == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "divisor (%d) is not a multiple of a lower-unit "
+ "in datetime metadata", den);
+ }
+ else {
+ PyErr_Format(PyExc_ValueError,
+ "divisor (%d) is not a multiple of a lower-unit "
+ "in datetime metadata \"%s\"", den, metastr);
+ }
+ return -1;
+ }
+ meta->base = baseunit[i];
+ meta->num *= q;
+
+ return 0;
+}
+
+/*
+ * Lookup table for factors between datetime units, except
+ * for years, months, and business days.
+ */
+static npy_uint32
+_datetime_factors[] = {
+ 1, /* Years - not used */
+ 1, /* Months - not used */
+ 7, /* Weeks -> Days */
+ 1, /* Business days - not used */
+ 24, /* Days -> Hours */
+ 60, /* Hours -> Minutes */
+ 60, /* Minutes -> Seconds */
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1 /* Attoseconds are the smallest base unit */
+};
+
+/*
+ * Returns the scale factor between the units. Does not validate
+ * that bigbase represents larger units than littlebase.
+ *
+ * Returns 0 if there is an overflow.
+ */
+static npy_uint64
+get_datetime_units_factor(NPY_DATETIMEUNIT bigbase, NPY_DATETIMEUNIT littlebase)
+{
+ npy_uint64 factor = 1;
+ int unit = (int)bigbase;
+ while (littlebase > unit) {
+ factor *= _datetime_factors[unit];
+ /*
+ * Detect overflow by disallowing the top 16 bits to be 1.
+ * That alows a margin of error much bigger than any of
+ * the datetime factors.
+ */
+ if (factor&0xff00000000000000ULL) {
+ return 0;
+ }
+ ++unit;
+ }
+ return factor;
+}
+
+/* Euclidean algorithm on two positive numbers */
+static npy_uint64
+_uint64_euclidean_gcd(npy_uint64 x, npy_uint64 y)
+{
+ npy_uint64 tmp;
+
+ if (x > y) {
+ tmp = x;
+ x = y;
+ y = tmp;
+ }
+ while (x != y && y != 0) {
+ tmp = x % y;
+ x = y;
+ y = tmp;
+ }
+
+ return x;
+}
+
+/*
+ * Computes the conversion factor to convert data with 'src_meta' metadata
+ * into data with 'dst_meta' metadata, not taking into account the events.
+ *
+ * To convert a npy_datetime or npy_timedelta, first the event number needs
+ * to be divided away, then it needs to be scaled by num/denom, and
+ * finally the event number can be added back in.
+ *
+ * If overflow occurs, both out_num and out_denom are set to 0, but
+ * no error is set.
+ */
+NPY_NO_EXPORT void
+get_datetime_conversion_factor(PyArray_DatetimeMetaData *src_meta,
+ PyArray_DatetimeMetaData *dst_meta,
+ npy_int64 *out_num, npy_int64 *out_denom)
+{
+ int src_base, dst_base, swapped;
+ npy_uint64 num = 1, denom = 1, tmp, gcd;
+
+ if (src_meta->base <= dst_meta->base) {
+ src_base = src_meta->base;
+ dst_base = dst_meta->base;
+ swapped = 0;
+ }
+ else {
+ src_base = dst_meta->base;
+ dst_base = src_meta->base;
+ swapped = 1;
+ }
+
+ if (src_base != dst_base) {
+ /*
+ * Conversions between years/months and other units use
+ * the factor averaged over the 400 year leap year cycle.
+ */
+ if (src_base == NPY_FR_Y) {
+ if (dst_base == NPY_FR_M) {
+ num *= 12;
+ }
+ else if (dst_base == NPY_FR_W) {
+ num *= (97 + 400*365);
+ denom *= 400*7;
+ }
+ else {
+ /* Year -> Day */
+ num *= (97 + 400*365);
+ denom *= 400;
+ /* Day -> dst_base */
+ num *= get_datetime_units_factor(NPY_FR_D, dst_base);
+ }
+ }
+ else if (src_base == NPY_FR_M) {
+ if (dst_base == NPY_FR_W) {
+ num *= (97 + 400*365);
+ denom *= 400*12*7;
+ }
+ else {
+ /* Month -> Day */
+ num *= (97 + 400*365);
+ denom *= 400*12;
+ /* Day -> dst_base */
+ num *= get_datetime_units_factor(NPY_FR_D, dst_base);
+ }
+ }
+ else {
+ num *= get_datetime_units_factor(src_base, dst_base);
+ }
+ }
+
+ /* If something overflowed, make both num and denom 0 */
+ if (denom == 0) {
+ *out_num = 0;
+ *out_denom = 0;
+ return;
+ }
+
+ /* Swap the numerator and denominator if necessary */
+ if (swapped) {
+ tmp = num;
+ num = denom;
+ denom = tmp;
+ }
+
+ num *= src_meta->num;
+ denom *= dst_meta->num;
+
+ /* Return as a fraction in reduced form */
+ gcd = _uint64_euclidean_gcd(num, denom);
+ *out_num = (npy_int64)(num / gcd);
+ *out_denom = (npy_int64)(denom / gcd);
+}
+
+/*
+ * Determines whether the 'divisor' metadata divides evenly into
+ * the 'dividend' metadata.
+ */
+NPY_NO_EXPORT npy_bool
+datetime_metadata_divides(
+ PyArray_Descr *dividend,
+ PyArray_Descr *divisor,
+ int strict_with_nonlinear_units)
+{
+ PyArray_DatetimeMetaData *meta1, *meta2;
+ npy_uint64 num1, num2;
+
+ /* Must be datetime types */
+ if ((dividend->type_num != NPY_DATETIME &&
+ dividend->type_num != NPY_TIMEDELTA) ||
+ (divisor->type_num != NPY_DATETIME &&
+ divisor->type_num != NPY_TIMEDELTA)) {
+ return 0;
+ }
+
+ meta1 = get_datetime_metadata_from_dtype(dividend);
+ if (meta1 == NULL) {
+ PyErr_Clear();
+ return 0;
+ }
+ meta2 = get_datetime_metadata_from_dtype(divisor);
+ if (meta2 == NULL) {
+ PyErr_Clear();
+ return 0;
+ }
+
+ /* Events must match */
+ if (meta1->events != meta2->events) {
+ return 0;
+ }
+
+ num1 = (npy_uint64)meta1->num;
+ num2 = (npy_uint64)meta2->num;
+
+ /* If the bases are different, factor in a conversion */
+ if (meta1->base != meta2->base) {
+ /*
+ * Years, Months, and Business days are incompatible with
+ * all other units (except years and months are compatible
+ * with each other).
+ */
+ if (meta1->base == NPY_FR_B || meta2->base == NPY_FR_B) {
+ return 0;
+ }
+ else if (meta1->base == NPY_FR_Y) {
+ if (meta2->base == NPY_FR_M) {
+ num1 *= 12;
+ }
+ else if (strict_with_nonlinear_units) {
+ return 0;
+ }
+ else {
+ /* Could do something complicated here */
+ return 1;
+ }
+ }
+ else if (meta2->base == NPY_FR_Y) {
+ if (meta1->base == NPY_FR_M) {
+ num2 *= 12;
+ }
+ else if (strict_with_nonlinear_units) {
+ return 0;
+ }
+ else {
+ /* Could do something complicated here */
+ return 1;
+ }
+ }
+ else if (meta1->base == NPY_FR_M || meta2->base == NPY_FR_M) {
+ if (strict_with_nonlinear_units) {
+ return 0;
+ }
+ else {
+ /* Could do something complicated here */
+ return 1;
+ }
+ }
+
+ /* Take the greater base (unit sizes are decreasing in enum) */
+ if (meta1->base > meta2->base) {
+ num2 *= get_datetime_units_factor(meta2->base, meta1->base);
+ if (num2 == 0) {
+ return 0;
+ }
+ }
+ else {
+ num1 *= get_datetime_units_factor(meta1->base, meta2->base);
+ if (num1 == 0) {
+ return 0;
+ }
+ }
+ }
+
+ /* Crude, incomplete check for overflow */
+ if (num1&0xff00000000000000LL || num2&0xff00000000000000LL ) {
+ return 0;
+ }
+
+ return (num1 % num2) == 0;
+}
+
+
+NPY_NO_EXPORT PyObject *
+compute_datetime_metadata_greatest_common_divisor(
+ PyArray_Descr *type1,
+ PyArray_Descr *type2,
+ int strict_with_nonlinear_units)
+{
+ PyArray_DatetimeMetaData *meta1, *meta2, *dt_data;
+ NPY_DATETIMEUNIT base;
+ npy_uint64 num1, num2, num;
+ int events = 1;
+
+ if ((type1->type_num != NPY_DATETIME &&
+ type1->type_num != NPY_TIMEDELTA) ||
+ (type2->type_num != NPY_DATETIME &&
+ type2->type_num != NPY_TIMEDELTA)) {
+ PyErr_SetString(PyExc_TypeError,
+ "Require datetime types for metadata "
+ "greatest common divisor operation");
+ return NULL;
+ }
+
+ meta1 = get_datetime_metadata_from_dtype(type1);
+ if (meta1 == NULL) {
+ return NULL;
+ }
+ meta2 = get_datetime_metadata_from_dtype(type2);
+ if (meta2 == NULL) {
+ return NULL;
+ }
+
+ /* Take the maximum of the events */
+ if (meta1->events > meta2->events) {
+ events = meta1->events;
+ }
+ else {
+ events = meta2->events;
+ }
+
+ num1 = (npy_uint64)meta1->num;
+ num2 = (npy_uint64)meta2->num;
+
+ /* First validate that the units have a reasonable GCD */
+ if (meta1->base == meta2->base) {
+ base = meta1->base;
+ }
+ else {
+ /*
+ * Years, Months, and Business days are incompatible with
+ * all other units (except years and months are compatible
+ * with each other).
+ */
+ if (meta1->base == NPY_FR_Y) {
+ if (meta2->base == NPY_FR_M) {
+ base = NPY_FR_M;
+ num1 *= 12;
+ }
+ else if (strict_with_nonlinear_units) {
+ goto incompatible_units;
+ }
+ else {
+ base = meta2->base;
+ /* Don't multiply num1 since there is no even factor */
+ }
+ }
+ else if (meta2->base == NPY_FR_Y) {
+ if (meta1->base == NPY_FR_M) {
+ base = NPY_FR_M;
+ num2 *= 12;
+ }
+ else if (strict_with_nonlinear_units) {
+ goto incompatible_units;
+ }
+ else {
+ base = meta1->base;
+ /* Don't multiply num2 since there is no even factor */
+ }
+ }
+ else if (meta1->base == NPY_FR_M ||
+ meta1->base == NPY_FR_B ||
+ meta2->base == NPY_FR_M ||
+ meta2->base == NPY_FR_B) {
+ if (strict_with_nonlinear_units) {
+ goto incompatible_units;
+ }
+ else {
+ if (meta1->base > meta2->base) {
+ base = meta1->base;
+ }
+ else {
+ base = meta2->base;
+ }
+
+ /*
+ * When combining business days with other units, end
+ * up with days instead of business days.
+ */
+ if (base == NPY_FR_B) {
+ base = NPY_FR_D;
+ }
+ }
+ }
+
+ /* Take the greater base (unit sizes are decreasing in enum) */
+ if (meta1->base > meta2->base) {
+ base = meta1->base;
+ num2 *= get_datetime_units_factor(meta2->base, meta1->base);
+ if (num2 == 0) {
+ goto units_overflow;
+ }
+ }
+ else {
+ base = meta2->base;
+ num1 *= get_datetime_units_factor(meta1->base, meta2->base);
+ if (num1 == 0) {
+ goto units_overflow;
+ }
+ }
+ }
+
+ /* Compute the GCD of the resulting multipliers */
+ num = _uint64_euclidean_gcd(num1, num2);
+
+ /* Create and return the metadata capsule */
+ dt_data = PyArray_malloc(sizeof(PyArray_DatetimeMetaData));
+ if (dt_data == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ dt_data->base = base;
+ dt_data->num = (int)num;
+ if (dt_data->num <= 0 || num != (npy_uint64)dt_data->num) {
+ goto units_overflow;
+ }
+ dt_data->events = events;
+
+ return NpyCapsule_FromVoidPtr((void *)dt_data, simple_capsule_dtor);
+
+incompatible_units: {
+ PyObject *errmsg;
+ errmsg = PyUString_FromString("Cannot get "
+ "a common metadata divisor for types ");
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)type1));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" and "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)type2));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" because they have "
+ "incompatible nonlinear base time units"));
+ PyErr_SetObject(PyExc_TypeError, errmsg);
+ return NULL;
+ }
+units_overflow: {
+ PyObject *errmsg;
+ errmsg = PyUString_FromString("Integer overflow "
+ "getting a common metadata divisor for types ");
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)type1));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" and "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)type2));
+ PyErr_SetObject(PyExc_OverflowError, errmsg);
+ return NULL;
+ }
+}
+
+/*
+ * Uses type1's type_num and the gcd of the metadata to create
+ * the result type.
+ */
+static PyArray_Descr *
+datetime_gcd_type_promotion(PyArray_Descr *type1, PyArray_Descr *type2)
+{
+ PyObject *gcdmeta;
+ PyArray_Descr *dtype;
+
+ /*
+ * Get the metadata GCD, being strict about nonlinear units for
+ * timedelta and relaxed for datetime.
+ */
+ gcdmeta = compute_datetime_metadata_greatest_common_divisor(
+ type1, type2,
+ type1->type_num == NPY_TIMEDELTA);
+ if (gcdmeta == NULL) {
+ return NULL;
+ }
+
+ /* Create a DATETIME or TIMEDELTA dtype */
+ dtype = PyArray_DescrNewFromType(type1->type_num);
+ if (dtype == NULL) {
+ Py_DECREF(gcdmeta);
+ return NULL;
+ }
+
+ /* Replace the metadata dictionary */
+ Py_XDECREF(dtype->metadata);
+ dtype->metadata = PyDict_New();
+ if (dtype->metadata == NULL) {
+ Py_DECREF(dtype);
+ Py_DECREF(gcdmeta);
+ return NULL;
+ }
+
+ /* Set the metadata object in the dictionary. */
+ if (PyDict_SetItemString(dtype->metadata, NPY_METADATA_DTSTR,
+ gcdmeta) < 0) {
+ Py_DECREF(dtype);
+ Py_DECREF(gcdmeta);
+ return NULL;
+ }
+ Py_DECREF(gcdmeta);
+
+ return dtype;
+}
+
+/*
+ * Both type1 and type2 must be either NPY_DATETIME or NPY_TIMEDELTA.
+ * Applies the type promotion rules between the two types, returning
+ * the promoted type.
+ */
+NPY_NO_EXPORT PyArray_Descr *
+datetime_type_promotion(PyArray_Descr *type1, PyArray_Descr *type2)
+{
+ int type_num1, type_num2;
+
+ type_num1 = type1->type_num;
+ type_num2 = type2->type_num;
+
+ if (type_num1 == NPY_DATETIME) {
+ if (type_num2 == NPY_DATETIME) {
+ return datetime_gcd_type_promotion(type1, type2);
+ }
+ else if (type_num2 == NPY_TIMEDELTA) {
+ Py_INCREF(type1);
+ return type1;
+ }
+ }
+ else if (type_num1 == NPY_TIMEDELTA) {
+ if (type_num2 == NPY_DATETIME) {
+ Py_INCREF(type2);
+ return type2;
+ }
+ else if (type_num2 == NPY_TIMEDELTA) {
+ return datetime_gcd_type_promotion(type1, type2);
+ }
+ }
+
+ PyErr_SetString(PyExc_RuntimeError,
+ "Called datetime_type_promotion on non-datetype type");
+ return NULL;
+}
+
+/*
+ * Converts a substring given by 'str' and 'len' into
+ * a date time unit enum value. The 'metastr' parameter
+ * is used for error messages, and may be NULL.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT NPY_DATETIMEUNIT
+parse_datetime_unit_from_string(char *str, Py_ssize_t len, char *metastr)
+{
+ /* Use switch statements so the compiler can make it fast */
+ if (len == 1) {
+ switch (str[0]) {
+ case 'Y':
+ return NPY_FR_Y;
+ case 'M':
+ return NPY_FR_M;
+ case 'W':
+ return NPY_FR_W;
+ case 'B':
+ return NPY_FR_B;
+ case 'D':
+ return NPY_FR_D;
+ case 'h':
+ return NPY_FR_h;
+ case 'm':
+ return NPY_FR_m;
+ case 's':
+ return NPY_FR_s;
+ }
+ }
+ /* All the two-letter units are variants of seconds */
+ else if (len == 2 && str[1] == 's') {
+ switch (str[0]) {
+ case 'm':
+ return NPY_FR_ms;
+ case 'u':
+ return NPY_FR_us;
+ case 'n':
+ return NPY_FR_ns;
+ case 'p':
+ return NPY_FR_ps;
+ case 'f':
+ return NPY_FR_fs;
+ case 'a':
+ return NPY_FR_as;
+ }
+ }
+
+ /* If nothing matched, it's an error */
+ if (metastr == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "Invalid datetime unit \"%s\" in metadata",
+ str);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "Invalid datetime unit in metadata string \"%s\"",
+ metastr);
+ }
+ return -1;
+}
+
+
+NPY_NO_EXPORT PyObject *
+convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta)
+{
+ PyObject *dt_tuple;
+
+ dt_tuple = PyTuple_New(3);
+ if (dt_tuple == NULL) {
+ return NULL;
+ }
+
+ PyTuple_SET_ITEM(dt_tuple, 0,
+ PyBytes_FromString(_datetime_strings[meta->base]));
+ PyTuple_SET_ITEM(dt_tuple, 1,
+ PyInt_FromLong(meta->num));
+ PyTuple_SET_ITEM(dt_tuple, 2,
+ PyInt_FromLong(meta->events));
+
+ return dt_tuple;
+}
+
+/*
+ * Converts a metadata tuple into a datetime metadata C struct.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple,
+ PyArray_DatetimeMetaData *out_meta)
+{
+ char *basestr = NULL;
+ Py_ssize_t len = 0, tuple_size;
+ int den = 1;
+
+ if (!PyTuple_Check(tuple)) {
+ PyObject_Print(tuple, stderr, 0);
+ PyErr_SetString(PyExc_TypeError,
+ "Require tuple for tuple to NumPy datetime "
+ "metadata conversion");
+ return -1;
+ }
+
+ tuple_size = PyTuple_GET_SIZE(tuple);
+ if (tuple_size < 3 || tuple_size > 4) {
+ PyErr_SetString(PyExc_TypeError,
+ "Require tuple of size 3 or 4 for "
+ "tuple to NumPy datetime metadata conversion");
+ return -1;
+ }
+
+ if (PyBytes_AsStringAndSize(PyTuple_GET_ITEM(tuple, 0),
+ &basestr, &len) < 0) {
+ return -1;
+ }
+
+ out_meta->base = parse_datetime_unit_from_string(basestr, len, NULL);
+ if (out_meta->base == -1) {
+ return -1;
+ }
+
+ /* Convert the values to longs */
+ out_meta->num = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 1));
+ if (out_meta->num == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+
+ if (tuple_size == 3) {
+ out_meta->events = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 2));
+ if (out_meta->events == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ }
+ else {
+ den = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 2));
+ if (den == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ out_meta->events = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 3));
+ if (out_meta->events == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ }
+
+ if (out_meta->num <= 0 || out_meta->events <= 0 || den <= 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid tuple values for "
+ "tuple to NumPy datetime metadata conversion");
+ return -1;
+ }
+
+ if (den != 1) {
+ if (convert_datetime_divisor_to_multiple(out_meta, den, NULL) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Converts a metadata tuple into a datetime metadata capsule.
+ */
+NPY_NO_EXPORT PyObject *
+convert_datetime_metadata_tuple_to_metacobj(PyObject *tuple)
+{
+ PyArray_DatetimeMetaData *dt_data;
+
+ dt_data = PyArray_malloc(sizeof(PyArray_DatetimeMetaData));
+
+ if (convert_datetime_metadata_tuple_to_datetime_metadata(
+ tuple, dt_data) < 0) {
+ PyArray_free(dt_data);
+ return NULL;
+ }
+
+ return NpyCapsule_FromVoidPtr((void *)dt_data, simple_capsule_dtor);
+}
+
+/*
+ * Converts an input object into datetime metadata. The input
+ * may be either a string or a tuple.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+convert_pyobject_to_datetime_metadata(PyObject *obj,
+ PyArray_DatetimeMetaData *out_meta)
+{
+ PyObject *ascii = NULL;
+ char *str = NULL;
+ Py_ssize_t len = 0;
+
+ if (PyTuple_Check(obj)) {
+ return convert_datetime_metadata_tuple_to_datetime_metadata(obj,
+ out_meta);
+ }
+
+ /* Get an ASCII string */
+ if (PyUnicode_Check(obj)) {
+ /* Allow unicode format strings: convert to bytes */
+ ascii = PyUnicode_AsASCIIString(obj);
+ if (ascii == NULL) {
+ return -1;
+ }
+ }
+ else if (PyBytes_Check(obj)) {
+ ascii = obj;
+ Py_INCREF(ascii);
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid object for specifying NumPy datetime metadata");
+ return -1;
+ }
+
+ if (PyBytes_AsStringAndSize(ascii, &str, &len) < 0) {
+ return -1;
+ }
+
+ if (len > 0 && str[0] == '[') {
+ return parse_datetime_metadata_from_metastr(str, len, out_meta);
+ }
+ else {
+ if (parse_datetime_extended_unit_from_string(str, len,
+ NULL, out_meta) < 0) {
+ return -1;
+ }
+
+ /* extended_unit is only 'num' and 'base', we have to fill the rest */
+ out_meta->events = 1;
+
+ return 0;
+ }
+
+}
+
+/*
+ * 'ret' is a PyUString containing the datetime string, and this
+ * function appends the metadata string to it.
+ *
+ * If 'skip_brackets' is true, skips the '[]' when events == 1.
+ *
+ * This function steals the reference 'ret'
+ */
+NPY_NO_EXPORT PyObject *
+append_metastr_to_string(PyArray_DatetimeMetaData *meta,
+ int skip_brackets,
+ PyObject *ret)
+{
+ PyObject *res;
+ int num, events;
+ char *basestr;
+
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ num = meta->num;
+ events = meta->events;
+ if (meta->base >= 0 && meta->base < NPY_DATETIME_NUMUNITS) {
+ basestr = _datetime_strings[meta->base];
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "NumPy datetime metadata is corrupted");
+ return NULL;
+ }
+
+ if (num == 1) {
+ if (skip_brackets && events == 1) {
+ res = PyUString_FromFormat("%s", basestr);
+ }
+ else {
+ res = PyUString_FromFormat("[%s]", basestr);
+ }
+ }
+ else {
+ if (skip_brackets && events == 1) {
+ res = PyUString_FromFormat("%d%s", num, basestr);
+ }
+ else {
+ res = PyUString_FromFormat("[%d%s]", num, basestr);
+ }
+ }
+
+ if (events != 1) {
+ PyUString_ConcatAndDel(&res,
+ PyUString_FromFormat("//%d", events));
+ }
+ PyUString_ConcatAndDel(&ret, res);
+ return ret;
+}
+
+/*
+ * Adjusts a datetimestruct based on a seconds offset. Assumes
+ * the current values are valid.
+ */
+NPY_NO_EXPORT void
+add_seconds_to_datetimestruct(npy_datetimestruct *dts, int seconds)
+{
+ int minutes;
+
+ dts->sec += seconds;
+ if (dts->sec < 0) {
+ minutes = dts->sec / 60;
+ dts->sec = dts->sec % 60;
+ if (dts->sec < 0) {
+ --minutes;
+ dts->sec += 60;
+ }
+ add_minutes_to_datetimestruct(dts, minutes);
+ }
+ else if (dts->sec >= 60) {
+ minutes = dts->sec / 60;
+ dts->sec = dts->sec % 60;
+ add_minutes_to_datetimestruct(dts, minutes);
+ }
+}
+
+/*
+ * Adjusts a datetimestruct based on a minutes offset. Assumes
+ * the current values are valid.
+ */
+NPY_NO_EXPORT void
+add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes)
+{
+ int isleap;
+
+ /* MINUTES */
+ dts->min += minutes;
+ while (dts->min < 0) {
+ dts->min += 60;
+ dts->hour--;
+ }
+ while (dts->min >= 60) {
+ dts->min -= 60;
+ dts->hour++;
+ }
+
+ /* HOURS */
+ while (dts->hour < 0) {
+ dts->hour += 24;
+ dts->day--;
+ }
+ while (dts->hour >= 24) {
+ dts->hour -= 24;
+ dts->day++;
+ }
+
+ /* DAYS */
+ if (dts->day < 1) {
+ dts->month--;
+ if (dts->month < 1) {
+ dts->year--;
+ dts->month = 12;
+ }
+ isleap = is_leapyear(dts->year);
+ dts->day += days_in_month[isleap][dts->month-1];
+ }
+ else if (dts->day > 28) {
+ isleap = is_leapyear(dts->year);
+ if (dts->day > days_in_month[isleap][dts->month-1]) {
+ dts->day -= days_in_month[isleap][dts->month-1];
+ dts->month++;
+ if (dts->month > 12) {
+ dts->year++;
+ dts->month = 1;
+ }
+ }
+ }
+}
+
+/*
+ * Parses (almost) standard ISO 8601 date strings. The differences are:
+ *
+ * + After the date and time, may place a ' ' followed by an event number.
+ * + The date "20100312" is parsed as the year 20100312, not as
+ * equivalent to "2010-03-12". The '-' in the dates are not optional.
+ * + Only seconds may have a decimal point, with up to 18 digits after it
+ * (maximum attoseconds precision).
+ * + Either a 'T' as in ISO 8601 or a ' ' may be used to separate
+ * the date and the time. Both are treated equivalently.
+ * + Doesn't (yet) handle the "YYYY-DDD" or "YYYY-Www" formats.
+ * + Doesn't handle leap seconds (seconds value has 60 in these cases).
+ * + Doesn't handle 24:00:00 as synonym for midnight (00:00:00) tomorrow
+ * + Accepts special values "NaT" (not a time), "Today", (current
+ * day according to local time) and "Now" (current time in UTC).
+ *
+ * 'str' must be a NULL-terminated string, and 'len' must be its length.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+parse_iso_8601_date(char *str, int len, npy_datetimestruct *out)
+{
+ int year_leap = 0;
+ int i;
+ char *substr, sublen;
+
+ /* Initialize the output to all zeros */
+ memset(out, 0, sizeof(npy_datetimestruct));
+ out->month = 1;
+ out->day = 1;
+
+ /* The empty string and case-variants of "NaT" parse to not-a-time */
+ if (len <= 0 || (len == 3 &&
+ tolower(str[0]) == 'n' &&
+ tolower(str[1]) == 'a' &&
+ tolower(str[2]) == 't')) {
+ out->year = NPY_DATETIME_NAT;
+ return 0;
+ }
+
+ /*
+ * The string "today" resolves to midnight of today's local date in UTC.
+ * This is perhaps a little weird, but done so that further truncation
+ * to a 'datetime64[D]' type produces the date you expect, rather than
+ * switching to an adjacent day depending on the current time and your
+ * timezone.
+ */
+ if (len == 5 && tolower(str[0]) == 't' &&
+ tolower(str[1]) == 'o' &&
+ tolower(str[2]) == 'd' &&
+ tolower(str[3]) == 'a' &&
+ tolower(str[4]) == 'y') {
+ time_t rawtime = 0;
+ struct tm tm_;
+
+ time(&rawtime);
+#if defined(_WIN32)
+ if (localtime_s(&tm_, &rawtime) != 0) {
+ PyErr_SetString(PyExc_OSError, "Failed to use localtime_s to "
+ "get local time");
+ return -1;
+ }
+#else
+ /* Other platforms may require something else */
+ if (localtime_r(&rawtime, &tm_) == NULL) {
+ PyErr_SetString(PyExc_OSError, "Failed to use localtime_r to "
+ "get local time");
+ return -1;
+ }
+#endif
+ out->year = tm_.tm_year + 1900;
+ out->month = tm_.tm_mon + 1;
+ out->day = tm_.tm_mday;
+ return 0;
+ }
+
+ /* The string "now" resolves to the current UTC time */
+ if (len == 3 && tolower(str[0]) == 'n' &&
+ tolower(str[1]) == 'o' &&
+ tolower(str[2]) == 'w') {
+ time_t rawtime = 0;
+ PyArray_DatetimeMetaData meta;
+ time(&rawtime);
+
+ /* Set up a dummy metadata for the conversion */
+ meta.base = NPY_FR_s;
+ meta.num = 1;
+ meta.events = 1;
+
+ return convert_datetime_to_datetimestruct(&meta, rawtime, out);
+ }
+
+ substr = str;
+ sublen = len;
+
+ /* Skip leading whitespace */
+ while (sublen > 0 && isspace(*substr)) {
+ ++substr;
+ --sublen;
+ }
+
+ /* Leading '-' sign for negative year */
+ if (*substr == '-') {
+ ++substr;
+ --sublen;
+ }
+
+ if (sublen == 0) {
+ goto parse_error;
+ }
+
+ /* PARSE THE YEAR (digits until the '-' character) */
+ out->year = 0;
+ while (sublen > 0 && isdigit(*substr)) {
+ out->year = 10 * out->year + (*substr - '0');
+ ++substr;
+ --sublen;
+ }
+
+ /* Negate the year if necessary */
+ if (str[0] == '-') {
+ out->year = -out->year;
+ }
+ /* Check whether it's a leap-year */
+ year_leap = is_leapyear(out->year);
+
+ /* Next character must be a '-' or the end of the string */
+ if (sublen == 0) {
+ goto finish;
+ }
+ else if (*substr == '-') {
+ ++substr;
+ --sublen;
+ }
+ else {
+ goto parse_error;
+ }
+
+ /* Can't have a trailing '-' */
+ if (sublen == 0) {
+ goto parse_error;
+ }
+
+ /* PARSE THE MONTH (2 digits) */
+ if (sublen >= 2 && isdigit(substr[0]) && isdigit(substr[1])) {
+ out->month = 10 * (substr[0] - '0') + (substr[1] - '0');
+
+ if (out->month < 1 || out->month > 12) {
+ PyErr_Format(PyExc_ValueError,
+ "Month out of range in datetime string \"%s\"", str);
+ goto error;
+ }
+ substr += 2;
+ sublen -= 2;
+ }
+ else {
+ goto parse_error;
+ }
+
+ /* Next character must be a '-' or the end of the string */
+ if (sublen == 0) {
+ goto finish;
+ }
+ else if (*substr == '-') {
+ ++substr;
+ --sublen;
+ }
+ else {
+ goto parse_error;
+ }
+
+ /* Can't have a trailing '-' */
+ if (sublen == 0) {
+ goto parse_error;
+ }
+
+ /* PARSE THE DAY (2 digits) */
+ if (sublen >= 2 && isdigit(substr[0]) && isdigit(substr[1])) {
+ out->day = 10 * (substr[0] - '0') + (substr[1] - '0');
+
+ if (out->day < 1 ||
+ out->day > days_in_month[year_leap][out->month-1]) {
+ PyErr_Format(PyExc_ValueError,
+ "Day out of range in datetime string \"%s\"", str);
+ goto error;
+ }
+ substr += 2;
+ sublen -= 2;
+ }
+ else {
+ goto parse_error;
+ }
+
+ /* Next character must be a 'T', ' ', or end of string */
+ if (sublen == 0) {
+ goto finish;
+ }
+ else if (*substr != 'T' && *substr != ' ') {
+ goto parse_error;
+ }
+ else {
+ ++substr;
+ --sublen;
+ }
+
+ /* PARSE THE HOURS (2 digits) */
+ if (sublen >= 2 && isdigit(substr[0]) && isdigit(substr[1])) {
+ out->hour = 10 * (substr[0] - '0') + (substr[1] - '0');
+
+ if (out->hour < 0 || out->hour >= 24) {
+ PyErr_Format(PyExc_ValueError,
+ "Hours out of range in datetime string \"%s\"", str);
+ goto error;
+ }
+ substr += 2;
+ sublen -= 2;
+ }
+ else {
+ goto parse_error;
+ }
+
+ /* Next character must be a ':' or the end of the string */
+ if (sublen > 0 && *substr == ':') {
+ ++substr;
+ --sublen;
+ }
+ else {
+ goto parse_timezone;
+ }
+
+ /* Can't have a trailing ':' */
+ if (sublen == 0) {
+ goto parse_error;
+ }
+
+ /* PARSE THE MINUTES (2 digits) */
+ if (sublen >= 2 && isdigit(substr[0]) && isdigit(substr[1])) {
+ out->min = 10 * (substr[0] - '0') + (substr[1] - '0');
+
+ if (out->hour < 0 || out->min >= 60) {
+ PyErr_Format(PyExc_ValueError,
+ "Minutes out of range in datetime string \"%s\"", str);
+ goto error;
+ }
+ substr += 2;
+ sublen -= 2;
+ }
+ else {
+ goto parse_error;
+ }
+
+ /* Next character must be a ':' or the end of the string */
+ if (sublen > 0 && *substr == ':') {
+ ++substr;
+ --sublen;
+ }
+ else {
+ goto parse_timezone;
+ }
+
+ /* Can't have a trailing ':' */
+ if (sublen == 0) {
+ goto parse_error;
+ }
+
+ /* PARSE THE SECONDS (2 digits) */
+ if (sublen >= 2 && isdigit(substr[0]) && isdigit(substr[1])) {
+ out->sec = 10 * (substr[0] - '0') + (substr[1] - '0');
+
+ if (out->sec < 0 || out->sec >= 60) {
+ PyErr_Format(PyExc_ValueError,
+ "Seconds out of range in datetime string \"%s\"", str);
+ goto error;
+ }
+ substr += 2;
+ sublen -= 2;
+ }
+ else {
+ goto parse_error;
+ }
+
+ /* Next character may be a '.' indicating fractional seconds */
+ if (sublen > 0 && *substr == '.') {
+ ++substr;
+ --sublen;
+ }
+ else {
+ goto parse_timezone;
+ }
+
+ /* PARSE THE MICROSECONDS (0 to 6 digits) */
+ for (i = 0; i < 6; ++i) {
+ out->us *= 10;
+ if (sublen > 0 && isdigit(*substr)) {
+ out->us += (*substr - '0');
+ ++substr;
+ --sublen;
+ }
+ }
+
+ if (sublen == 0 || !isdigit(*substr)) {
+ goto parse_timezone;
+ }
+
+ /* PARSE THE PICOSECONDS (0 to 6 digits) */
+ for (i = 0; i < 6; ++i) {
+ out->ps *= 10;
+ if (sublen > 0 && isdigit(*substr)) {
+ out->ps += (*substr - '0');
+ ++substr;
+ --sublen;
+ }
+ }
+
+ if (sublen == 0 || !isdigit(*substr)) {
+ goto parse_timezone;
+ }
+
+ /* PARSE THE ATTOSECONDS (0 to 6 digits) */
+ for (i = 0; i < 6; ++i) {
+ out->as *= 10;
+ if (sublen > 0 && isdigit(*substr)) {
+ out->as += (*substr - '0');
+ ++substr;
+ --sublen;
+ }
+ }
+
+parse_timezone:
+ if (sublen == 0) {
+ /*
+ * ISO 8601 states to treat date-times without a timezone offset
+ * or 'Z' for UTC as local time. The C standard libary functions
+ * mktime and gmtime allow us to do this conversion.
+ *
+ * Only do this timezone adjustment for recent and future years.
+ */
+ if (out->year > 1900 && out->year < 10000) {
+ time_t rawtime = 0;
+ struct tm tm_;
+
+ tm_.tm_sec = out->sec;
+ tm_.tm_min = out->min;
+ tm_.tm_hour = out->hour;
+ tm_.tm_mday = out->day;
+ tm_.tm_mon = out->month - 1;
+ tm_.tm_year = out->year - 1900;
+ tm_.tm_isdst = -1;
+
+ /* mktime converts a local 'struct tm' into a time_t */
+ rawtime = mktime(&tm_);
+ if (rawtime == -1) {
+ PyErr_SetString(PyExc_OSError, "Failed to use mktime to "
+ "convert local time to UTC");
+ goto error;
+ }
+
+ /* gmtime converts a 'time_t' into a UTC 'struct tm' */
+#if defined(_WIN32)
+ if (gmtime_s(&tm_, &rawtime) != 0) {
+ PyErr_SetString(PyExc_OSError, "Failed to use gmtime_s to "
+ "get a UTC time");
+ goto error;
+ }
+#else
+ /* Other platforms may require something else */
+ if (gmtime_r(&rawtime, &tm_) == NULL) {
+ PyErr_SetString(PyExc_OSError, "Failed to use gmtime_r to "
+ "get a UTC time");
+ goto error;
+ }
+#endif
+ out->sec = tm_.tm_sec;
+ out->min = tm_.tm_min;
+ out->hour = tm_.tm_hour;
+ out->day = tm_.tm_mday;
+ out->month = tm_.tm_mon + 1;
+ out->year = tm_.tm_year + 1900;
+ }
+
+ goto finish;
+ }
+
+ /* UTC specifier */
+ if (*substr == 'Z') {
+ if (sublen == 1) {
+ goto finish;
+ }
+ else {
+ ++substr;
+ --sublen;
+ }
+ }
+ /* Time zone offset */
+ else if (*substr == '-' || *substr == '+') {
+ int offset_neg = 0, offset_hour = 0, offset_minute = 0;
+ if (*substr == '-') {
+ offset_neg = 1;
+ }
+ ++substr;
+ --sublen;
+
+ /* The hours offset */
+ if (sublen >= 2 && isdigit(substr[0]) && isdigit(substr[1])) {
+ offset_hour = 10 * (substr[0] - '0') + (substr[1] - '0');
+ substr += 2;
+ sublen -= 2;
+ if (offset_hour >= 24) {
+ PyErr_Format(PyExc_ValueError,
+ "Timezone hours offset out of range "
+ "in datetime string \"%s\"", str);
+ goto error;
+ }
+ }
+ else {
+ goto parse_error;
+ }
+
+ /* The minutes offset is optional */
+ if (sublen > 0) {
+ /* Optional ':' */
+ if (*substr == ':') {
+ ++substr;
+ --sublen;
+ }
+
+ /* The minutes offset (at the end of the string) */
+ if (sublen >= 2 && isdigit(substr[0]) && isdigit(substr[1])) {
+ offset_minute = 10 * (substr[0] - '0') + (substr[1] - '0');
+ substr += 2;
+ sublen -= 2;
+ if (offset_minute >= 60) {
+ PyErr_Format(PyExc_ValueError,
+ "Timezone minutes offset out of range "
+ "in datetime string \"%s\"", str);
+ goto error;
+ }
+ }
+ else {
+ goto parse_error;
+ }
+ }
+
+ /* Apply the time zone offset */
+ if (offset_neg) {
+ offset_hour = -offset_hour;
+ offset_minute = -offset_minute;
+ }
+ add_minutes_to_datetimestruct(out, -60 * offset_hour - offset_minute);
+ }
+
+ /* May have a ' ' followed by an event number */
+ if (sublen == 0) {
+ goto finish;
+ }
+ else if (sublen > 0 && *substr == ' ') {
+ ++substr;
+ --sublen;
+
+ while (sublen > 0 && isdigit(*substr)) {
+ out->event = 10 * out->event + (*substr - '0');
+ ++substr;
+ --sublen;
+ }
+ }
+ else {
+ goto parse_error;
+ }
+
+ /* Skip trailing whitespace */
+ while (sublen > 0 && isspace(*substr)) {
+ ++substr;
+ --sublen;
+ }
+
+ if (sublen != 0) {
+ goto parse_error;
+ }
+
+finish:
+ return 0;
+
+parse_error:
+ PyErr_Format(PyExc_ValueError,
+ "Error parsing datetime string \"%s\" at position %d",
+ str, (int)(substr-str));
+ return -1;
+
+error:
+ return -1;
+}
+
+/*
+ * Provides a string length to use for converting datetime
+ * objects with the given local and unit settings.
+ */
+NPY_NO_EXPORT int
+get_datetime_iso_8601_strlen(int local, NPY_DATETIMEUNIT base)
+{
+ int len = 0;
+
+ /* If no unit is provided, return the maximum length */
+ if (base == -1) {
+ return NPY_DATETIME_MAX_ISO8601_STRLEN;
+ }
+
+ switch (base) {
+ case NPY_FR_as:
+ len += 3; /* "###" */
+ case NPY_FR_fs:
+ len += 3; /* "###" */
+ case NPY_FR_ps:
+ len += 3; /* "###" */
+ case NPY_FR_ns:
+ len += 3; /* "###" */
+ case NPY_FR_us:
+ len += 3; /* "###" */
+ case NPY_FR_ms:
+ len += 4; /* ".###" */
+ case NPY_FR_s:
+ len += 3; /* ":##" */
+ case NPY_FR_m:
+ len += 3; /* ":##" */
+ case NPY_FR_h:
+ len += 3; /* "T##" */
+ case NPY_FR_D:
+ case NPY_FR_B:
+ case NPY_FR_W:
+ len += 3; /* "-##" */
+ case NPY_FR_M:
+ len += 3; /* "-##" */
+ case NPY_FR_Y:
+ len += 21; /* 64-bit year */
+ break;
+ }
+
+ if (base >= NPY_FR_h) {
+ if (local) {
+ len += 5; /* "+####" or "-####" */
+ }
+ else {
+ len += 1; /* "Z" */
+ }
+ }
+
+ len += 1; /* NULL terminator */
+
+ return len;
+}
+
+/*
+ * Converts an npy_datetimestruct to an (almost) ISO 8601
+ * NULL-terminated string.
+ *
+ * If 'local' is non-zero, it produces a string in local time with
+ * a +-#### timezone offset, otherwise it uses timezone Z (UTC).
+ *
+ * 'base' restricts the output to that unit. Set 'base' to
+ * -1 to auto-detect a base after which all the values are zero.
+ *
+ * 'tzoffset' is used if 'local' is enabled, and 'tzoffset' is
+ * set to a value other than -1. This is a manual override for
+ * the local time zone to use, as an offset in minutes.
+ *
+ * Returns 0 on success, -1 on failure (for example if the output
+ * string was too short).
+ */
+NPY_NO_EXPORT int
+make_iso_8601_date(npy_datetimestruct *dts, char *outstr, int outlen,
+ int local, NPY_DATETIMEUNIT base, int tzoffset)
+{
+ npy_datetimestruct dts_local;
+ int timezone_offset = 0;
+
+ char *substr = outstr, sublen = outlen;
+ int tmplen;
+
+ /* Handle NaT */
+ if (dts->year == NPY_DATETIME_NAT) {
+ if (outlen < 4) {
+ goto string_too_short;
+ }
+ outstr[0] = 'N';
+ outstr[0] = 'a';
+ outstr[0] = 'T';
+ outstr[0] = '\0';
+
+ return 0;
+ }
+
+ /* Only do local time within a reasonable year range */
+ if ((dts->year <= 1900 || dts->year >= 10000) && tzoffset == -1) {
+ local = 0;
+ }
+
+ /* Automatically detect a good unit */
+ if (base == -1) {
+ if (dts->as % 1000 != 0) {
+ base = NPY_FR_as;
+ }
+ else if (dts->as != 0) {
+ base = NPY_FR_fs;
+ }
+ else if (dts->ps % 1000 != 0) {
+ base = NPY_FR_ps;
+ }
+ else if (dts->ps != 0) {
+ base = NPY_FR_ns;
+ }
+ else if (dts->us % 1000 != 0) {
+ base = NPY_FR_us;
+ }
+ else if (dts->us != 0) {
+ base = NPY_FR_ms;
+ }
+ else if (dts->sec != 0) {
+ base = NPY_FR_s;
+ }
+ /*
+ * hours and minutes don't get split up by default, and printing
+ * in local time forces minutes
+ */
+ else if (local || dts->min != 0 || dts->hour != 0) {
+ base = NPY_FR_m;
+ }
+ /* dates don't get split up by default */
+ else {
+ base = NPY_FR_D;
+ }
+ }
+ /*
+ * Print business days and weeks with the same precision as days.
+ *
+ * TODO: Could print weeks with YYYY-Www format if the week
+ * epoch is a Monday.
+ */
+ else if (base == NPY_FR_B || base == NPY_FR_W) {
+ base = NPY_FR_D;
+ }
+
+ /* Printed dates have no time zone */
+ if (base < NPY_FR_h) {
+ local = 0;
+ }
+
+ /* Use the C API to convert from UTC to local time */
+ if (local && tzoffset == -1) {
+ time_t rawtime = 0, localrawtime;
+ struct tm tm_;
+
+ /*
+ * Convert everything in 'dts' to a time_t, to minutes precision.
+ * This is POSIX time, which skips leap-seconds, but because
+ * we drop the seconds value from the npy_datetimestruct, everything
+ * is ok for this operation.
+ */
+ rawtime = (time_t)get_datetimestruct_days(dts) * 24 * 60 * 60;
+ rawtime += dts->hour * 60 * 60;
+ rawtime += dts->min * 60;
+
+ /* localtime converts a 'time_t' into a local 'struct tm' */
+#if defined(_WIN32)
+ if (localtime_s(&tm_, &rawtime) != 0) {
+ PyErr_SetString(PyExc_OSError, "Failed to use localtime_s to "
+ "get a local time");
+ return -1;
+ }
+#else
+ /* Other platforms may require something else */
+ if (localtime_r(&rawtime, &tm_) == NULL) {
+ PyErr_SetString(PyExc_OSError, "Failed to use localtime_r to "
+ "get a local time");
+ return -1;
+ }
+#endif
+ /* Make a copy of the npy_datetimestruct we can modify */
+ dts_local = *dts;
+
+ /* Copy back all the values except seconds */
+ dts_local.min = tm_.tm_min;
+ dts_local.hour = tm_.tm_hour;
+ dts_local.day = tm_.tm_mday;
+ dts_local.month = tm_.tm_mon + 1;
+ dts_local.year = tm_.tm_year + 1900;
+
+ /* Extract the timezone offset that was applied */
+ rawtime /= 60;
+ localrawtime = (time_t)get_datetimestruct_days(&dts_local) * 24 * 60;
+ localrawtime += dts_local.hour * 60;
+ localrawtime += dts_local.min;
+
+ timezone_offset = localrawtime - rawtime;
+
+ /* Set dts to point to our local time instead of the UTC time */
+ dts = &dts_local;
+ }
+ /* Use the manually provided tzoffset */
+ else if (local) {
+ /* Make a copy of the npy_datetimestruct we can modify */
+ dts_local = *dts;
+ dts = &dts_local;
+
+ /* Set and apply the required timezone offset */
+ timezone_offset = tzoffset;
+ add_minutes_to_datetimestruct(dts, timezone_offset);
+ }
+
+ /* YEAR */
+#ifdef _WIN32
+ tmplen = _snprintf(substr, sublen, "%04" NPY_INT64_FMT, dts->year);
+#else
+ tmplen = snprintf(substr, sublen, "%04" NPY_INT64_FMT, dts->year);
+#endif
+ /* If it ran out of space or there isn't space for the NULL terminator */
+ if (tmplen < 0 || tmplen >= sublen) {
+ goto string_too_short;
+ }
+ substr += tmplen;
+ sublen -= tmplen;
+
+ /* Stop if the unit is years */
+ if (base == NPY_FR_Y) {
+ *substr = '\0';
+ return 0;
+ }
+
+ /* MONTH */
+ substr[0] = '-';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->month / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->month % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is months */
+ if (base == NPY_FR_M) {
+ *substr = '\0';
+ return 0;
+ }
+
+ /* DAY */
+ substr[0] = '-';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->day / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->day % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is days */
+ if (base == NPY_FR_D) {
+ *substr = '\0';
+ return 0;
+ }
+
+ /* HOUR */
+ substr[0] = 'T';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->hour / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->hour % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is hours */
+ if (base == NPY_FR_h) {
+ goto add_time_zone;
+ }
+
+ /* MINUTE */
+ substr[0] = ':';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->min / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->min % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is minutes */
+ if (base == NPY_FR_m) {
+ goto add_time_zone;
+ }
+
+ /* SECOND */
+ substr[0] = ':';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->sec / 10) + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->sec % 10) + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is seconds */
+ if (base == NPY_FR_s) {
+ goto add_time_zone;
+ }
+
+ /* MILLISECOND */
+ substr[0] = '.';
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->us / 100000) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->us / 10000) % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr[3] = (char)((dts->us / 1000) % 10 + '0');
+ if (sublen <= 4 ) {
+ goto string_too_short;
+ }
+ substr += 4;
+ sublen -= 4;
+
+ /* Stop if the unit is milliseconds */
+ if (base == NPY_FR_ms) {
+ goto add_time_zone;
+ }
+
+ /* MICROSECOND */
+ substr[0] = (char)((dts->us / 100) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->us / 10) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)(dts->us % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is microseconds */
+ if (base == NPY_FR_us) {
+ goto add_time_zone;
+ }
+
+ /* NANOSECOND */
+ substr[0] = (char)((dts->ps / 100000) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->ps / 10000) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->ps / 1000) % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is nanoseconds */
+ if (base == NPY_FR_ns) {
+ goto add_time_zone;
+ }
+
+ /* PICOSECOND */
+ substr[0] = (char)((dts->ps / 100) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->ps / 10) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)(dts->ps % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is picoseconds */
+ if (base == NPY_FR_ps) {
+ goto add_time_zone;
+ }
+
+ /* FEMTOSECOND */
+ substr[0] = (char)((dts->as / 100000) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->as / 10000) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)((dts->as / 1000) % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+ /* Stop if the unit is femtoseconds */
+ if (base == NPY_FR_fs) {
+ goto add_time_zone;
+ }
+
+ /* ATTOSECOND */
+ substr[0] = (char)((dts->as / 100) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((dts->as / 10) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)(dts->as % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr += 3;
+ sublen -= 3;
+
+add_time_zone:
+ if (local) {
+ /* Add the +/- sign */
+ if (timezone_offset < 0) {
+ substr[0] = '-';
+ timezone_offset = -timezone_offset;
+ }
+ else {
+ substr[0] = '+';
+ }
+ if (sublen <= 1) {
+ goto string_too_short;
+ }
+ substr += 1;
+ sublen -= 1;
+
+ /* Add the timezone offset */
+ substr[0] = (char)((timezone_offset / (10*60)) % 10 + '0');
+ if (sublen <= 1 ) {
+ goto string_too_short;
+ }
+ substr[1] = (char)((timezone_offset / 60) % 10 + '0');
+ if (sublen <= 2 ) {
+ goto string_too_short;
+ }
+ substr[2] = (char)(((timezone_offset % 60) / 10) % 10 + '0');
+ if (sublen <= 3 ) {
+ goto string_too_short;
+ }
+ substr[3] = (char)((timezone_offset % 60) % 10 + '0');
+ if (sublen <= 4 ) {
+ goto string_too_short;
+ }
+ substr += 4;
+ sublen -= 4;
+ }
+ /* UTC "Zulu" time */
+ else {
+ substr[0] = 'Z';
+ if (sublen <= 1) {
+ goto string_too_short;
+ }
+ substr += 1;
+ sublen -= 1;
+ }
+
+ /* Add a NULL terminator, and return */
+ substr[0] = '\0';
+
+ return 0;
+
+string_too_short:
+ /* Put a NULL terminator on anyway */
+ if (outlen > 0) {
+ outstr[outlen-1] = '\0';
+ }
+
+ PyErr_Format(PyExc_RuntimeError,
+ "The string provided for NumPy ISO datetime formatting "
+ "was too short, with length %d",
+ outlen);
+ return -1;
+}
+
+/*
+ * Tests for and converts a Python datetime.datetime or datetime.date
+ * object into a NumPy npy_datetimestruct.
+ *
+ * While the C API has PyDate_* and PyDateTime_* functions, the following
+ * implementation just asks for attributes, and thus supports
+ * datetime duck typing. The tzinfo time zone conversion would require
+ * this style of access anyway.
+ *
+ * Returns -1 on error, 0 on success, and 1 (with no error set)
+ * if obj doesn't have the neeeded date or datetime attributes.
+ */
+NPY_NO_EXPORT int
+convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out)
+{
+ PyObject *tmp;
+ int isleap;
+
+ /* Initialize the output to all zeros */
+ memset(out, 0, sizeof(npy_datetimestruct));
+ out->month = 1;
+ out->day = 1;
+
+ /* Need at least year/month/day attributes */
+ if (!PyObject_HasAttrString(obj, "year") ||
+ !PyObject_HasAttrString(obj, "month") ||
+ !PyObject_HasAttrString(obj, "day")) {
+ return 1;
+ }
+
+ /* Get the year */
+ tmp = PyObject_GetAttrString(obj, "year");
+ if (tmp == NULL) {
+ return -1;
+ }
+ out->year = PyInt_AsLong(tmp);
+ if (out->year == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /* Get the month */
+ tmp = PyObject_GetAttrString(obj, "month");
+ if (tmp == NULL) {
+ return -1;
+ }
+ out->month = PyInt_AsLong(tmp);
+ if (out->month == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /* Get the day */
+ tmp = PyObject_GetAttrString(obj, "day");
+ if (tmp == NULL) {
+ return -1;
+ }
+ out->day = PyInt_AsLong(tmp);
+ if (out->day == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /* Validate that the month and day are valid for the year */
+ if (out->month < 1 || out->month > 12) {
+ goto invalid_date;
+ }
+ isleap = is_leapyear(out->year);
+ if (out->day < 1 || out->day > days_in_month[isleap][out->month-1]) {
+ goto invalid_date;
+ }
+
+ /* Check for time attributes (if not there, return success as a date) */
+ if (!PyObject_HasAttrString(obj, "hour") ||
+ !PyObject_HasAttrString(obj, "minute") ||
+ !PyObject_HasAttrString(obj, "second") ||
+ !PyObject_HasAttrString(obj, "microsecond")) {
+ return 0;
+ }
+
+ /* Get the hour */
+ tmp = PyObject_GetAttrString(obj, "hour");
+ if (tmp == NULL) {
+ return -1;
+ }
+ out->hour = PyInt_AsLong(tmp);
+ if (out->hour == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /* Get the minute */
+ tmp = PyObject_GetAttrString(obj, "minute");
+ if (tmp == NULL) {
+ return -1;
+ }
+ out->min = PyInt_AsLong(tmp);
+ if (out->min == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /* Get the second */
+ tmp = PyObject_GetAttrString(obj, "second");
+ if (tmp == NULL) {
+ return -1;
+ }
+ out->sec = PyInt_AsLong(tmp);
+ if (out->sec == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /* Get the microsecond */
+ tmp = PyObject_GetAttrString(obj, "microsecond");
+ if (tmp == NULL) {
+ return -1;
+ }
+ out->us = PyInt_AsLong(tmp);
+ if (out->us == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ if (out->hour < 0 || out->hour >= 24 ||
+ out->min < 0 || out->min >= 60 ||
+ out->sec < 0 || out->sec >= 60 ||
+ out->us < 0 || out->us >= 1000000) {
+ goto invalid_time;
+ }
+
+ /* Apply the time zone offset if it exists */
+ if (PyObject_HasAttrString(obj, "tzinfo")) {
+ tmp = PyObject_GetAttrString(obj, "tzinfo");
+ if (tmp == NULL) {
+ return -1;
+ }
+ if (tmp == Py_None) {
+ Py_DECREF(tmp);
+ }
+ else {
+ PyObject *offset;
+ int seconds_offset, minutes_offset;
+
+ /* The utcoffset function should return a timedelta */
+ offset = PyObject_CallMethod(tmp, "utcoffset", "O", obj);
+ if (offset == NULL) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /*
+ * The timedelta should have an attribute "seconds"
+ * which contains the value we want.
+ */
+ tmp = PyObject_GetAttrString(obj, "seconds");
+ if (tmp == NULL) {
+ return -1;
+ }
+ seconds_offset = PyInt_AsLong(tmp);
+ if (seconds_offset == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /* Convert to a minutes offset and apply it */
+ minutes_offset = seconds_offset / 60;
+
+ add_minutes_to_datetimestruct(out, -minutes_offset);
+ }
+ }
+
+ return 0;
+
+invalid_date:
+ PyErr_Format(PyExc_ValueError,
+ "Invalid date (%d,%d,%d) when converting to NumPy datetime",
+ (int)out->year, (int)out->month, (int)out->day);
+ return -1;
+
+invalid_time:
+ PyErr_Format(PyExc_ValueError,
+ "Invalid time (%d,%d,%d,%d) when converting "
+ "to NumPy datetime",
+ (int)out->hour, (int)out->min, (int)out->sec, (int)out->us);
+ return -1;
+}
+
+/*
+ * Converts a PyObject * into a datetime, in any of the forms supported
+ *
+ * Returns -1 on error, 0 on success.
+ */
+NPY_NO_EXPORT int
+convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj,
+ npy_datetime *out)
+{
+ if (PyBytes_Check(obj) || PyUnicode_Check(obj)) {
+ PyObject *bytes = NULL;
+ char *str = NULL;
+ int len = 0;
+ npy_datetimestruct dts;
+ /* Convert to an ASCII string for the date parser */
+ if (PyUnicode_Check(obj)) {
+ bytes = PyUnicode_AsASCIIString(obj);
+ if (bytes == NULL) {
+ return -1;
+ }
+ }
+ else {
+ bytes = obj;
+ Py_INCREF(bytes);
+ }
+ if (PyBytes_AsStringAndSize(bytes, &str, &len) == -1) {
+ Py_DECREF(bytes);
+ return -1;
+ }
+
+ /* Parse the ISO date */
+ if (parse_iso_8601_date(str, len, &dts) < 0) {
+ Py_DECREF(bytes);
+ return -1;
+ }
+ Py_DECREF(bytes);
+
+ if (convert_datetimestruct_to_datetime(meta, &dts, out) < 0) {
+ return -1;
+ }
+
+ return 0;
+ }
+ /* Do no conversion on raw integers */
+ else if (PyInt_Check(obj)) {
+ *out = PyInt_AS_LONG(obj);
+ return 0;
+ }
+ else if (PyLong_Check(obj)) {
+ *out = PyLong_AsLongLong(obj);
+ return 0;
+ }
+ /* Could be a tuple with event number in the second entry */
+ else if (PyTuple_Check(obj) && PyTuple_Size(obj) == 2) {
+ int event, event_old;
+ if (convert_pyobject_to_datetime(meta, PyTuple_GET_ITEM(obj, 0),
+ out) < 0) {
+ return -1;
+ }
+ event = (int)PyInt_AsLong(PyTuple_GET_ITEM(obj, 1));
+ if (event == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ if (event < 0 || event >= meta->events) {
+ PyErr_SetString(PyExc_ValueError, "event value for NumPy "
+ "datetime is out of range");
+ return -1;
+ }
+ /* Replace the event with the one from the tuple */
+ event_old = *out % meta->events;
+ if (event_old < 0) {
+ event_old += meta->events;
+ }
+ *out = *out - event_old + event;
+
+ return 0;
+ }
+ /* Datetime scalar */
+ else if (PyArray_IsScalar(obj, Datetime)) {
+ PyDatetimeScalarObject *dts = (PyDatetimeScalarObject *)obj;
+
+ return cast_datetime_to_datetime(&dts->obmeta, meta, dts->obval, out);
+ }
+ /* Datetime zero-dimensional array */
+ else if (PyArray_Check(obj) &&
+ PyArray_NDIM(obj) == 0 &&
+ PyArray_DESCR(obj)->type_num == NPY_DATETIME) {
+ PyArray_DatetimeMetaData *obj_meta;
+ npy_datetime dt = 0;
+
+ obj_meta = get_datetime_metadata_from_dtype(PyArray_DESCR(obj));
+ if (obj_meta == NULL) {
+ return -1;
+ }
+ PyArray_DESCR(obj)->f->copyswap(&dt,
+ PyArray_DATA(obj),
+ !PyArray_ISNOTSWAPPED(obj),
+ obj);
+
+ return cast_datetime_to_datetime(obj_meta, meta, dt, out);
+ }
+ /* Convert from a Python date or datetime object */
+ else {
+ int code;
+ npy_datetimestruct dts;
+
+ code = convert_pydatetime_to_datetimestruct(obj, &dts);
+ if (code == -1) {
+ return -1;
+ }
+ else if (code == 0) {
+ if (convert_datetimestruct_to_datetime(meta, &dts, out) < 0) {
+ return -1;
+ }
+
+ return 0;
+ }
+ }
+
+ PyErr_SetString(PyExc_ValueError,
+ "Could not convert object to NumPy datetime");
+ return -1;
+}
+
+/*
+ * Converts a PyObject * into a timedelta, in any of the forms supported
+ *
+ * Returns -1 on error, 0 on success.
+ */
+NPY_NO_EXPORT int
+convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj,
+ npy_timedelta *out)
+{
+ /* Do no conversion on raw integers */
+ if (PyInt_Check(obj)) {
+ *out = PyInt_AS_LONG(obj);
+ return 0;
+ }
+ else if (PyLong_Check(obj)) {
+ *out = PyLong_AsLongLong(obj);
+ return 0;
+ }
+ /* Timedelta scalar */
+ else if (PyArray_IsScalar(obj, Timedelta)) {
+ PyTimedeltaScalarObject *dts = (PyTimedeltaScalarObject *)obj;
+
+ return cast_timedelta_to_timedelta(&dts->obmeta, meta,
+ dts->obval, out);
+ }
+ /* Timedelta zero-dimensional array */
+ else if (PyArray_Check(obj) &&
+ PyArray_NDIM(obj) == 0 &&
+ PyArray_DESCR(obj)->type_num == NPY_TIMEDELTA) {
+ PyArray_DatetimeMetaData *obj_meta;
+ npy_timedelta dt = 0;
+
+ obj_meta = get_datetime_metadata_from_dtype(PyArray_DESCR(obj));
+ if (obj_meta == NULL) {
+ return -1;
+ }
+ PyArray_DESCR(obj)->f->copyswap(&dt,
+ PyArray_DATA(obj),
+ !PyArray_ISNOTSWAPPED(obj),
+ obj);
+
+ return cast_timedelta_to_timedelta(obj_meta, meta, dt, out);
+ }
+ /* Convert from a Python timedelta object */
+ else if (PyObject_HasAttrString(obj, "days") &&
+ PyObject_HasAttrString(obj, "seconds") &&
+ PyObject_HasAttrString(obj, "microseconds")) {
+ PyObject *tmp;
+ PyArray_DatetimeMetaData us_meta;
+ npy_timedelta td;
+ npy_int64 days;
+ int seconds = 0, useconds = 0;
+
+ /* Get the days */
+ tmp = PyObject_GetAttrString(obj, "days");
+ if (tmp == NULL) {
+ return -1;
+ }
+ days = PyLong_AsLongLong(tmp);
+ if (days == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /* Get the seconds */
+ tmp = PyObject_GetAttrString(obj, "seconds");
+ if (tmp == NULL) {
+ return -1;
+ }
+ seconds = PyInt_AsLong(tmp);
+ if (seconds == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /* Get the microseconds */
+ tmp = PyObject_GetAttrString(obj, "microseconds");
+ if (tmp == NULL) {
+ return -1;
+ }
+ useconds = PyInt_AsLong(tmp);
+ if (useconds == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ Py_DECREF(tmp);
+
+ /*
+ * Convert to a microseconds timedelta, then cast to the
+ * desired units.
+ */
+ td = days*(24*60*60*1000000LL) + seconds*1000000LL + useconds;
+ us_meta.base = NPY_FR_us;
+ us_meta.num = 1;
+ us_meta.events = 1;
+
+ return cast_timedelta_to_timedelta(&us_meta, meta, td, out);
+ }
+
+ PyErr_SetString(PyExc_ValueError,
+ "Could not convert object to NumPy timedelta");
+ return -1;
+}
+
+/*
+ * Converts a datetime into a PyObject *.
+ *
+ * Not-a-time is returned as the string "NaT".
+ * For days or coarser, returns a datetime.date.
+ * For microseconds or coarser, returns a datetime.datetime.
+ * For units finer than microseconds, returns an integer.
+ */
+NPY_NO_EXPORT PyObject *
+convert_datetime_to_pyobject(npy_datetime dt, PyArray_DatetimeMetaData *meta)
+{
+ PyObject *ret = NULL, *tup = NULL;
+ npy_datetimestruct dts;
+
+ /* Handle not-a-time */
+ if (dt == NPY_DATETIME_NAT) {
+ return PyUString_FromString("NaT");
+ }
+
+ /* If the type's precision is greater than microseconds, return an int */
+ if (meta->base > NPY_FR_us) {
+ /* Skip use of a tuple for the events, just return the raw int */
+ return PyLong_FromLongLong(dt);
+ }
+
+ /* Convert to a datetimestruct */
+ if (convert_datetime_to_datetimestruct(meta, dt, &dts) < 0) {
+ return NULL;
+ }
+
+ /*
+ * If the year is outside the range of years supported by Python's
+ * datetime, or the datetime64 falls on a leap second,
+ * return a raw int.
+ */
+ if (dts.year < 1 || dts.year > 9999 || dts.sec == 60) {
+ /* Also skip use of a tuple for the events */
+ return PyLong_FromLongLong(dt);
+ }
+
+ /* If the type's precision is greater than days, return a datetime */
+ if (meta->base > NPY_FR_D) {
+ ret = PyDateTime_FromDateAndTime(dts.year, dts.month, dts.day,
+ dts.hour, dts.min, dts.sec, dts.us);
+ }
+ /* Otherwise return a date */
+ else {
+ ret = PyDate_FromDate(dts.year, dts.month, dts.day);
+ }
+
+ /* If there is one event, just return the datetime */
+ if (meta->events == 1) {
+ return ret;
+ }
+ /* Otherwise return a tuple with the event in the second position */
+ else {
+ tup = PyTuple_New(2);
+ if (tup == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tup, 0, ret);
+
+ ret = PyInt_FromLong(dts.event);
+ if (ret == NULL) {
+ Py_DECREF(tup);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tup, 1, ret);
+
+ return tup;
+ }
+}
+
+/*
+ * Converts a timedelta into a PyObject *.
+ *
+ * Not-a-time is returned as the string "NaT".
+ * For microseconds or coarser, returns a datetime.timedelta.
+ * For units finer than microseconds, returns an integer.
+ */
+NPY_NO_EXPORT PyObject *
+convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta)
+{
+ PyObject *ret = NULL, *tup = NULL;
+ npy_timedelta value;
+ int event = 0;
+ int days = 0, seconds = 0, useconds = 0;
+
+ /* Handle not-a-time */
+ if (td == NPY_DATETIME_NAT) {
+ return PyUString_FromString("NaT");
+ }
+
+ /*
+ * If the type's precision is greater than microseconds or is
+ * Y/M/B (nonlinear units), return an int
+ */
+ if (meta->base > NPY_FR_us ||
+ meta->base == NPY_FR_Y ||
+ meta->base == NPY_FR_M ||
+ meta->base == NPY_FR_B) {
+ /* Skip use of a tuple for the events, just return the raw int */
+ return PyLong_FromLongLong(td);
+ }
+
+ value = td;
+
+ /* If there are events, extract the event */
+ if (meta->events > 1) {
+ event = (int)(value % meta->events);
+ value = value / meta->events;
+ if (event < 0) {
+ --value;
+ event += meta->events;
+ }
+ }
+
+ /* Apply the unit multiplier (TODO: overflow treatment...) */
+ value *= meta->num;
+
+ /* Convert to days/seconds/useconds */
+ switch (meta->base) {
+ case NPY_FR_W:
+ value *= 7;
+ break;
+ case NPY_FR_D:
+ break;
+ case NPY_FR_h:
+ seconds = (int)((value % 24) * (60*60));
+ value = value / 24;
+ break;
+ case NPY_FR_m:
+ seconds = (int)(value % (24*60)) * 60;
+ value = value / (24*60);
+ break;
+ case NPY_FR_s:
+ seconds = (int)(value % (24*60*60));
+ value = value / (24*60*60);
+ break;
+ case NPY_FR_ms:
+ useconds = (int)(value % 1000) * 1000;
+ value = value / 1000;
+ seconds = (int)(value % (24*60*60));
+ value = value / (24*60*60);
+ break;
+ case NPY_FR_us:
+ useconds = (int)(value % (1000*1000));
+ value = value / (1000*1000);
+ seconds = (int)(value % (24*60*60));
+ value = value / (24*60*60);
+ break;
+ default:
+ break;
+ }
+ /*
+ * 'value' represents days, and seconds/useconds are filled.
+ *
+ * If it would overflow the datetime.timedelta days, return a raw int
+ */
+ if (value < -999999999 || value > 999999999) {
+ return PyLong_FromLongLong(td);
+ }
+ else {
+ days = (int)value;
+ ret = PyDelta_FromDSU(days, seconds, useconds);
+ if (ret == NULL) {
+ return NULL;
+ }
+ }
+
+ /* If there is one event, just return the datetime */
+ if (meta->events == 1) {
+ return ret;
+ }
+ /* Otherwise return a tuple with the event in the second position */
+ else {
+ tup = PyTuple_New(2);
+ if (tup == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tup, 0, ret);
+
+ ret = PyInt_FromLong(event);
+ if (ret == NULL) {
+ Py_DECREF(tup);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tup, 1, ret);
+
+ return tup;
+ }
+}
+
+/*
+ * Returns true if the datetime metadata matches
+ */
+NPY_NO_EXPORT npy_bool
+has_equivalent_datetime_metadata(PyArray_Descr *type1, PyArray_Descr *type2)
+{
+ PyArray_DatetimeMetaData *meta1, *meta2;
+
+ if ((type1->type_num != NPY_DATETIME &&
+ type1->type_num != NPY_TIMEDELTA) ||
+ (type2->type_num != NPY_DATETIME &&
+ type2->type_num != NPY_TIMEDELTA)) {
+ return 0;
+ }
+
+ meta1 = get_datetime_metadata_from_dtype(type1);
+ if (meta1 == NULL) {
+ PyErr_Clear();
+ return 0;
+ }
+ meta2 = get_datetime_metadata_from_dtype(type2);
+ if (meta2 == NULL) {
+ PyErr_Clear();
+ return 0;
+ }
+
+ return meta1->base == meta2->base &&
+ meta1->num == meta2->num &&
+ meta1->events == meta2->events;
+}
+
+/*
+ * Casts a single datetime from having src_meta metadata into
+ * dst_meta metadata.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+cast_datetime_to_datetime(PyArray_DatetimeMetaData *src_meta,
+ PyArray_DatetimeMetaData *dst_meta,
+ npy_datetime src_dt,
+ npy_datetime *dst_dt)
+{
+ npy_datetimestruct dts;
+
+ /* If the metadata is the same, short-circuit the conversion */
+ if (src_meta->base == dst_meta->base &&
+ src_meta->num == dst_meta->num &&
+ src_meta->events == dst_meta->events) {
+ *dst_dt = src_dt;
+ return 0;
+ }
+
+ /* Otherwise convert through a datetimestruct */
+ if (convert_datetime_to_datetimestruct(src_meta, src_dt, &dts) < 0) {
+ *dst_dt = NPY_DATETIME_NAT;
+ return -1;
+ }
+ if (dts.event >= dst_meta->events) {
+ dts.event = dts.event % dst_meta->events;
+ }
+ if (convert_datetimestruct_to_datetime(dst_meta, &dts, dst_dt) < 0) {
+ *dst_dt = NPY_DATETIME_NAT;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Casts a single timedelta from having src_meta metadata into
+ * dst_meta metadata.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+cast_timedelta_to_timedelta(PyArray_DatetimeMetaData *src_meta,
+ PyArray_DatetimeMetaData *dst_meta,
+ npy_timedelta src_dt,
+ npy_timedelta *dst_dt)
+{
+ npy_int64 num = 0, denom = 0;
+ int event = 0;
+
+ /* If the metadata is the same, short-circuit the conversion */
+ if (src_meta->base == dst_meta->base &&
+ src_meta->num == dst_meta->num &&
+ src_meta->events == dst_meta->events) {
+ *dst_dt = src_dt;
+ return 0;
+ }
+
+ /* Get the conversion factor */
+ get_datetime_conversion_factor(src_meta, dst_meta, &num, &denom);
+
+ if (num == 0) {
+ PyErr_SetString(PyExc_OverflowError,
+ "Integer overflow getting a conversion factor between "
+ "different timedelta types");
+ return -1;
+ }
+
+ /* Remove the event number from the value */
+ if (src_meta->events > 1) {
+ event = (int)(src_dt % src_meta->events);
+ src_dt = src_dt / src_meta->events;
+ if (event < 0) {
+ --src_dt;
+ event += src_meta->events;
+ }
+ }
+
+ /* Apply the scaling */
+ if (src_dt < 0) {
+ *dst_dt = (src_dt * num - (denom - 1)) / denom;
+ }
+ else {
+ *dst_dt = src_dt * num / denom;
+ }
+
+ /* Add the event number back in */
+ if (dst_meta->events > 1) {
+ event = event % dst_meta->events;
+ *dst_dt = (*dst_dt) * dst_meta->events + event;
+ }
+
+ return 0;
+}
+
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c
index bf4ea27a6..1fe8f748d 100644
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -13,6 +13,7 @@
#include "numpy/npy_3kcompat.h"
+#include "_datetime.h"
#include "common.h"
#define _chk_byteorder(arg) (arg == '>' || arg == '<' || \
@@ -102,9 +103,9 @@ array_set_typeDict(PyObject *NPY_UNUSED(ignored), PyObject *args)
}
static int
-_check_for_commastring(char *type, int len)
+_check_for_commastring(char *type, Py_ssize_t len)
{
- int i;
+ Py_ssize_t i;
/* Check for ints at start of string */
if ((type[0] >= '0'
@@ -135,9 +136,9 @@ _check_for_commastring(char *type, int len)
}
static int
-_check_for_datetime(char *type, int len)
+is_datetime_typestr(char *type, Py_ssize_t len)
{
- if (len < 1) {
+ if (len < 2) {
return 0;
}
if (type[1] == '8' && (type[0] == 'M' || type[0] == 'm')) {
@@ -251,7 +252,7 @@ _convert_from_tuple(PyObject *obj)
newdescr->elsize = type->elsize;
newdescr->elsize *= PyArray_MultiplyList(shape.ptr, shape.len);
PyDimMem_FREE(shape.ptr);
- newdescr->subarray = _pya_malloc(sizeof(PyArray_ArrayDescr));
+ newdescr->subarray = PyArray_malloc(sizeof(PyArray_ArrayDescr));
newdescr->flags = type->flags;
newdescr->subarray->base = type;
type = NULL;
@@ -535,262 +536,6 @@ _convert_from_list(PyObject *obj, int align)
return NULL;
}
-/* Exported as DATETIMEUNITS in multiarraymodule.c */
-NPY_NO_EXPORT char *_datetime_strings[] = {
- NPY_STR_Y,
- NPY_STR_M,
- NPY_STR_W,
- NPY_STR_B,
- NPY_STR_D,
- NPY_STR_h,
- NPY_STR_m,
- 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 NPY_DATETIMEUNIT _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;
- NPY_DATETIMEUNIT *totry;
- NPY_DATETIMEUNIT *baseunit;
- int q, r;
-
- ind = ((int)meta->base - (int)NPY_FR_Y)*2;
- totry = _multiples_table[ind];
- baseunit = _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 = _multiples_table[ind + 1];
- baseunit[0] = meta->base + 1;
- baseunit[1] = meta->base + 2;
- if (meta->base == NPY_DATETIME_NUMUNITS - 2) {
- num = 1;
- }
- if (meta->base == NPY_DATETIME_NUMUNITS - 1) {
- num = 0;
- }
- }
-
- 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 = 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 = NpyCapsule_AsVoidPtr(cobj);
- dt_tuple = PyTuple_New(4);
-
- PyTuple_SET_ITEM(dt_tuple, 0,
- PyBytes_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;
- PyObject *ret;
-
- dt_data = _pya_malloc(sizeof(PyArray_DatetimeMetaData));
- dt_data->base = _unit_from_str(
- PyBytes_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;
- }
- }
-
-/* FIXME
- * There is no error handling here.
- */
- ret = NpyCapsule_FromVoidPtr((void *)dt_data, simple_capsule_dtor);
- return ret;
-}
-
-static PyArray_Descr *
-_convert_from_datetime_tuple(PyObject *obj)
-{
- PyArray_Descr *new;
- PyObject *dt_tuple;
- PyObject *dt_cobj;
- PyObject *datetime_flag;
-
- 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_flag = PyTuple_GET_ITEM(obj, 1);
- if (!PyTuple_Check(dt_tuple)
- || PyTuple_GET_SIZE(dt_tuple) != 4
- || !PyInt_Check(datetime_flag)) {
- 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_flag)) {
- new = PyArray_DescrNewFromType(PyArray_DATETIME);
- }
- else {
- new = PyArray_DescrNewFromType(PyArray_TIMEDELTA);
- }
-
- if (new == NULL) {
- return NULL;
- }
- /*
- * Remove any reference to old metadata dictionary
- * And create a new one for this new dtype
- */
- Py_XDECREF(new->metadata);
- if ((new->metadata = PyDict_New()) == NULL) {
- 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_SetItemString(new->metadata, NPY_METADATA_DTSTR, 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 (!PyBytes_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
@@ -1288,20 +1033,14 @@ PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at)
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;
+ if (is_datetime_typestr(type, len)) {
+ *at = parse_dtype_from_datetime_typestr(type, len);
+ return (*at) ? PY_SUCCEED : 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);
- if (*at) {
- return PY_SUCCEED;
- }
- return PY_FAIL;
+ return (*at) ? PY_SUCCEED : PY_FAIL;
}
check_num = (int) type[0];
if ((char) check_num == '>'
@@ -1438,7 +1177,13 @@ PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at)
return PY_SUCCEED;
fail:
- PyErr_SetString(PyExc_TypeError, "data type not understood");
+ if (PyBytes_Check(obj)) {
+ PyErr_Format(PyExc_TypeError, "data type \"%s\" not understood",
+ PyBytes_AS_STRING(obj));
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "data type not understood");
+ }
*at = NULL;
return PY_FAIL;
}
@@ -1479,7 +1224,7 @@ PyArray_DescrNew(PyArray_Descr *base)
Py_XINCREF(new->fields);
Py_XINCREF(new->names);
if (new->subarray) {
- new->subarray = _pya_malloc(sizeof(PyArray_ArrayDescr));
+ new->subarray = PyArray_malloc(sizeof(PyArray_ArrayDescr));
memcpy(new->subarray, base->subarray, sizeof(PyArray_ArrayDescr));
Py_INCREF(new->subarray->shape);
Py_INCREF(new->subarray->base);
@@ -1511,7 +1256,7 @@ arraydescr_dealloc(PyArray_Descr *self)
if (self->subarray) {
Py_XDECREF(self->subarray->shape);
Py_DECREF(self->subarray->base);
- _pya_free(self->subarray);
+ PyArray_free(self->subarray);
}
Py_XDECREF(self->metadata);
Py_TYPE(self)->tp_free((PyObject *)self);
@@ -1554,48 +1299,6 @@ arraydescr_subdescr_get(PyArray_Descr *self)
(PyObject *)self->subarray->base, 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 = NpyCapsule_AsVoidPtr(tmp);
- num = dt_data->num;
- den = dt_data->den;
- events = dt_data->events;
- basestr = _datetime_strings[dt_data->base];
-
- if (num == 1) {
- tmp = PyUString_FromString(basestr);
- }
- else {
- tmp = PyUString_FromFormat("%d%s", num, basestr);
- }
- if (den != 1) {
- res = PyUString_FromFormat("/%d", den);
- PyUString_ConcatAndDel(&tmp, res);
- }
-
- res = PyUString_FromString("[");
- PyUString_ConcatAndDel(&res, tmp);
- PyUString_ConcatAndDel(&res, PyUString_FromString("]"));
- if (events != 1) {
- tmp = PyUString_FromFormat("//%d", events);
- PyUString_ConcatAndDel(&res, tmp);
- }
- PyUString_ConcatAndDel(&ret, res);
- return ret;
-}
-
NPY_NO_EXPORT PyObject *
arraydescr_protocol_typestr_get(PyArray_Descr *self)
{
@@ -1616,7 +1319,15 @@ arraydescr_protocol_typestr_get(PyArray_Descr *self)
ret = PyUString_FromFormat("%c%c%d", endian, basic_, size);
if (PyDataType_ISDATETIME(self)) {
- ret = _append_to_datetime_typestr(self, ret);
+ PyArray_DatetimeMetaData *meta;
+
+ meta = get_datetime_metadata_from_dtype(self);
+ if (meta == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ ret = append_metastr_to_string(meta, 0, ret);
}
return ret;
@@ -1659,7 +1370,15 @@ arraydescr_typename_get(PyArray_Descr *self)
PyUString_ConcatAndDel(&res, p);
}
if (PyDataType_ISDATETIME(self)) {
- res = _append_to_datetime_typestr(self, res);
+ PyArray_DatetimeMetaData *meta;
+
+ meta = get_datetime_metadata_from_dtype(self);
+ if (meta == NULL) {
+ Py_DECREF(res);
+ return NULL;
+ }
+
+ res = append_metastr_to_string(meta, 0, res);
}
return res;
@@ -2042,27 +1761,50 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), PyObject *args, PyObject *kwds
* (cleaned metadata dictionary, tuple with (str, num, events))
*/
static PyObject *
-_get_pickleabletype_from_metadata(PyObject *metadata)
+_get_pickleabletype_from_datetime_metadata(PyArray_Descr *dtype)
{
PyObject *newdict;
- PyObject *newtup, *dt_tuple;
- PyObject *cobj;
+ PyObject *ret, *dt_tuple;
+ PyArray_DatetimeMetaData *meta;
- newdict = PyDict_Copy(metadata);
- PyDict_DelItemString(newdict, NPY_METADATA_DTSTR);
- newtup = PyTuple_New(2);
- PyTuple_SET_ITEM(newtup, 0, newdict);
+ /* Create the 2-item tuple to return */
+ ret = PyTuple_New(2);
+ if (ret == NULL) {
+ return NULL;
+ }
- cobj = PyDict_GetItemString(metadata, NPY_METADATA_DTSTR);
- dt_tuple = _get_datetime_tuple_from_cobj(cobj);
+ /* Make a cleaned copy of the metadata dictionary */
+ newdict = PyDict_Copy(dtype->metadata);
+ if (newdict == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ PyDict_DelItemString(newdict, NPY_METADATA_DTSTR);
+ PyTuple_SET_ITEM(ret, 0, newdict);
- PyTuple_SET_ITEM(newtup, 1, dt_tuple);
+ /* Convert the datetime metadata into a tuple */
+ meta = get_datetime_metadata_from_dtype(dtype);
+ if (meta == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ dt_tuple = convert_datetime_metadata_to_tuple(meta);
+ if (dt_tuple == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(ret, 1, dt_tuple);
- return newtup;
+ return ret;
}
-
-/* return a tuple of (callable object, args, state). */
+/*
+ * return a tuple of (callable object, args, state).
+ *
+ * TODO: This method needs to change so that unpickling doesn't
+ * use __setstate__. This is required for the dtype
+ * to be an immutable object.
+ */
static PyObject *
arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args))
{
@@ -2129,7 +1871,12 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args))
* 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);
+ newobj = _get_pickleabletype_from_datetime_metadata(self);
+ if (newobj == NULL) {
+ Py_DECREF(state);
+ Py_DECREF(ret);
+ return NULL;
+ }
PyTuple_SET_ITEM(state, 8, newobj);
}
else {
@@ -2357,7 +2104,7 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args)
if (self->subarray) {
Py_XDECREF(self->subarray->base);
Py_XDECREF(self->subarray->shape);
- _pya_free(self->subarray);
+ PyArray_free(self->subarray);
}
self->subarray = NULL;
@@ -2401,7 +2148,10 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args)
return NULL;
}
- self->subarray = _pya_malloc(sizeof(PyArray_ArrayDescr));
+ self->subarray = PyArray_malloc(sizeof(PyArray_ArrayDescr));
+ if (self->subarray == NULL) {
+ return PyErr_NoMemory();
+ }
self->subarray->base = (PyArray_Descr *)PyTuple_GET_ITEM(subarray, 0);
Py_INCREF(self->subarray->base);
self->subarray->shape = subarray_shape;
@@ -2435,7 +2185,11 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args)
PyObject *cobj;
self->metadata = PyTuple_GET_ITEM(metadata, 0);
Py_INCREF(self->metadata);
- cobj = _convert_datetime_tuple_to_cobj(PyTuple_GET_ITEM(metadata, 1));
+ cobj = convert_datetime_metadata_tuple_to_metacobj(
+ PyTuple_GET_ITEM(metadata, 1));
+ if (cobj == NULL) {
+ return NULL;
+ }
PyDict_SetItemString(self->metadata, NPY_METADATA_DTSTR, cobj);
Py_DECREF(cobj);
}
diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c
index f04dbe8eb..14a6c9d6a 100644
--- a/numpy/core/src/multiarray/dtype_transfer.c
+++ b/numpy/core/src/multiarray/dtype_transfer.c
@@ -16,9 +16,12 @@
#define _MULTIARRAYMODULE
#include <numpy/ndarrayobject.h>
-#include <numpy/ufuncobject.h>
#include <numpy/npy_cpu.h>
+#include "numpy/npy_3kcompat.h"
+
+#include "_datetime.h"
+
#include "lowlevel_strided_loops.h"
#define NPY_LOWLEVEL_BUFFER_BLOCKSIZE 128
@@ -696,6 +699,241 @@ get_nbo_cast_numeric_transfer_function(int aligned,
return NPY_SUCCEED;
}
+/* Does a datetime->datetime or timedelta->timedelta cast */
+typedef struct {
+ free_strided_transfer_data freefunc;
+ copy_strided_transfer_data copyfunc;
+ /* The conversion fraction */
+ npy_int64 num, denom;
+ /* The number of events in the source and destination */
+ int src_events, dst_events;
+ /*
+ * The metadata for when dealing with Months, Years, or
+ * Business Days (all of which behave non-linearly).
+ */
+ PyArray_DatetimeMetaData src_meta, dst_meta;
+} _strided_datetime_cast_data;
+
+/* strided cast data copy function */
+void *_strided_datetime_cast_data_copy(void *data)
+{
+ _strided_datetime_cast_data *newdata =
+ (_strided_datetime_cast_data *)PyArray_malloc(
+ sizeof(_strided_datetime_cast_data));
+ if (newdata == NULL) {
+ return NULL;
+ }
+
+ memcpy(newdata, data, sizeof(_strided_datetime_cast_data));
+
+ return (void *)newdata;
+}
+
+static void
+_strided_to_strided_datetime_general_cast(char *dst, npy_intp dst_stride,
+ char *src, npy_intp src_stride,
+ npy_intp N, npy_intp src_itemsize,
+ void *data)
+{
+ _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)data;
+ npy_int64 dt;
+ npy_datetimestruct dts;
+
+ while (N > 0) {
+ memcpy(&dt, src, sizeof(dt));
+
+ if (convert_datetime_to_datetimestruct(&d->src_meta,
+ dt, &dts) < 0) {
+ dt = NPY_DATETIME_NAT;
+ }
+ else {
+ dts.event = dts.event % d->dst_meta.events;
+ if (convert_datetimestruct_to_datetime(&d->dst_meta,
+ &dts, &dt) < 0) {
+ dt = NPY_DATETIME_NAT;
+ }
+ }
+
+ memcpy(dst, &dt, sizeof(dt));
+
+ dst += dst_stride;
+ src += src_stride;
+ --N;
+ }
+}
+
+static void
+_strided_to_strided_datetime_cast(char *dst, npy_intp dst_stride,
+ char *src, npy_intp src_stride,
+ npy_intp N, npy_intp src_itemsize,
+ void *data)
+{
+ _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)data;
+ npy_int64 num = d->num, denom = d->denom;
+ npy_int64 dt;
+ int event = 0, src_events = d->src_events, dst_events = d->dst_events;
+
+ while (N > 0) {
+ memcpy(&dt, src, sizeof(dt));
+
+ if (dt != NPY_DATETIME_NAT) {
+ /* Remove the event number from the value */
+ if (src_events > 1) {
+ event = (int)(dt % src_events);
+ dt = dt / src_events;
+ if (event < 0) {
+ --dt;
+ event += src_events;
+ }
+ }
+
+ /* Apply the scaling */
+ if (dt < 0) {
+ dt = (dt * num - (denom - 1)) / denom;
+ }
+ else {
+ dt = dt * num / denom;
+ }
+
+ /* Add the event number back in */
+ if (dst_events > 1) {
+ event = event % dst_events;
+ dt = dt * dst_events + event;
+ }
+ }
+
+ memcpy(dst, &dt, sizeof(dt));
+
+ dst += dst_stride;
+ src += src_stride;
+ --N;
+ }
+}
+
+static void
+_aligned_strided_to_strided_datetime_cast_no_events(char *dst,
+ npy_intp dst_stride,
+ char *src, npy_intp src_stride,
+ npy_intp N, npy_intp src_itemsize,
+ void *data)
+{
+ _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)data;
+ npy_int64 num = d->num, denom = d->denom;
+ npy_int64 dt;
+
+ while (N > 0) {
+ dt = *(npy_int64 *)src;
+
+ if (dt != NPY_DATETIME_NAT) {
+ /* Apply the scaling */
+ if (dt < 0) {
+ dt = (dt * num - (denom - 1)) / denom;
+ }
+ else {
+ dt = dt * num / denom;
+ }
+ }
+
+ *(npy_int64 *)dst = dt;
+
+ dst += dst_stride;
+ src += src_stride;
+ --N;
+ }
+}
+
+/*
+ * Assumes src_dtype and dst_dtype are both datetimes or both timedeltas
+ */
+static int
+get_nbo_cast_datetime_transfer_function(int aligned,
+ npy_intp src_stride, npy_intp dst_stride,
+ PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype,
+ PyArray_StridedTransferFn **out_stransfer,
+ void **out_transferdata)
+{
+ PyArray_DatetimeMetaData *src_meta, *dst_meta;
+ npy_int64 num = 0, denom = 0;
+ _strided_datetime_cast_data *data;
+
+ src_meta = get_datetime_metadata_from_dtype(src_dtype);
+ if (src_meta == NULL) {
+ return NPY_FAIL;
+ }
+ dst_meta = get_datetime_metadata_from_dtype(dst_dtype);
+ if (dst_meta == NULL) {
+ return NPY_FAIL;
+ }
+
+ get_datetime_conversion_factor(src_meta, dst_meta, &num, &denom);
+
+ if (num == 0) {
+ PyObject *errmsg;
+ errmsg = PyUString_FromString("Integer overflow "
+ "getting a conversion factor between types ");
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)src_dtype));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" and "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)dst_dtype));
+ PyErr_SetObject(PyExc_OverflowError, errmsg);
+ return NPY_FAIL;
+ }
+
+ /* Allocate the data for the casting */
+ data = (_strided_datetime_cast_data *)PyArray_malloc(
+ sizeof(_strided_datetime_cast_data));
+ if (data == NULL) {
+ PyErr_NoMemory();
+ *out_stransfer = NULL;
+ *out_transferdata = NULL;
+ return NPY_FAIL;
+ }
+ data->freefunc = &PyArray_free;
+ data->copyfunc = &_strided_datetime_cast_data_copy;
+ data->num = num;
+ data->denom = denom;
+ data->src_events = src_meta->events;
+ data->dst_events = dst_meta->events;
+
+ /*
+ * Special case the datetime (but not timedelta) with the nonlinear
+ * units (years, months, business days). For timedelta, an average
+ * years and months value is used.
+ */
+ if (src_dtype->type_num == NPY_DATETIME &&
+ (src_meta->base == NPY_FR_Y ||
+ src_meta->base == NPY_FR_M ||
+ src_meta->base == NPY_FR_B ||
+ dst_meta->base == NPY_FR_Y ||
+ dst_meta->base == NPY_FR_M ||
+ dst_meta->base == NPY_FR_B)) {
+ memcpy(&data->src_meta, src_meta, sizeof(data->src_meta));
+ memcpy(&data->dst_meta, dst_meta, sizeof(data->dst_meta));
+ *out_stransfer = &_strided_to_strided_datetime_general_cast;
+ }
+ else if (aligned && data->src_events == 1 && data->dst_events == 1) {
+ *out_stransfer = &_aligned_strided_to_strided_datetime_cast_no_events;
+ }
+ else {
+ *out_stransfer = &_strided_to_strided_datetime_cast;
+ }
+ *out_transferdata = data;
+
+#if NPY_DT_DBG_TRACING
+ printf("Dtype transfer from ");
+ PyObject_Print((PyObject *)src_dtype, stdout, 0);
+ printf(" to ");
+ PyObject_Print((PyObject *)dst_dtype, stdout, 0);
+ printf("\n");
+ printf("has conversion fraction %lld/%lld\n", num, denom);
+#endif
+
+
+ return NPY_SUCCEED;
+}
+
static int
get_nbo_cast_transfer_function(int aligned,
npy_intp src_stride, npy_intp dst_stride,
@@ -722,6 +960,19 @@ get_nbo_cast_transfer_function(int aligned,
out_stransfer, out_transferdata);
}
+ /* As a parameterized type, datetime->datetime sometimes needs casting */
+ if ((src_dtype->type_num == NPY_DATETIME &&
+ dst_dtype->type_num == NPY_DATETIME) ||
+ (src_dtype->type_num == NPY_TIMEDELTA &&
+ dst_dtype->type_num == NPY_TIMEDELTA)) {
+ *out_needs_wrap = !PyArray_ISNBO(src_dtype->byteorder) ||
+ !PyArray_ISNBO(dst_dtype->byteorder);
+ return get_nbo_cast_datetime_transfer_function(aligned,
+ src_stride, dst_stride,
+ src_dtype, dst_dtype,
+ out_stransfer, out_transferdata);
+ }
+
*out_needs_wrap = !aligned ||
!PyArray_ISNBO(src_dtype->byteorder) ||
!PyArray_ISNBO(dst_dtype->byteorder);
@@ -854,7 +1105,9 @@ get_cast_transfer_function(int aligned,
npy_intp src_itemsize = src_dtype->elsize,
dst_itemsize = dst_dtype->elsize;
- if (src_dtype->type_num == dst_dtype->type_num) {
+ if (src_dtype->type_num == dst_dtype->type_num &&
+ src_dtype->type_num != NPY_DATETIME &&
+ src_dtype->type_num != NPY_TIMEDELTA) {
PyErr_SetString(PyExc_ValueError,
"low level cast function is for unequal type numbers");
return NPY_FAIL;
@@ -2872,7 +3125,8 @@ PyArray_GetDTypeTransferFunction(int aligned,
if (src_itemsize == dst_itemsize && src_dtype->kind == dst_dtype->kind &&
!PyDataType_HASFIELDS(src_dtype) &&
!PyDataType_HASFIELDS(dst_dtype) &&
- src_dtype->subarray == NULL && dst_dtype->subarray == NULL) {
+ src_dtype->subarray == NULL && dst_dtype->subarray == NULL &&
+ src_type_num != NPY_DATETIME && src_type_num != NPY_TIMEDELTA) {
/* A custom data type requires that we use its copy/swap */
if (src_type_num >= NPY_NTYPES || dst_type_num >= NPY_NTYPES) {
/*
@@ -2895,8 +3149,6 @@ PyArray_GetDTypeTransferFunction(int aligned,
PyArray_ISNBO(dst_dtype->byteorder),
out_stransfer, out_transferdata);
}
-
-
}
/* The special types, which have no byte-order */
diff --git a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
index fc8d71f46..0a1786bc4 100644
--- a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
+++ b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
@@ -14,7 +14,6 @@
#define _MULTIARRAYMODULE
#include <numpy/ndarrayobject.h>
-#include <numpy/ufuncobject.h>
#include <numpy/npy_cpu.h>
#include <numpy/halffloat.h>
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index 2c4110afd..9502bc9f0 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -185,8 +185,6 @@ array_view(PyArrayObject *self, PyObject *args, PyObject *kwds)
if ((out_dtype) &&
(PyArray_DescrConverter(out_dtype, &dtype) == PY_FAIL)) {
- PyErr_SetString(PyExc_ValueError,
- "Dtype must be a numpy data-type");
return NULL;
}
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index f121f6147..f1103ef7f 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -44,6 +44,7 @@ NPY_NO_EXPORT int NPY_NUMUSERTYPES = 0;
#include "numpymemoryview.h"
#include "convert_datatype.h"
#include "nditer_pywrap.h"
+#include "_datetime.h"
/* Only here for API compatibility */
NPY_NO_EXPORT PyTypeObject PyBigArray_Type;
@@ -1401,39 +1402,6 @@ _equivalent_fields(PyObject *field1, PyObject *field2) {
}
/*
- * compare the metadata for two date-times
- * return 1 if they are the same
- * or 0 if not
- */
-static int
-_equivalent_units(PyObject *meta1, PyObject *meta2)
-{
- PyObject *cobj1, *cobj2;
- PyArray_DatetimeMetaData *data1, *data2;
-
- /* Same meta object */
- if (meta1 == meta2) {
- return 1;
- }
-
- cobj1 = PyDict_GetItemString(meta1, NPY_METADATA_DTSTR);
- cobj2 = PyDict_GetItemString(meta2, NPY_METADATA_DTSTR);
- if (cobj1 == cobj2) {
- return 1;
- }
-
-/* FIXME
- * There is no err handling here.
- */
- data1 = NpyCapsule_AsVoidPtr(cobj1);
- data2 = NpyCapsule_AsVoidPtr(cobj2);
- return ((data1->base == data2->base)
- && (data1->num == data2->num)
- && (data1->den == data2->den)
- && (data1->events == data2->events));
-}
-
-/*
* Compare the subarray data for two types.
* Return 1 if they are the same, 0 if not.
*/
@@ -1471,42 +1439,42 @@ _equivalent_subarrays(PyArray_ArrayDescr *sub1, PyArray_ArrayDescr *sub2)
* equivalent (same basic kind and same itemsize).
*/
NPY_NO_EXPORT unsigned char
-PyArray_EquivTypes(PyArray_Descr *typ1, PyArray_Descr *typ2)
+PyArray_EquivTypes(PyArray_Descr *type1, PyArray_Descr *type2)
{
- int typenum1, typenum2, size1, size2;
+ int type_num1, type_num2, size1, size2;
- if (typ1 == typ2) {
+ if (type1 == type2) {
return TRUE;
}
- typenum1 = typ1->type_num;
- typenum2 = typ2->type_num;
- size1 = typ1->elsize;
- size2 = typ2->elsize;
+ type_num1 = type1->type_num;
+ type_num2 = type2->type_num;
+ size1 = type1->elsize;
+ size2 = type2->elsize;
if (size1 != size2) {
return FALSE;
}
- if (PyArray_ISNBO(typ1->byteorder) != PyArray_ISNBO(typ2->byteorder)) {
+ if (PyArray_ISNBO(type1->byteorder) != PyArray_ISNBO(type2->byteorder)) {
return FALSE;
}
- if (typ1->subarray || typ2->subarray) {
- return ((typenum1 == typenum2)
- && _equivalent_subarrays(typ1->subarray, typ2->subarray));
+ if (type1->subarray || type2->subarray) {
+ return ((type_num1 == type_num2)
+ && _equivalent_subarrays(type1->subarray, type2->subarray));
}
- if (typenum1 == PyArray_VOID
- || typenum2 == PyArray_VOID) {
- return ((typenum1 == typenum2)
- && _equivalent_fields(typ1->fields, typ2->fields));
+ if (type_num1 == NPY_VOID
+ || type_num2 == NPY_VOID) {
+ return ((type_num1 == type_num2)
+ && _equivalent_fields(type1->fields, type2->fields));
}
- if (typenum1 == PyArray_DATETIME
- || typenum1 == PyArray_DATETIME
- || typenum2 == PyArray_TIMEDELTA
- || typenum2 == PyArray_TIMEDELTA) {
- return ((typenum1 == typenum2)
- && _equivalent_units(typ1->metadata, typ2->metadata));
+ if (type_num1 == NPY_DATETIME
+ || type_num1 == NPY_DATETIME
+ || type_num2 == NPY_TIMEDELTA
+ || type_num2 == NPY_TIMEDELTA) {
+ return ((type_num1 == type_num2)
+ && has_equivalent_datetime_metadata(type1, type2));
}
- return typ1->kind == typ2->kind;
+ return type1->kind == type2->kind;
}
/*NUMPY_API*/
@@ -2569,36 +2537,11 @@ array_set_ops_function(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args),
}
static PyObject *
-array_set_datetimeparse_function(PyObject *NPY_UNUSED(self), PyObject *args,
- PyObject *kwds)
+array_set_datetimeparse_function(PyObject *NPY_UNUSED(self),
+ PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(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;
+ PyErr_SetString(PyExc_RuntimeError, "This function is to be removed");
+ return NULL;
}
@@ -2844,6 +2787,189 @@ finish:
return ret;
}
+static PyObject *
+array_datetime_data(PyObject *NPY_UNUSED(dummy), PyObject *args)
+{
+ PyArray_Descr *dtype;
+ PyArray_DatetimeMetaData *meta;
+
+ if(!PyArg_ParseTuple(args, "O&:datetime_data",
+ PyArray_DescrConverter, &dtype)) {
+ return NULL;
+ }
+
+ meta = get_datetime_metadata_from_dtype(dtype);
+ if (meta == NULL) {
+ return NULL;
+ }
+
+ return convert_datetime_metadata_to_tuple(meta);
+}
+
+static PyObject *
+array_datetime_as_string(PyObject *NPY_UNUSED(self), PyObject *args,
+ PyObject *kwds)
+{
+ PyObject *arr_in = NULL, *unit_in = NULL;
+ int local = 0, tzoffset = -1;
+ NPY_DATETIMEUNIT unit;
+ PyArray_DatetimeMetaData *meta;
+ int strsize;
+
+ PyArrayObject *ret = NULL;
+
+ NpyIter *iter = NULL;
+ PyArrayObject *op[2] = {NULL, NULL};
+ PyArray_Descr *op_dtypes[2] = {NULL, NULL};
+ npy_uint32 flags, op_flags[2];
+
+ static char *kwlist[] = {"arr", "local", "unit", "tzoffset", NULL};
+
+ if(!PyArg_ParseTupleAndKeywords(args, kwds,
+ "O|iOi:datetime_as_string", kwlist,
+ &arr_in,
+ &local,
+ &unit_in,
+ &tzoffset)) {
+ goto fail;
+ }
+
+ op[0] = (PyArrayObject *)PyArray_FromAny(arr_in,
+ NULL, 0, 0, 0, NULL);
+ if (PyArray_DESCR(op[0])->type_num != NPY_DATETIME) {
+ PyErr_SetString(PyExc_TypeError,
+ "input must have type NumPy datetime");
+ goto fail;
+ }
+
+ /* Get the datetime metadata */
+ meta = get_datetime_metadata_from_dtype(PyArray_DESCR(op[0]));
+ if (meta == NULL) {
+ goto fail;
+ }
+
+ /* Use the metadata's unit for printing by default */
+ unit = meta->base;
+
+ /* Parse the input unit if provided */
+ if (unit_in != NULL && unit_in != Py_None) {
+ PyObject *strobj;
+ char *str = NULL;
+ Py_ssize_t len = 0;
+
+ if (PyUnicode_Check(unit_in)) {
+ strobj = PyUnicode_AsASCIIString(unit_in);
+ if (strobj == NULL) {
+ goto fail;
+ }
+ }
+ else {
+ strobj = unit_in;
+ Py_INCREF(strobj);
+ }
+
+ if (PyBytes_AsStringAndSize(strobj, &str, &len) < 0) {
+ Py_DECREF(strobj);
+ goto fail;
+ }
+
+ /* unit == -1 means to autodetect the unit from the datetime data */
+ if (strcmp(str, "auto") == 0) {
+ unit = -1;
+ }
+ else {
+ unit = parse_datetime_unit_from_string(str, len, NULL);
+ if (unit == -1) {
+ Py_DECREF(strobj);
+ goto fail;
+ }
+ }
+ Py_DECREF(strobj);
+ }
+
+ if (!local && tzoffset != -1) {
+ PyErr_SetString(PyExc_ValueError,
+ "Can only use 'tzoffset' parameter when 'local' is "
+ "set to True");
+ goto fail;
+ }
+
+ /* Create the output string data type with a big enough length */
+ op_dtypes[1] = PyArray_DescrNewFromType(NPY_STRING);
+ if (op_dtypes[1] == NULL) {
+ goto fail;
+ }
+ strsize = get_datetime_iso_8601_strlen(local, unit);
+ op_dtypes[1]->elsize = strsize;
+
+ flags = NPY_ITER_ZEROSIZE_OK|
+ NPY_ITER_BUFFERED;
+ op_flags[0] = NPY_ITER_READONLY|
+ NPY_ITER_ALIGNED;
+ op_flags[1] = NPY_ITER_WRITEONLY|
+ NPY_ITER_ALLOCATE;
+
+ iter = NpyIter_MultiNew(2, op, flags, NPY_KEEPORDER, NPY_NO_CASTING,
+ op_flags, op_dtypes);
+ if (iter == NULL) {
+ goto fail;
+ }
+
+ if (NpyIter_GetIterSize(iter) != 0) {
+ NpyIter_IterNextFunc *iternext;
+ char **dataptr;
+ npy_datetime dt;
+ npy_datetimestruct dts;
+
+ iternext = NpyIter_GetIterNext(iter, NULL);
+ if (iternext == NULL) {
+ goto fail;
+ }
+ dataptr = NpyIter_GetDataPtrArray(iter);
+
+ do {
+ /* Get the datetime */
+ dt = *(datetime *)dataptr[0];
+ /* Convert it to a struct */
+ if (convert_datetime_to_datetimestruct(meta, dt, &dts) < 0) {
+ goto fail;
+ }
+ /* Zero the destination string completely */
+ memset(dataptr[1], 0, strsize);
+ /* Convert that into a string */
+ if (make_iso_8601_date(&dts, (char *)dataptr[1], strsize,
+ local, unit, tzoffset) < 0) {
+ goto fail;
+ }
+ } while(iternext(iter));
+ }
+
+ ret = NpyIter_GetOperandArray(iter)[1];
+ Py_INCREF(ret);
+
+ Py_XDECREF(op[0]);
+ Py_XDECREF(op[1]);
+ Py_XDECREF(op_dtypes[0]);
+ Py_XDECREF(op_dtypes[1]);
+ if (iter != NULL) {
+ NpyIter_Deallocate(iter);
+ }
+
+ return PyArray_Return(ret);
+
+fail:
+ Py_XDECREF(op[0]);
+ Py_XDECREF(op[1]);
+ Py_XDECREF(op_dtypes[0]);
+ Py_XDECREF(op_dtypes[1]);
+ if (iter != NULL) {
+ NpyIter_Deallocate(iter);
+ }
+
+ return NULL;
+}
+
+
#if !defined(NPY_PY3K)
static PyObject *
new_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args)
@@ -3464,6 +3590,12 @@ static struct PyMethodDef array_module_methods[] = {
{"result_type",
(PyCFunction)array_result_type,
METH_VARARGS, NULL},
+ {"datetime_data",
+ (PyCFunction)array_datetime_data,
+ METH_VARARGS, NULL},
+ {"datetime_as_string",
+ (PyCFunction)array_datetime_as_string,
+ METH_VARARGS | METH_KEYWORDS, NULL},
#if !defined(NPY_PY3K)
{"newbuffer",
(PyCFunction)new_buffer,
@@ -3609,9 +3741,10 @@ setup_scalartypes(PyObject *NPY_UNUSED(dict))
SINGLE_INHERIT(LongLong, SignedInteger);
#endif
- SINGLE_INHERIT(TimeInteger, SignedInteger);
- SINGLE_INHERIT(Datetime, TimeInteger);
- SINGLE_INHERIT(Timedelta, TimeInteger);
+ /* Datetime doesn't fit in any category */
+ SINGLE_INHERIT(Datetime, Generic);
+ /* Timedelta is an integer with an associated unit */
+ SINGLE_INHERIT(Timedelta, SignedInteger);
/*
fprintf(stderr,
@@ -3733,6 +3866,9 @@ PyMODINIT_FUNC initmultiarray(void) {
1);
#endif
+ /* Initialize access to the PyDateTime API */
+ numpy_pydatetime_import();
+
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
if (!d) {
diff --git a/numpy/core/src/multiarray/scalarapi.c b/numpy/core/src/multiarray/scalarapi.c
index 3d2fae39c..d4ccc42c8 100644
--- a/numpy/core/src/multiarray/scalarapi.c
+++ b/numpy/core/src/multiarray/scalarapi.c
@@ -94,10 +94,7 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr)
_IFCASE(Int);
_IFCASE(Long);
_IFCASE(LongLong);
- if _CHK(TimeInteger) {
- _IFCASE(Datetime);
- _IFCASE(Timedelta);
- }
+ _IFCASE(Timedelta);
}
else {
/* Unsigned Integer */
@@ -127,6 +124,9 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr)
else if (_CHK(Bool)) {
return _OBJ(Bool);
}
+ else if (_CHK(Datetime)) {
+ return _OBJ(Datetime);
+ }
else if (_CHK(Flexible)) {
if (_CHK(String)) {
return (void *)PyString_AS_STRING(scalar);
@@ -522,7 +522,7 @@ PyArray_DescrFromScalar(PyObject *sc)
return descr;
}
- if (PyArray_IsScalar(sc, TimeInteger)) {
+ if (PyArray_IsScalar(sc, Datetime) || PyArray_IsScalar(sc, Timedelta)) {
PyObject *cobj;
PyArray_DatetimeMetaData *dt_data;
diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src
index 58dc39861..2fea9d28d 100644
--- a/numpy/core/src/multiarray/scalartypes.c.src
+++ b/numpy/core/src/multiarray/scalartypes.c.src
@@ -21,12 +21,16 @@
#include "numpyos.h"
#include "common.h"
#include "scalartypes.h"
+#include "_datetime.h"
NPY_NO_EXPORT PyBoolScalarObject _PyArrayScalar_BoolValues[] = {
{PyObject_HEAD_INIT(&PyBoolArrType_Type) 0},
{PyObject_HEAD_INIT(&PyBoolArrType_Type) 1},
};
+/* TimeInteger is deleted, but still here to fill the API slot */
+NPY_NO_EXPORT PyTypeObject PyTimeIntegerArrType_Type;
+
/*
* Inheritance is established later when tp_bases is set (or tp_base for
* single inheritance)
@@ -34,9 +38,9 @@ NPY_NO_EXPORT PyBoolScalarObject _PyArrayScalar_BoolValues[] = {
/**begin repeat
* #name = number, integer, signedinteger, unsignedinteger, inexact,
- * floating, complexfloating, flexible, character, timeinteger#
+ * floating, complexfloating, flexible, character#
* #NAME = Number, Integer, SignedInteger, UnsignedInteger, Inexact,
- * Floating, ComplexFloating, Flexible, Character, TimeInteger#
+ * Floating, ComplexFloating, Flexible, Character#
*/
NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = {
#if defined(NPY_PY3K)
@@ -107,7 +111,7 @@ gentype_alloc(PyTypeObject *type, Py_ssize_t nitems)
PyObject *obj;
const size_t size = _PyObject_VAR_SIZE(type, nitems + 1);
- obj = (PyObject *)_pya_malloc(size);
+ obj = (PyObject *)PyArray_malloc(size);
memset(obj, 0, size);
if (type->tp_itemsize == 0) {
PyObject_INIT(obj, type);
@@ -333,15 +337,13 @@ gentype_nonzero_number(PyObject *m1)
static PyObject *
gentype_str(PyObject *self)
{
- PyArrayObject *arr;
- PyObject *ret;
+ PyObject *arr, *ret = NULL;
- arr = (PyArrayObject *)PyArray_FromScalar(self, NULL);
- if (arr == NULL) {
- return NULL;
+ arr = PyArray_FromScalar(self, NULL);
+ if (arr != NULL) {
+ ret = PyObject_Str((PyObject *)arr);
+ Py_DECREF(arr);
}
- ret = PyObject_Str((PyObject *)arr);
- Py_DECREF(arr);
return ret;
}
@@ -349,15 +351,14 @@ gentype_str(PyObject *self)
static PyObject *
gentype_repr(PyObject *self)
{
- PyArrayObject *arr;
- PyObject *ret;
+ PyObject *arr, *ret = NULL;
- arr = (PyArrayObject *)PyArray_FromScalar(self, NULL);
- if (arr == NULL) {
- return NULL;
+ arr = PyArray_FromScalar(self, NULL);
+ if (arr != NULL) {
+ /* XXX: Why are we using str here? */
+ ret = PyObject_Str((PyObject *)arr);
+ Py_DECREF(arr);
}
- ret = PyObject_Str((PyObject *)arr);
- Py_DECREF(arr);
return ret;
}
@@ -596,6 +597,147 @@ static PyObject *
}
/**end repeat**/
+static PyObject *
+datetimetype_repr(PyObject *self)
+{
+ PyDatetimeScalarObject *scal;
+ npy_datetimestruct dts;
+ PyObject *ret;
+ char iso[NPY_DATETIME_MAX_ISO8601_STRLEN];
+
+ if (!PyArray_IsScalar(self, Datetime)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Called NumPy datetime repr on a non-datetime type");
+ return NULL;
+ }
+
+ scal = (PyDatetimeScalarObject *)self;
+
+ if (convert_datetime_to_datetimestruct(&scal->obmeta, scal->obval,
+ &dts) < 0) {
+ return NULL;
+ }
+
+ if (make_iso_8601_date(&dts, iso, sizeof(iso), 1,
+ scal->obmeta.base, -1) < 0) {
+ return NULL;
+ }
+
+ ret = PyUString_FromString("numpy.datetime64('");
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString(iso));
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString("','"));
+ ret = append_metastr_to_string(&scal->obmeta, 1, ret);
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString("')"));
+
+ return ret;
+}
+
+static PyObject *
+timedeltatype_repr(PyObject *self)
+{
+ PyTimedeltaScalarObject *scal;
+ PyObject *ret;
+
+ if (!PyArray_IsScalar(self, Timedelta)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Called NumPy timedelta repr on a non-datetime type");
+ return NULL;
+ }
+
+ scal = (PyTimedeltaScalarObject *)self;
+
+ ret = PyUString_FromFormat("numpy.timedelta64(%lld", scal->obval);
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString(",'"));
+ ret = append_metastr_to_string(&scal->obmeta, 1, ret);
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString("')"));
+
+ return ret;
+}
+
+static PyObject *
+datetimetype_str(PyObject *self)
+{
+ PyDatetimeScalarObject *scal;
+ npy_datetimestruct dts;
+ char iso[NPY_DATETIME_MAX_ISO8601_STRLEN];
+
+ if (!PyArray_IsScalar(self, Datetime)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Called NumPy datetime str on a non-datetime type");
+ return NULL;
+ }
+
+ scal = (PyDatetimeScalarObject *)self;
+
+ if (convert_datetime_to_datetimestruct(&scal->obmeta, scal->obval,
+ &dts) < 0) {
+ return NULL;
+ }
+
+ if (make_iso_8601_date(&dts, iso, sizeof(iso), 1,
+ scal->obmeta.base, -1) < 0) {
+ return NULL;
+ }
+
+ return PyUString_FromString(iso);
+}
+
+static char *_datetime_strings[] = {
+ "years",
+ "months",
+ "weeks",
+ "business days",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "picoseconds",
+ "femtoseconds",
+ "attoseconds"
+};
+
+static PyObject *
+timedeltatype_str(PyObject *self)
+{
+ PyTimedeltaScalarObject *scal;
+ PyObject *ret;
+ char *basestr = "invalid";
+
+ if (!PyArray_IsScalar(self, Timedelta)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Called NumPy timedelta str on a non-datetime type");
+ return NULL;
+ }
+
+ scal = (PyTimedeltaScalarObject *)self;
+
+ /* TODO: Account for events, etc */
+
+ if (scal->obmeta.base >= 0 && scal->obmeta.base < NPY_DATETIME_NUMUNITS) {
+ basestr = _datetime_strings[scal->obmeta.base];
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "NumPy datetime metadata is corrupted");
+ return NULL;
+ }
+
+ ret = PyUString_FromFormat("%lld ",
+ (long long)(scal->obval * scal->obmeta.num));
+ PyUString_ConcatAndDel(&ret,
+ PyUString_FromString(basestr));
+
+ return ret;
+}
+
/* The REPR values are finfo.precision + 2 */
#define HALFPREC_REPR 5
#define HALFPREC_STR 5
@@ -909,8 +1051,8 @@ gentype_struct_free(PyObject *ptr)
context = (PyObject *)PyCapsule_GetContext(ptr);
Py_DECREF(context);
Py_XDECREF(arrif->descr);
- _pya_free(arrif->shape);
- _pya_free(arrif);
+ PyArray_free(arrif->shape);
+ PyArray_free(arrif);
}
#else
NPY_NO_EXPORT void
@@ -919,8 +1061,8 @@ gentype_struct_free(void *ptr, void *arg)
PyArrayInterface *arrif = (PyArrayInterface *)ptr;
Py_DECREF((PyObject *)arg);
Py_XDECREF(arrif->descr);
- _pya_free(arrif->shape);
- _pya_free(arrif);
+ PyArray_free(arrif->shape);
+ PyArray_free(arrif);
}
#endif
@@ -932,7 +1074,7 @@ gentype_struct_get(PyObject *self)
PyObject *ret;
arr = (PyArrayObject *)PyArray_FromScalar(self, NULL);
- inter = (PyArrayInterface *)_pya_malloc(sizeof(PyArrayInterface));
+ inter = (PyArrayInterface *)PyArray_malloc(sizeof(PyArrayInterface));
inter->two = 2;
inter->nd = 0;
inter->flags = arr->flags;
@@ -1296,7 +1438,7 @@ gentype_byteswap(PyObject *self, PyObject *args)
gentype_getreadbuf(self, 0, (void **)&data);
descr = PyArray_DescrFromScalar(self);
- newmem = _pya_malloc(descr->elsize);
+ newmem = PyArray_malloc(descr->elsize);
if (newmem == NULL) {
Py_DECREF(descr);
return PyErr_NoMemory();
@@ -1305,7 +1447,7 @@ gentype_byteswap(PyObject *self, PyObject *args)
descr->f->copyswap(newmem, data, 1, NULL);
}
new = PyArray_Scalar(newmem, descr, NULL);
- _pya_free(newmem);
+ PyArray_free(newmem);
Py_DECREF(descr);
return new;
}
@@ -1478,7 +1620,7 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args))
int newlen;
if (PyArray_IsScalar(self, Unicode)) {
- tmp = _pya_malloc(buflen*2);
+ tmp = PyArray_malloc(buflen*2);
if (tmp == NULL) {
Py_DECREF(ret);
return PyErr_NoMemory();
@@ -1505,7 +1647,7 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args))
Py_BuildValue("NN", obj, mod));
#ifndef Py_UNICODE_WIDE
fail:
- if (alloc) _pya_free((char *)buffer);
+ if (alloc) PyArray_free((char *)buffer);
#endif
}
return ret;
@@ -2212,12 +2354,15 @@ object_arrtype_dealloc(PyObject *v)
/**begin repeat
* #name = byte, short, int, long, longlong, ubyte, ushort, uint, ulong,
* ulonglong, half, float, double, longdouble, cfloat, cdouble,
- * clongdouble, string, unicode, object, datetime, timedelta#
+ * clongdouble, string, unicode, object#
+ * #Name = Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong,
+ * ULongLong, Half, Float, Double, LongDouble, CFloat, CDouble,
+ * CLongDouble, String, Unicode, Object#
* #TYPE = BYTE, SHORT, INT, LONG, LONGLONG, UBYTE, USHORT, UINT, ULONG,
* ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE,
- * CLONGDOUBLE, STRING, UNICODE, OBJECT, DATETIME, TIMEDELTA#
- * #work = 0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,z,z,0,0,0#
- * #default = 0*17,1*2,2,0*2#
+ * CLONGDOUBLE, STRING, UNICODE, OBJECT#
+ * #work = 0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,z,z,0#
+ * #default = 0*17,1*2,2#
*/
#define _NPY_UNUSED2_1
@@ -2248,18 +2393,22 @@ static PyObject *
if (!PyArg_ParseTuple(args, "|O", &obj)) {
return NULL;
}
- typecode = PyArray_DescrFromType(PyArray_@TYPE@);
+ typecode = PyArray_DescrFromType(NPY_@TYPE@);
+ if (typecode == NULL) {
+ return NULL;
+ }
/*
* typecode is new reference and stolen by
* PyArray_FromAny but not PyArray_Scalar
*/
if (obj == NULL) {
#if @default@ == 0
- char *mem = malloc(sizeof(npy_@name@));
-
- memset(mem, 0, sizeof(npy_@name@));
- robj = PyArray_Scalar(mem, typecode, NULL);
- free(mem);
+ robj = PyArray_Scalar(NULL, typecode, NULL);
+ if (robj == NULL) {
+ Py_DECREF(typecode);
+ return NULL;
+ }
+ memset(&((Py@Name@ScalarObject *)robj)->obval, 0, sizeof(npy_@name@));
#elif @default@ == 1
robj = PyArray_Scalar(NULL, typecode, NULL);
#elif @default@ == 2
@@ -2339,6 +2488,89 @@ finish:
#undef _WORK0
#undef _WORK
+/**begin repeat
+ * #name = datetime, timedelta#
+ * #Name = Datetime, Timedelta#
+ * #NAME = DATETIME, TIMEDELTA#
+ */
+
+static PyObject *
+@name@_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyObject *obj = NULL, *meta_obj = NULL;
+ Py@Name@ScalarObject *ret;
+
+ if (!PyArg_ParseTuple(args, "|OO", &obj, &meta_obj)) {
+ return NULL;
+ }
+
+ /* Allocate the return scalar */
+ ret = (Py@Name@ScalarObject *)Py@Name@ArrType_Type.tp_alloc(
+ &Py@Name@ArrType_Type, 0);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ /* Incorporate the metadata if its provided */
+ if (meta_obj != NULL) {
+ /* Parse the provided metadata input */
+ if (convert_pyobject_to_datetime_metadata(meta_obj, &ret->obmeta)
+ < 0) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ }
+ else {
+ ret->obmeta.base = NPY_DATETIME_DEFAULTUNIT;
+ ret->obmeta.num = 1;
+ ret->obmeta.events = 1;
+ }
+
+ if (obj == NULL) {
+ ret->obval = 0;
+ }
+ /*
+ * If the metadata was unspecified and the input is a datetime/timedelta
+ * scalar, then copy the input's value and metadata exactly.
+ */
+ else if (meta_obj == NULL && PyArray_IsScalar(obj, @Name@)) {
+ ret->obval = ((Py@Name@ScalarObject *)obj)->obval;
+ ret->obmeta = ((Py@Name@ScalarObject *)obj)->obmeta;
+ }
+ /*
+ * If the metadata was unspecified and the input is a datetime/timedelta
+ * zero-dimensional array, then copy the input's value and metadata
+ * directly.
+ */
+ else if (meta_obj == NULL &&
+ PyArray_Check(obj) &&
+ PyArray_NDIM(obj) == 0 &&
+ PyArray_DESCR(obj)->type_num == NPY_@NAME@) {
+ PyArray_DatetimeMetaData *meta;
+
+ meta = get_datetime_metadata_from_dtype(PyArray_DESCR(obj));
+ if (meta == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ PyArray_DESCR(obj)->f->copyswap(&ret->obval,
+ PyArray_DATA(obj),
+ !PyArray_ISNOTSWAPPED(obj),
+ obj);
+ ret->obmeta = *meta;
+ }
+ else {
+ if (convert_pyobject_to_@name@(&ret->obmeta, obj, &ret->obval)
+ < 0) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ }
+
+ return (PyObject *)ret;
+}
+/**end repeat**/
+
/* bool->tp_new only returns Py_True or Py_False */
static PyObject *
bool_arrtype_new(PyTypeObject *NPY_UNUSED(type), PyObject *args, PyObject *NPY_UNUSED(kwds))
@@ -3426,18 +3658,21 @@ initialize_casting_tables(void)
for (i = 0; i < NPY_NTYPES; ++i) {
/* Identity */
_npy_can_cast_safely_table[i][i] = 1;
- /* Bool -> <Anything> */
- _npy_can_cast_safely_table[NPY_BOOL][i] = 1;
- /* DateTime sits out for these... */
- if (i != PyArray_DATETIME && i != PyArray_TIMEDELTA) {
- /* <Anything> -> Object */
- _npy_can_cast_safely_table[i][NPY_OBJECT] = 1;
- /* <Anything> -> Void */
- _npy_can_cast_safely_table[i][NPY_VOID] = 1;
+ if (i != NPY_DATETIME) {
+ /*
+ * Bool -> <Anything> except datetime (since
+ * it conceptually has no zero)
+ */
+ _npy_can_cast_safely_table[NPY_BOOL][i] = 1;
}
+ /* <Anything> -> Object */
+ _npy_can_cast_safely_table[i][NPY_OBJECT] = 1;
+ /* <Anything> -> Void */
+ _npy_can_cast_safely_table[i][NPY_VOID] = 1;
}
_npy_can_cast_safely_table[NPY_STRING][NPY_UNICODE] = 1;
+ _npy_can_cast_safely_table[NPY_BOOL][NPY_TIMEDELTA] = 1;
#ifndef NPY_SIZEOF_BYTE
#define NPY_SIZEOF_BYTE 1
@@ -3467,8 +3702,13 @@ initialize_casting_tables(void)
#define _FROM_BSIZE NPY_SIZEOF_@FROM_BASENAME@
#define _FROM_NUM (NPY_@FROM_NAME@)
- _npy_can_cast_safely_table[_FROM_NUM][PyArray_STRING] = 1;
- _npy_can_cast_safely_table[_FROM_NUM][PyArray_UNICODE] = 1;
+ _npy_can_cast_safely_table[_FROM_NUM][NPY_STRING] = 1;
+ _npy_can_cast_safely_table[_FROM_NUM][NPY_UNICODE] = 1;
+
+ /* Allow casts from any integer to the TIMEDELTA type */
+#if @from_isint@ || @from_isuint@
+ _npy_can_cast_safely_table[_FROM_NUM][NPY_TIMEDELTA] = 1;
+#endif
/**begin repeat1
* #TO_NAME = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
@@ -3576,8 +3816,9 @@ initialize_casting_tables(void)
*/
for (i = 0; i < NPY_NTYPES; ++i) {
_npy_type_promotion_table[i][i] = i;
- /* Don't let number promote to string/unicode/void */
- if (i == NPY_STRING || i == NPY_UNICODE || i == NPY_VOID) {
+ /* Don't let number promote to string/unicode/void/datetime/timedelta */
+ if (i == NPY_STRING || i == NPY_UNICODE || i == NPY_VOID ||
+ i == NPY_DATETIME || i == NPY_TIMEDELTA) {
/* Promoting these types requires examining their contents */
_npy_type_promotion_table[i][i] = -1;
for (j = i+1; j < NPY_NTYPES; ++j) {
@@ -3650,9 +3891,6 @@ initialize_casting_tables(void)
}
}
}
- /* Special case date-time */
- _npy_type_promotion_table[NPY_DATETIME][NPY_TIMEDELTA] = NPY_DATETIME;
- _npy_type_promotion_table[NPY_TIMEDELTA][NPY_DATETIME] = NPY_DATETIME;
}
@@ -3672,7 +3910,7 @@ initialize_numeric_types(void)
PyGenericArrType_Type.tp_getset = gentype_getsets;
PyGenericArrType_Type.tp_new = NULL;
PyGenericArrType_Type.tp_alloc = gentype_alloc;
- PyGenericArrType_Type.tp_free = _pya_free;
+ PyGenericArrType_Type.tp_free = PyArray_free;
PyGenericArrType_Type.tp_repr = gentype_repr;
PyGenericArrType_Type.tp_str = gentype_str;
PyGenericArrType_Type.tp_richcompare = gentype_richcompare;
@@ -3712,7 +3950,7 @@ initialize_numeric_types(void)
/**begin repeat
* #NAME= Number, Integer, SignedInteger, UnsignedInteger, Inexact,
- * Floating, ComplexFloating, Flexible, Character, TimeInteger#
+ * Floating, ComplexFloating, Flexible, Character#
*/
Py@NAME@ArrType_Type.tp_flags = BASEFLAGS;
/**end repeat**/
@@ -3775,6 +4013,9 @@ initialize_numeric_types(void)
PyDoubleArrType_Type.tp_@name@ = doubletype_@name@;
PyCDoubleArrType_Type.tp_@name@ = cdoubletype_@name@;
+
+ PyDatetimeArrType_Type.tp_@name@ = datetimetype_@name@;
+ PyTimedeltaArrType_Type.tp_@name@ = timedeltatype_@name@;
/**end repeat**/
PyHalfArrType_Type.tp_print = halftype_print;
@@ -3801,6 +4042,7 @@ initialize_numeric_types(void)
PyCLongDoubleArrType_Type.@kind@_@name@ = clongdoubletype_@name@;
/**end repeat**/
+
#if !defined(NPY_PY3K)
/**begin repeat
* #name = long, hex, oct#
diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src
index 54e5ac984..31345ff45 100644
--- a/numpy/core/src/umath/loops.c.src
+++ b/numpy/core/src/umath/loops.c.src
@@ -941,64 +941,70 @@ U@TYPE@_remainder(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(f
*****************************************************************************
*/
-/**begin repeat
- * #type = datetime, timedelta#
- * #TYPE = DATETIME, TIMEDELTA#
- * #ftype = double, double#
- */
-
NPY_NO_EXPORT void
-@TYPE@_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data))
+TIMEDELTA_negative(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
{
- OUTPUT_LOOP {
- *((@type@ *)op1) = 1;
+ UNARY_LOOP {
+ const npy_timedelta in1 = *(npy_timedelta *)ip1;
+ if (in1 == NPY_DATETIME_NAT) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = -in1;
+ }
}
}
NPY_NO_EXPORT void
-@TYPE@_negative(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+TIMEDELTA_absolute(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
{
UNARY_LOOP {
- const @type@ in1 = *(@type@ *)ip1;
- *((@type@ *)op1) = (@type@)(-(@type@)in1);
+ const npy_timedelta in1 = *(npy_timedelta *)ip1;
+ if (in1 == NPY_DATETIME_NAT) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = (in1 >= 0) ? in1 : -in1;
+ }
}
}
NPY_NO_EXPORT void
-@TYPE@_logical_not(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+TIMEDELTA_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
{
UNARY_LOOP {
- const @type@ in1 = *(@type@ *)ip1;
- *((Bool *)op1) = !in1;
+ const npy_timedelta in1 = *(npy_timedelta *)ip1;
+ *((npy_timedelta *)op1) = in1 > 0 ? 1 : (in1 < 0 ? -1 : 0);
}
}
-
-/**begin repeat1
- * #kind = equal, not_equal, greater, greater_equal, less, less_equal,
- * logical_and, logical_or#
- * #OP = ==, !=, >, >=, <, <=, &&, ||#
+/**begin repeat
+ * #type = datetime, timedelta#
+ * #TYPE = DATETIME, TIMEDELTA#
*/
+
NPY_NO_EXPORT void
-@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+@TYPE@_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data))
{
- BINARY_LOOP {
- const @type@ in1 = *(@type@ *)ip1;
- const @type@ in2 = *(@type@ *)ip2;
- *((Bool *)op1) = in1 @OP@ in2;
+ OUTPUT_LOOP {
+ *((@type@ *)op1) = 1;
}
}
-/**end repeat1**/
+/**begin repeat1
+ * #kind = equal, not_equal, greater, greater_equal, less, less_equal#
+ * #OP = ==, !=, >, >=, <, <=#
+ */
NPY_NO_EXPORT void
-@TYPE@_logical_xor(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+@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 && !in2) || (!in1 && in2);
+ *((npy_bool *)op1) = in1 @OP@ in2;
}
}
+/**end repeat1**/
/**begin repeat1
* #kind = maximum, minimum#
@@ -1010,50 +1016,42 @@ NPY_NO_EXPORT void
if (IS_BINARY_REDUCE) {
BINARY_REDUCE_LOOP(@type@) {
const @type@ in2 = *(@type@ *)ip2;
- io1 = (io1 @OP@ in2) ? io1 : in2;
+ io1 = (io1 @OP@ in2 || in2 == NPY_DATETIME_NAT) ? io1 : in2;
}
*((@type@ *)iop1) = io1;
}
else {
- BINARY_LOOP {
- const @type@ in1 = *(@type@ *)ip1;
- const @type@ in2 = *(@type@ *)ip2;
- *((@type@ *)op1) = (in1 @OP@ in2) ? in1 : in2;
- }
+ BINARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ const @type@ in2 = *(@type@ *)ip2;
+ if (in1 == NPY_DATETIME_NAT) {
+ *((@type@ *)op1) = in2;
+ }
+ else if (in2 == NPY_DATETIME_NAT) {
+ *((@type@ *)op1) = in1;
+ }
+ else {
+ *((@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;
+ if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) {
+ *((npy_datetime *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_datetime *)op1) = in1 + in2;
+ }
}
}
@@ -1063,7 +1061,12 @@ DATETIME_mM_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(f
BINARY_LOOP {
const timedelta in1 = *(timedelta *)ip1;
const datetime in2 = *(datetime *)ip2;
- *((datetime *)op1) = in1 + in2;
+ if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) {
+ *((npy_datetime *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_datetime *)op1) = in1 + in2;
+ }
}
}
@@ -1073,7 +1076,12 @@ TIMEDELTA_mm_m_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(
BINARY_LOOP {
const timedelta in1 = *(timedelta *)ip1;
const timedelta in2 = *(timedelta *)ip2;
- *((timedelta *)op1) = in1 + in2;
+ if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = in1 + in2;
+ }
}
}
@@ -1083,7 +1091,12 @@ DATETIME_Mm_M_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNU
BINARY_LOOP {
const datetime in1 = *(datetime *)ip1;
const timedelta in2 = *(timedelta *)ip2;
- *((datetime *)op1) = in1 - in2;
+ if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) {
+ *((npy_datetime *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_datetime *)op1) = in1 - in2;
+ }
}
}
@@ -1093,7 +1106,12 @@ DATETIME_MM_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNU
BINARY_LOOP {
const datetime in1 = *(datetime *)ip1;
const datetime in2 = *(datetime *)ip2;
- *((timedelta *)op1) = in1 - in2;
+ if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = in1 - in2;
+ }
}
}
@@ -1103,10 +1121,113 @@ TIMEDELTA_mm_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UN
BINARY_LOOP {
const timedelta in1 = *(timedelta *)ip1;
const timedelta in2 = *(timedelta *)ip2;
- *((timedelta *)op1) = in1 - in2;
+ if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = in1 - in2;
+ }
+ }
+}
+
+/* Note: Assuming 'q' == NPY_LONGLONG */
+NPY_NO_EXPORT void
+TIMEDELTA_mq_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const npy_timedelta in1 = *(npy_timedelta *)ip1;
+ const npy_int64 in2 = *(npy_int64 *)ip2;
+ if (in1 == NPY_DATETIME_NAT) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = in1 * in2;
+ }
+ }
+}
+
+/* Note: Assuming 'q' == NPY_LONGLONG */
+NPY_NO_EXPORT void
+TIMEDELTA_qm_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const npy_int64 in1 = *(npy_int64 *)ip1;
+ const npy_timedelta in2 = *(npy_timedelta *)ip2;
+ if (in2 == NPY_DATETIME_NAT) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = in1 * in2;
+ }
}
}
+NPY_NO_EXPORT void
+TIMEDELTA_md_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const npy_timedelta in1 = *(npy_timedelta *)ip1;
+ const double in2 = *(double *)ip2;
+ if (in1 == NPY_DATETIME_NAT || isnan(in2)) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = (npy_timedelta)(in1 * in2);
+ }
+ }
+}
+
+NPY_NO_EXPORT void
+TIMEDELTA_dm_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const double in1 = *(double *)ip1;
+ const npy_timedelta in2 = *(npy_timedelta *)ip2;
+ if (isnan(in1) || in2 == NPY_DATETIME_NAT) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = (npy_timedelta)(in1 * in2);
+ }
+ }
+}
+
+/* Note: Assuming 'q' == NPY_LONGLONG */
+NPY_NO_EXPORT void
+TIMEDELTA_mq_m_divide(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const npy_timedelta in1 = *(npy_timedelta *)ip1;
+ const npy_int64 in2 = *(npy_int64 *)ip2;
+ if (in1 == NPY_DATETIME_NAT || in2 == 0) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = in1 / in2;
+ }
+ }
+}
+
+NPY_NO_EXPORT void
+TIMEDELTA_md_m_divide(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP {
+ const npy_timedelta in1 = *(npy_timedelta *)ip1;
+ const double in2 = *(double *)ip2;
+ if (in1 == NPY_DATETIME_NAT) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ double result = in1 / in2;
+ if (isnan(result)) {
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ *((npy_timedelta *)op1) = (npy_timedelta)(result);
+ }
+ }
+ }
+}
/*
*****************************************************************************
diff --git a/numpy/core/src/umath/loops.h b/numpy/core/src/umath/loops.h
index abd8de23e..2a827e168 100644
--- a/numpy/core/src/umath/loops.h
+++ b/numpy/core/src/umath/loops.h
@@ -2535,145 +2535,142 @@ CLONGDOUBLE_fmin(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(fu
*****************************************************************************
*/
-#line 422
-#define DATETIME_fmax DATETIME_maximum
-#define DATETIME_fmin DATETIME_minimum
-
-#line 422
-#define TIMEDELTA_fmax TIMEDELTA_maximum
-#define TIMEDELTA_fmin TIMEDELTA_minimum
-
-
-#line 431
NPY_NO_EXPORT void
-DATETIME_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_negative(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-TIMEDELTA_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_absolute(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
NPY_NO_EXPORT void
-DATETIME_not_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-NPY_NO_EXPORT void
-TIMEDELTA_not_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+#line 432
-#line 431
NPY_NO_EXPORT void
-DATETIME_greater(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data));
+#line 440
NPY_NO_EXPORT void
-TIMEDELTA_greater(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
+#line 440
NPY_NO_EXPORT void
-DATETIME_greater_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_not_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+#line 440
NPY_NO_EXPORT void
-TIMEDELTA_greater_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_greater(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
+#line 440
NPY_NO_EXPORT void
-DATETIME_less(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_greater_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+#line 440
NPY_NO_EXPORT void
-TIMEDELTA_less(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_less(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
+#line 440
NPY_NO_EXPORT void
DATETIME_less_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-NPY_NO_EXPORT void
-TIMEDELTA_less_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
+#line 448
NPY_NO_EXPORT void
-DATETIME_absolute(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_maximum(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+#line 448
NPY_NO_EXPORT void
-TIMEDELTA_absolute(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_minimum(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
-NPY_NO_EXPORT void
-DATETIME_logical_and(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-NPY_NO_EXPORT void
-TIMEDELTA_logical_and(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
-NPY_NO_EXPORT void
-DATETIME_logical_not(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+#line 432
NPY_NO_EXPORT void
-TIMEDELTA_logical_not(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data));
-#line 431
+#line 440
NPY_NO_EXPORT void
-DATETIME_logical_or(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+#line 440
NPY_NO_EXPORT void
-TIMEDELTA_logical_or(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_not_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
+#line 440
NPY_NO_EXPORT void
-DATETIME_logical_xor(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_greater(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+#line 440
NPY_NO_EXPORT void
-TIMEDELTA_logical_xor(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_greater_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
+#line 440
NPY_NO_EXPORT void
-DATETIME_maximum(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_less(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+#line 440
NPY_NO_EXPORT void
-TIMEDELTA_maximum(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_less_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
-#line 431
+#line 448
NPY_NO_EXPORT void
-DATETIME_minimum(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_maximum(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+#line 448
NPY_NO_EXPORT void
TIMEDELTA_minimum(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
-NPY_NO_EXPORT void
-DATETIME_negative(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+
NPY_NO_EXPORT void
-TIMEDELTA_negative(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_Mm_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data));
-#line 431
NPY_NO_EXPORT void
-DATETIME_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_mM_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-TIMEDELTA_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_mm_m_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 431
NPY_NO_EXPORT void
-DATETIME_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_Mm_M_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-TIMEDELTA_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_MM_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+NPY_NO_EXPORT void
+TIMEDELTA_mm_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-DATETIME_Mm_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_mq_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-DATETIME_mM_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_qm_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-DATETIME_Mm_M_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_md_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-DATETIME_MM_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_dm_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-TIMEDELTA_mm_m_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_mq_m_divide(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-TIMEDELTA_mm_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_md_m_divide(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+/* Special case equivalents to above functions */
+
+#define TIMEDELTA_mq_m_true_divide TIMEDELTA_mq_m_divide
+#define TIMEDELTA_md_m_true_divide TIMEDELTA_md_m_divide
+#define TIMEDELTA_mq_m_floor_divide TIMEDELTA_mq_m_divide
+#define TIMEDELTA_md_m_floor_divide TIMEDELTA_md_m_divide
+#define TIMEDELTA_fmin TIMEDELTA_minimum
+#define TIMEDELTA_fmax TIMEDELTA_maximum
+#define DATETIME_fmin DATETIME_minimum
+#define DATETIME_fmax DATETIME_maximum
/*
*****************************************************************************
@@ -2681,27 +2678,27 @@ TIMEDELTA_mm_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UN
*****************************************************************************
*/
-#line 466
+#line 511
NPY_NO_EXPORT void
OBJECT_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 466
+#line 511
NPY_NO_EXPORT void
OBJECT_not_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 466
+#line 511
NPY_NO_EXPORT void
OBJECT_greater(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 466
+#line 511
NPY_NO_EXPORT void
OBJECT_greater_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 466
+#line 511
NPY_NO_EXPORT void
OBJECT_less(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
-#line 466
+#line 511
NPY_NO_EXPORT void
OBJECT_less_equal(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
@@ -2716,3 +2713,4 @@ OBJECT_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
*/
#endif
+
diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src
index 1def8ba0f..bf41e4187 100644
--- a/numpy/core/src/umath/loops.h.src
+++ b/numpy/core/src/umath/loops.h.src
@@ -416,42 +416,87 @@ C@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func
*****************************************************************************
*/
+NPY_NO_EXPORT void
+TIMEDELTA_negative(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
+TIMEDELTA_absolute(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
+TIMEDELTA_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
/**begin repeat
+ * #type = datetime, timedelta#
* #TYPE = DATETIME, TIMEDELTA#
*/
-#define @TYPE@_fmax @TYPE@_maximum
-#define @TYPE@_fmin @TYPE@_minimum
-/**end repeat**/
-/**begin repeat
- * #kind = equal, not_equal, greater, greater_equal, less, less_equal,
- * absolute, logical_and, logical_not, logical_or, logical_xor, maximum,
- * minimum, negative, ones_like, sign#
+NPY_NO_EXPORT void
+@TYPE@_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data));
+
+/**begin repeat1
+ * #kind = equal, not_equal, greater, greater_equal, less, less_equal#
+ * #OP = ==, !=, >, >=, <, <=#
*/
NPY_NO_EXPORT void
-DATETIME_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+/**end repeat1**/
+/**begin repeat1
+ * #kind = maximum, minimum#
+ * #OP = >, <#
+ **/
NPY_NO_EXPORT void
-TIMEDELTA_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+/**end repeat1**/
+
/**end repeat**/
NPY_NO_EXPORT void
-DATETIME_Mm_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+DATETIME_Mm_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data));
NPY_NO_EXPORT void
DATETIME_mM_M_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
+TIMEDELTA_mm_m_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
DATETIME_Mm_M_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
DATETIME_MM_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-TIMEDELTA_mm_m_add(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_mm_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-TIMEDELTA_mm_m_subtract(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+TIMEDELTA_mq_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
+TIMEDELTA_qm_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
+TIMEDELTA_md_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
+TIMEDELTA_dm_m_multiply(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
+TIMEDELTA_mq_m_divide(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
+TIMEDELTA_md_m_divide(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
+
+/* Special case equivalents to above functions */
+
+#define TIMEDELTA_mq_m_true_divide TIMEDELTA_mq_m_divide
+#define TIMEDELTA_md_m_true_divide TIMEDELTA_md_m_divide
+#define TIMEDELTA_mq_m_floor_divide TIMEDELTA_mq_m_divide
+#define TIMEDELTA_md_m_floor_divide TIMEDELTA_md_m_divide
+#define TIMEDELTA_fmin TIMEDELTA_minimum
+#define TIMEDELTA_fmax TIMEDELTA_maximum
+#define DATETIME_fmin DATETIME_minimum
+#define DATETIME_fmax DATETIME_maximum
/*
*****************************************************************************
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 930c91ca1..36dea2c83 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -577,12 +577,12 @@ _parse_signature(PyUFuncObject *self, const char *signature)
}
len = strlen(signature);
- self->core_signature = _pya_malloc(sizeof(char) * (len+1));
+ self->core_signature = PyArray_malloc(sizeof(char) * (len+1));
if (self->core_signature) {
strcpy(self->core_signature, signature);
}
/* Allocate sufficient memory to store pointers to all dimension names */
- var_names = _pya_malloc(sizeof(char const*) * len);
+ var_names = PyArray_malloc(sizeof(char const*) * len);
if (var_names == NULL) {
PyErr_NoMemory();
return -1;
@@ -590,9 +590,9 @@ _parse_signature(PyUFuncObject *self, const char *signature)
self->core_enabled = 1;
self->core_num_dim_ix = 0;
- self->core_num_dims = _pya_malloc(sizeof(int) * self->nargs);
- self->core_dim_ixs = _pya_malloc(sizeof(int) * len); /* shrink this later */
- self->core_offsets = _pya_malloc(sizeof(int) * self->nargs);
+ self->core_num_dims = PyArray_malloc(sizeof(int) * self->nargs);
+ self->core_dim_ixs = PyArray_malloc(sizeof(int) * len); /* shrink this later */
+ self->core_offsets = PyArray_malloc(sizeof(int) * self->nargs);
if (self->core_num_dims == NULL || self->core_dim_ixs == NULL
|| self->core_offsets == NULL) {
PyErr_NoMemory();
@@ -677,24 +677,24 @@ _parse_signature(PyUFuncObject *self, const char *signature)
parse_error = "incomplete signature: not all arguments found";
goto fail;
}
- self->core_dim_ixs = _pya_realloc(self->core_dim_ixs,
+ self->core_dim_ixs = PyArray_realloc(self->core_dim_ixs,
sizeof(int)*cur_core_dim);
/* check for trivial core-signature, e.g. "(),()->()" */
if (cur_core_dim == 0) {
self->core_enabled = 0;
}
- _pya_free((void*)var_names);
+ PyArray_free((void*)var_names);
return 0;
fail:
- _pya_free((void*)var_names);
+ PyArray_free((void*)var_names);
if (parse_error) {
- char *buf = _pya_malloc(sizeof(char) * (len + 200));
+ char *buf = PyArray_malloc(sizeof(char) * (len + 200));
if (buf) {
sprintf(buf, "%s at position %d in \"%s\"",
parse_error, i, signature);
PyErr_SetString(PyExc_ValueError, signature);
- _pya_free(buf);
+ PyArray_free(buf);
}
else {
PyErr_NoMemory();
@@ -720,8 +720,7 @@ static int get_ufunc_arguments(PyUFuncObject *self,
NPY_CASTING *out_casting,
PyObject **out_extobj,
PyObject **out_typetup,
- int *out_subok,
- int *out_any_object)
+ int *out_subok)
{
npy_intp i, nargs, nin = self->nin;
PyObject *obj, *context;
@@ -938,8 +937,6 @@ static int get_ufunc_arguments(PyUFuncObject *self,
}
}
- *out_any_object = any_object;
-
Py_XDECREF(str_key_obj);
return 0;
@@ -1066,25 +1063,49 @@ ufunc_loop_matches(PyUFuncObject *self,
static int
set_ufunc_loop_data_types(PyUFuncObject *self, PyArrayObject **op,
PyArray_Descr **out_dtype,
- int *types,
- npy_intp buffersize, int *out_trivial_loop_ok)
+ int *types)
{
- npy_intp i, nin = self->nin, nop = nin + self->nout;
+ int i, nin = self->nin, nop = nin + self->nout;
- *out_trivial_loop_ok = 1;
/* Fill the dtypes array */
for (i = 0; i < nop; ++i) {
out_dtype[i] = PyArray_DescrFromType(types[i]);
if (out_dtype[i] == NULL) {
+ while (--i >= 0) {
+ Py_DECREF(out_dtype[i]);
+ out_dtype[i] = NULL;
+ }
return -1;
}
+ }
+
+ return 0;
+}
+
+/*
+ * This checks whether a trivial loop is ok,
+ * making copies of scalar and one dimensional operands if that will
+ * help.
+ *
+ * Returns 1 if a trivial loop is ok, 0 if it is not, and
+ * -1 if there is an error.
+ */
+static int
+check_for_trivial_loop(PyUFuncObject *self,
+ PyArrayObject **op,
+ PyArray_Descr **dtype,
+ npy_intp buffersize)
+{
+ npy_intp i, nin = self->nin, nop = nin + self->nout;
+
+ for (i = 0; i < nop; ++i) {
/*
* If the dtype doesn't match, or the array isn't aligned,
* indicate that the trivial loop can't be done.
*/
- if (*out_trivial_loop_ok && op[i] != NULL &&
+ if (op[i] != NULL &&
(!PyArray_ISALIGNED(op[i]) ||
- !PyArray_EquivTypes(out_dtype[i], PyArray_DESCR(op[i]))
+ !PyArray_EquivTypes(dtype[i], PyArray_DESCR(op[i]))
)) {
/*
* If op[j] is a scalar or small one dimensional
@@ -1095,9 +1116,9 @@ set_ufunc_loop_data_types(PyUFuncObject *self, PyArrayObject **op,
(PyArray_NDIM(op[i]) == 1 &&
PyArray_DIM(op[i],0) <= buffersize))) {
PyArrayObject *tmp;
- Py_INCREF(out_dtype[i]);
+ Py_INCREF(dtype[i]);
tmp = (PyArrayObject *)
- PyArray_CastToType(op[i], out_dtype[i], 0);
+ PyArray_CastToType(op[i], dtype[i], 0);
if (tmp == NULL) {
return -1;
}
@@ -1105,12 +1126,12 @@ set_ufunc_loop_data_types(PyUFuncObject *self, PyArrayObject **op,
op[i] = tmp;
}
else {
- *out_trivial_loop_ok = 0;
+ return 0;
}
}
}
- return 0;
+ return 1;
}
/*
@@ -1121,13 +1142,11 @@ find_ufunc_matching_userloop(PyUFuncObject *self,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING output_casting,
- npy_intp buffersize,
int any_object,
int use_min_scalar,
PyArray_Descr **out_dtype,
PyUFuncGenericFunction *out_innerloop,
void **out_innerloopdata,
- int *out_trivial_loop_ok,
int *out_no_castable_output,
char *out_err_src_typecode,
char *out_err_dst_typecode)
@@ -1168,8 +1187,7 @@ find_ufunc_matching_userloop(PyUFuncObject *self,
return -1;
/* Found a match */
case 1:
- set_ufunc_loop_data_types(self, op, out_dtype, types,
- buffersize, out_trivial_loop_ok);
+ set_ufunc_loop_data_types(self, op, out_dtype, types);
/* Save the inner loop and its data */
*out_innerloop = funcdata->func;
@@ -1199,15 +1217,13 @@ find_ufunc_specified_userloop(PyUFuncObject *self,
int *specified_types,
PyArrayObject **op,
NPY_CASTING casting,
- npy_intp buffersize,
int any_object,
int use_min_scalar,
PyArray_Descr **out_dtype,
PyUFuncGenericFunction *out_innerloop,
- void **out_innerloopdata,
- int *out_trivial_loop_ok)
+ void **out_innerloopdata)
{
- npy_intp i, j, nin = self->nin, nop = nin + self->nout;
+ int i, j, nin = self->nin, nop = nin + self->nout;
PyUFunc_Loop1d *funcdata;
/* Use this to try to avoid repeating the same userdef loop search */
@@ -1261,8 +1277,7 @@ find_ufunc_specified_userloop(PyUFuncObject *self,
&err_dst_typecode)) {
/* It works */
case 1:
- set_ufunc_loop_data_types(self, op, out_dtype, types,
- buffersize, out_trivial_loop_ok);
+ set_ufunc_loop_data_types(self, op, out_dtype, types);
/* Save the inner loop and its data */
*out_innerloop = funcdata->func;
@@ -1365,10 +1380,6 @@ should_use_min_scalar(PyArrayObject **op, int nop)
/*
* Does a linear search for the best inner loop of the ufunc.
- * When op[i] is a scalar or a one dimensional array smaller than
- * the buffersize, and needs a dtype conversion, this function
- * may substitute op[i] with a version cast to the correct type. This way,
- * the later trivial loop detection has a higher chance of being triggered.
*
* Note that if an error is returned, the caller must free the non-zero
* references in out_dtype. This function does not do its own clean-up.
@@ -1378,12 +1389,10 @@ find_best_ufunc_inner_loop(PyUFuncObject *self,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING output_casting,
- npy_intp buffersize,
int any_object,
PyArray_Descr **out_dtype,
PyUFuncGenericFunction *out_innerloop,
- void **out_innerloopdata,
- int *out_trivial_loop_ok)
+ void **out_innerloopdata)
{
npy_intp i, j, nin = self->nin, nop = nin + self->nout;
int types[NPY_MAXARGS];
@@ -1401,9 +1410,8 @@ find_best_ufunc_inner_loop(PyUFuncObject *self,
if (self->userloops) {
switch (find_ufunc_matching_userloop(self, op,
input_casting, output_casting,
- buffersize, any_object, use_min_scalar,
+ any_object, use_min_scalar,
out_dtype, out_innerloop, out_innerloopdata,
- out_trivial_loop_ok,
&no_castable_output, &err_src_typecode,
&err_dst_typecode)) {
/* Error */
@@ -1452,8 +1460,7 @@ find_best_ufunc_inner_loop(PyUFuncObject *self,
return -1;
/* Found a match */
case 1:
- set_ufunc_loop_data_types(self, op, out_dtype, types,
- buffersize, out_trivial_loop_ok);
+ set_ufunc_loop_data_types(self, op, out_dtype, types);
/* Save the inner loop and its data */
*out_innerloop = self->functions[i];
@@ -1494,10 +1501,6 @@ find_best_ufunc_inner_loop(PyUFuncObject *self,
/*
* Does a linear search for the inner loop of the ufunc specified by type_tup.
- * When op[i] is a scalar or a one dimensional array smaller than
- * the buffersize, and needs a dtype conversion, this function
- * may substitute op[i] with a version cast to the correct type. This way,
- * the later trivial loop detection has a higher chance of being triggered.
*
* Note that if an error is returned, the caller must free the non-zero
* references in out_dtype. This function does not do its own clean-up.
@@ -1507,12 +1510,10 @@ find_specified_ufunc_inner_loop(PyUFuncObject *self,
PyObject *type_tup,
PyArrayObject **op,
NPY_CASTING casting,
- npy_intp buffersize,
int any_object,
PyArray_Descr **out_dtype,
PyUFuncGenericFunction *out_innerloop,
- void **out_innerloopdata,
- int *out_trivial_loop_ok)
+ void **out_innerloopdata)
{
npy_intp i, j, n, nin = self->nin, nop = nin + self->nout;
int n_specified = 0;
@@ -1619,9 +1620,8 @@ find_specified_ufunc_inner_loop(PyUFuncObject *self,
switch (find_ufunc_specified_userloop(self,
n_specified, specified_types,
op, casting,
- buffersize, any_object, use_min_scalar,
- out_dtype, out_innerloop, out_innerloopdata,
- out_trivial_loop_ok)) {
+ any_object, use_min_scalar,
+ out_dtype, out_innerloop, out_innerloopdata)) {
/* Error */
case -1:
return -1;
@@ -1673,8 +1673,7 @@ find_specified_ufunc_inner_loop(PyUFuncObject *self,
return -1;
/* It worked */
case 1:
- set_ufunc_loop_data_types(self, op, out_dtype, types,
- buffersize, out_trivial_loop_ok);
+ set_ufunc_loop_data_types(self, op, out_dtype, types);
/* Save the inner loop and its data */
*out_innerloop = self->functions[i];
@@ -1706,6 +1705,1219 @@ find_specified_ufunc_inner_loop(PyUFuncObject *self,
return -1;
}
+/*UFUNC_API
+ *
+ * This function applies the default type resolution rules
+ * for the provided ufunc, filling out_dtypes, out_innerloop,
+ * and out_innerloopdata.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+NPY_NO_EXPORT int
+PyUFunc_DefaultTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int i, nop = ufunc->nin + ufunc->nout;
+ int retval = 0, any_object = 0;
+ NPY_CASTING input_casting;
+
+ for (i = 0; i < nop; ++i) {
+ if (operands[i] != NULL &&
+ PyTypeNum_ISOBJECT(PyArray_DESCR(operands[i])->type_num)) {
+ any_object = 1;
+ break;
+ }
+ }
+
+ /*
+ * Decide the casting rules for inputs and outputs. We want
+ * NPY_SAFE_CASTING or stricter, so that the loop selection code
+ * doesn't choose an integer loop for float inputs, for example.
+ */
+ input_casting = (casting > NPY_SAFE_CASTING) ? NPY_SAFE_CASTING : casting;
+
+ if (type_tup == NULL) {
+ /* Find the best ufunc inner loop, and fill in the dtypes */
+ retval = find_best_ufunc_inner_loop(ufunc, operands,
+ input_casting, casting, any_object,
+ out_dtypes, out_innerloop, out_innerloopdata);
+ } else {
+ /* Find the specified ufunc inner loop, and fill in the dtypes */
+ retval = find_specified_ufunc_inner_loop(ufunc, type_tup,
+ operands, casting, any_object, out_dtypes,
+ out_innerloop, out_innerloopdata);
+ }
+
+ return retval;
+}
+
+/*
+ * This function applies special type resolution rules for the case
+ * where all the functions have the pattern XX->bool, using
+ * PyArray_ResultType instead of a linear search to get the best
+ * loop.
+ *
+ * Note that a simpler linear search through the functions loop
+ * is still done, but switching to a simple array lookup for
+ * built-in types would be better at some point.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+NPY_NO_EXPORT int
+PyUFunc_SimpleBinaryComparisonTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int i, type_num, type_num1, type_num2;
+ char *ufunc_name;
+
+ ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>";
+
+ if (ufunc->nin != 2 || ufunc->nout != 1) {
+ PyErr_Format(PyExc_RuntimeError, "ufunc %s is configured "
+ "to use binary comparison type resolution but has "
+ "the wrong number of inputs or outputs",
+ ufunc_name);
+ return -1;
+ }
+
+ /*
+ * Use the default type resolution if there's a custom data type
+ * or object arrays.
+ */
+ type_num1 = PyArray_DESCR(operands[0])->type_num;
+ type_num2 = PyArray_DESCR(operands[1])->type_num;
+ if (type_num1 >= NPY_NTYPES || type_num2 >= NPY_NTYPES ||
+ type_num1 == NPY_OBJECT || type_num2 == NPY_OBJECT) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (type_tup == NULL) {
+ /* Input types are the result type */
+ out_dtypes[0] = PyArray_ResultType(2, operands, 0, NULL);
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ }
+ else {
+ /*
+ * If the type tuple isn't a single-element tuple, let the
+ * default type resolution handle this one.
+ */
+ if (!PyTuple_Check(type_tup) || PyTuple_GET_SIZE(type_tup) != 1) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (!PyArray_DescrCheck(PyTuple_GET_ITEM(type_tup, 0))) {
+ PyErr_SetString(PyExc_ValueError,
+ "require data type in the type tuple");
+ return -1;
+ }
+
+ out_dtypes[0] = (PyArray_Descr *)PyTuple_GET_ITEM(type_tup, 0);
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[0]);
+ Py_INCREF(out_dtypes[1]);
+ }
+
+ /* Output type is always boolean */
+ out_dtypes[2] = PyArray_DescrFromType(NPY_BOOL);
+ if (out_dtypes[2] == NULL) {
+ for (i = 0; i < 2; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
+ return -1;
+ }
+
+ /* Check against the casting rules */
+ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
+ for (i = 0; i < 3; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
+ return -1;
+ }
+
+ type_num = out_dtypes[0]->type_num;
+
+ /* If we have a built-in type, search in the functions list */
+ if (type_num < NPY_NTYPES) {
+ char *types = ufunc->types;
+ int n = ufunc->ntypes;
+
+ for (i = 0; i < n; ++i) {
+ if (types[3*i] == type_num) {
+ *out_innerloop = ufunc->functions[i];
+ *out_innerloopdata = ufunc->data[i];
+ return 0;
+ }
+ }
+
+ PyErr_Format(PyExc_TypeError,
+ "ufunc '%s' not supported for the input types",
+ ufunc_name);
+ return -1;
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "user type shouldn't have resulted from type promotion");
+ return -1;
+ }
+}
+
+/*
+ * This function applies special type resolution rules for the case
+ * where all the functions have the pattern X->X, copying
+ * the input descr directly so that metadata is maintained.
+ *
+ * Note that a simpler linear search through the functions loop
+ * is still done, but switching to a simple array lookup for
+ * built-in types would be better at some point.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+NPY_NO_EXPORT int
+PyUFunc_SimpleUnaryOperationTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int i, type_num, type_num1;
+ char *ufunc_name;
+
+ ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>";
+
+ if (ufunc->nin != 1 || ufunc->nout != 1) {
+ PyErr_Format(PyExc_RuntimeError, "ufunc %s is configured "
+ "to use unary operation type resolution but has "
+ "the wrong number of inputs or outputs",
+ ufunc_name);
+ return -1;
+ }
+
+ /*
+ * Use the default type resolution if there's a custom data type
+ * or object arrays.
+ */
+ type_num1 = PyArray_DESCR(operands[0])->type_num;
+ if (type_num1 >= NPY_NTYPES || type_num1 == NPY_OBJECT) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (type_tup == NULL) {
+ /* Input types are the result type */
+ out_dtypes[0] = PyArray_DESCR(operands[0]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ }
+ else {
+ /*
+ * If the type tuple isn't a single-element tuple, let the
+ * default type resolution handle this one.
+ */
+ if (!PyTuple_Check(type_tup) || PyTuple_GET_SIZE(type_tup) != 1) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (!PyArray_DescrCheck(PyTuple_GET_ITEM(type_tup, 0))) {
+ PyErr_SetString(PyExc_ValueError,
+ "require data type in the type tuple");
+ return -1;
+ }
+
+ out_dtypes[0] = (PyArray_Descr *)PyTuple_GET_ITEM(type_tup, 0);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ }
+
+ /* Check against the casting rules */
+ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
+ for (i = 0; i < 2; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
+ return -1;
+ }
+
+ type_num = out_dtypes[0]->type_num;
+
+ /* If we have a built-in type, search in the functions list */
+ if (type_num < NPY_NTYPES) {
+ char *types = ufunc->types;
+ int n = ufunc->ntypes;
+
+ for (i = 0; i < n; ++i) {
+ if (types[2*i] == type_num) {
+ *out_innerloop = ufunc->functions[i];
+ *out_innerloopdata = ufunc->data[i];
+ return 0;
+ }
+ }
+
+ PyErr_Format(PyExc_TypeError,
+ "ufunc '%s' not supported for the input types",
+ ufunc_name);
+ return -1;
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "user type shouldn't have resulted from type promotion");
+ return -1;
+ }
+}
+
+/*
+ * This function applies special type resolution rules for the case
+ * where all the functions have the pattern XX->X, using
+ * PyArray_ResultType instead of a linear search to get the best
+ * loop.
+ *
+ * Note that a simpler linear search through the functions loop
+ * is still done, but switching to a simple array lookup for
+ * built-in types would be better at some point.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+NPY_NO_EXPORT int
+PyUFunc_SimpleBinaryOperationTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int i, type_num, type_num1, type_num2;
+ char *ufunc_name;
+
+ ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>";
+
+ if (ufunc->nin != 2 || ufunc->nout != 1) {
+ PyErr_Format(PyExc_RuntimeError, "ufunc %s is configured "
+ "to use binary operation type resolution but has "
+ "the wrong number of inputs or outputs",
+ ufunc_name);
+ return -1;
+ }
+
+ /*
+ * Use the default type resolution if there's a custom data type
+ * or object arrays.
+ */
+ type_num1 = PyArray_DESCR(operands[0])->type_num;
+ type_num2 = PyArray_DESCR(operands[1])->type_num;
+ if (type_num1 >= NPY_NTYPES || type_num2 >= NPY_NTYPES ||
+ type_num1 == NPY_OBJECT || type_num2 == NPY_OBJECT) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (type_tup == NULL) {
+ /* Input types are the result type */
+ out_dtypes[0] = PyArray_ResultType(2, operands, 0, NULL);
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+ }
+ else {
+ /*
+ * If the type tuple isn't a single-element tuple, let the
+ * default type resolution handle this one.
+ */
+ if (!PyTuple_Check(type_tup) || PyTuple_GET_SIZE(type_tup) != 1) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (!PyArray_DescrCheck(PyTuple_GET_ITEM(type_tup, 0))) {
+ PyErr_SetString(PyExc_ValueError,
+ "require data type in the type tuple");
+ return -1;
+ }
+
+ out_dtypes[0] = (PyArray_Descr *)PyTuple_GET_ITEM(type_tup, 0);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+ }
+
+ /* Check against the casting rules */
+ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
+ for (i = 0; i < 3; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
+ return -1;
+ }
+
+ type_num = out_dtypes[0]->type_num;
+
+ /* If we have a built-in type, search in the functions list */
+ if (type_num < NPY_NTYPES) {
+ char *types = ufunc->types;
+ int n = ufunc->ntypes;
+
+ for (i = 0; i < n; ++i) {
+ if (types[3*i] == type_num) {
+ *out_innerloop = ufunc->functions[i];
+ *out_innerloopdata = ufunc->data[i];
+ return 0;
+ }
+ }
+
+ PyErr_Format(PyExc_TypeError,
+ "ufunc '%s' not supported for the input types",
+ ufunc_name);
+ return -1;
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "user type shouldn't have resulted from type promotion");
+ return -1;
+ }
+}
+
+/*
+ * This function applies special type resolution rules for the absolute
+ * ufunc. This ufunc converts complex -> float, so isn't covered
+ * by the simple unary type resolution.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+NPY_NO_EXPORT int
+PyUFunc_AbsoluteTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ /* Use the default for complex types, to find the loop producing float */
+ if (PyTypeNum_ISCOMPLEX(PyArray_DESCR(operands[0])->type_num)) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+ else {
+ return PyUFunc_SimpleUnaryOperationTypeResolution(ufunc, casting,
+ operands, type_tup, out_dtypes, out_innerloop,
+ out_innerloopdata);
+ }
+}
+
+
+/*
+ * This function returns the a new reference to the
+ * capsule with the datetime metadata.
+ *
+ * NOTE: This function is copied from datetime.c in multiarray,
+ * because umath and multiarray are not linked together.
+ */
+static PyObject *
+get_datetime_metacobj_from_dtype(PyArray_Descr *dtype)
+{
+ PyObject *metacobj;
+
+ /* Check that the dtype has metadata */
+ if (dtype->metadata == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Datetime type object is invalid, lacks metadata");
+ return NULL;
+ }
+
+ /* Check that the dtype has unit metadata */
+ metacobj = PyDict_GetItemString(dtype->metadata, NPY_METADATA_DTSTR);
+ if (metacobj == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Datetime type object is invalid, lacks unit metadata");
+ return NULL;
+ }
+
+ Py_INCREF(metacobj);
+ return metacobj;
+}
+
+/*
+ * This function returns a pointer to the DateTimeMetaData
+ * contained within the provided datetime dtype.
+ *
+ * NOTE: This function is copied from datetime.c in multiarray,
+ * because umath and multiarray are not linked together.
+ */
+static PyArray_DatetimeMetaData *
+get_datetime_metadata_from_dtype(PyArray_Descr *dtype)
+{
+ PyObject *metacobj;
+ PyArray_DatetimeMetaData *meta = NULL;
+
+ metacobj = get_datetime_metacobj_from_dtype(dtype);
+ if (metacobj == NULL) {
+ return NULL;
+ }
+
+ /* Check that the dtype has an NpyCapsule for the metadata */
+ meta = (PyArray_DatetimeMetaData *)NpyCapsule_AsVoidPtr(metacobj);
+ if (meta == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Datetime type object is invalid, unit metadata is corrupt");
+ return NULL;
+ }
+
+ return meta;
+}
+
+/*
+ * Creates a new NPY_TIMEDELTA dtype, copying the datetime metadata
+ * from the given dtype.
+ *
+ * NOTE: This function is copied from datetime.c in multiarray,
+ * because umath and multiarray are not linked together.
+ */
+static PyArray_Descr *
+timedelta_dtype_with_copied_meta(PyArray_Descr *dtype)
+{
+ PyArray_Descr *ret;
+ PyObject *metacobj;
+
+ ret = PyArray_DescrNewFromType(NPY_TIMEDELTA);
+ if (ret == NULL) {
+ return NULL;
+ }
+ Py_XDECREF(ret->metadata);
+ ret->metadata = PyDict_New();
+ if (ret->metadata == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ metacobj = get_datetime_metacobj_from_dtype(dtype);
+ if (metacobj == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ if (PyDict_SetItemString(ret->metadata, NPY_METADATA_DTSTR,
+ metacobj) < 0) {
+ Py_DECREF(metacobj);
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+
+/*
+ * This function applies the type resolution rules for addition.
+ * In particular, there are a number of special cases with datetime:
+ * m8[<A>] + m8[<B>] => m8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)]
+ * m8[<A>] + int => m8[<A>] + m8[<A>]
+ * int + m8[<A>] => m8[<A>] + m8[<A>]
+ * M8[<A>] + int => M8[<A>] + m8[<A>]
+ * int + M8[<A>] => m8[<A>] + M8[<A>]
+ * M8[<A>] + m8[<B>] => M8[<A>] + m8[<A>]
+ * m8[<A>] + M8[<B>] => m8[<B>] + M8[<B>]
+ * TODO: Non-linear time unit cases require highly special-cased loops
+ * M8[<A>] + m8[Y|M|B]
+ * m8[Y|M|B] + M8[<A>]
+ */
+NPY_NO_EXPORT int
+PyUFunc_AdditionTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int type_num1, type_num2;
+ char *types;
+ int i, n;
+ char *ufunc_name;
+
+ ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>";
+
+ type_num1 = PyArray_DESCR(operands[0])->type_num;
+ type_num2 = PyArray_DESCR(operands[1])->type_num;
+
+ /* Use the default when datetime and timedelta are not involved */
+ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (type_num1 == NPY_TIMEDELTA) {
+ /* m8[<A>] + m8[<B>] => m8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)] */
+ if (type_num2 == NPY_TIMEDELTA) {
+ out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
+ PyArray_DESCR(operands[1]));
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+ }
+ /* m8[<A>] + M8[<B>] => m8[<B>] + M8[<B>] */
+ else if (type_num2 == NPY_DATETIME) {
+ /* Make a new NPY_TIMEDELTA, and copy type2's metadata */
+ out_dtypes[0] = timedelta_dtype_with_copied_meta(
+ PyArray_DESCR(operands[1]));
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+ out_dtypes[1] = PyArray_DESCR(operands[1]);
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[1];
+ Py_INCREF(out_dtypes[2]);
+ }
+ /* m8[<A>] + int => m8[<A>] + m8[<A>] */
+ else if (PyTypeNum_ISINTEGER(type_num2) ||
+ PyTypeNum_ISBOOL(type_num2)) {
+ out_dtypes[0] = PyArray_DESCR(operands[0]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num2 = NPY_TIMEDELTA;
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else if (type_num1 == NPY_DATETIME) {
+ /* M8[<A>] + m8[<B>] => M8[<A>] + m8[<A>] */
+ /* M8[<A>] + int => M8[<A>] + m8[<A>] */
+ if (type_num2 == NPY_TIMEDELTA ||
+ PyTypeNum_ISINTEGER(type_num2) ||
+ PyTypeNum_ISBOOL(type_num2)) {
+ /* Make a new NPY_TIMEDELTA, and copy type1's metadata */
+ out_dtypes[1] = timedelta_dtype_with_copied_meta(
+ PyArray_DESCR(operands[0]));
+ if (out_dtypes[1] == NULL) {
+ return -1;
+ }
+ out_dtypes[0] = PyArray_DESCR(operands[0]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num2 = NPY_TIMEDELTA;
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) {
+ /* int + m8[<A>] => m8[<A>] + m8[<A>] */
+ if (type_num2 == NPY_TIMEDELTA) {
+ out_dtypes[0] = PyArray_DESCR(operands[1]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num1 = NPY_TIMEDELTA;
+ }
+ else if (type_num2 == NPY_DATETIME) {
+ /* Make a new NPY_TIMEDELTA, and copy type2's metadata */
+ out_dtypes[0] = timedelta_dtype_with_copied_meta(
+ PyArray_DESCR(operands[1]));
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+ out_dtypes[1] = PyArray_DESCR(operands[1]);
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[1];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num1 = NPY_TIMEDELTA;
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else {
+ goto type_reso_error;
+ }
+
+ /* Check against the casting rules */
+ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
+ for (i = 0; i < 3; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
+ return -1;
+ }
+
+ /* Search in the functions list */
+ types = ufunc->types;
+ n = ufunc->ntypes;
+
+ for (i = 0; i < n; ++i) {
+ if (types[3*i] == type_num1 && types[3*i+1] == type_num2) {
+ *out_innerloop = ufunc->functions[i];
+ *out_innerloopdata = ufunc->data[i];
+ return 0;
+ }
+ }
+
+ PyErr_Format(PyExc_TypeError,
+ "internal error: could not find appropriate datetime "
+ "inner loop in %s ufunc", ufunc_name);
+ return -1;
+
+type_reso_error: {
+ PyObject *errmsg;
+ errmsg = PyUString_FromFormat("ufunc %s cannot use operands "
+ "with types ", ufunc_name);
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[0])));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" and "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[1])));
+ PyErr_SetObject(PyExc_TypeError, errmsg);
+ return -1;
+ }
+}
+
+/*
+ * This function applies the type resolution rules for subtraction.
+ * In particular, there are a number of special cases with datetime:
+ * m8[<A>] - m8[<B>] => m8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)]
+ * m8[<A>] - int => m8[<A>] - m8[<A>]
+ * int - m8[<A>] => m8[<A>] - m8[<A>]
+ * M8[<A>] - int => M8[<A>] - m8[<A>]
+ * M8[<A>] - m8[<B>] => M8[<A>] - m8[<A>]
+ * TODO: Non-linear time unit cases require highly special-cased loops
+ * M8[<A>] - m8[Y|M|B]
+ */
+NPY_NO_EXPORT int
+PyUFunc_SubtractionTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int type_num1, type_num2;
+ char *types;
+ int i, n;
+ char *ufunc_name;
+
+ ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>";
+
+ type_num1 = PyArray_DESCR(operands[0])->type_num;
+ type_num2 = PyArray_DESCR(operands[1])->type_num;
+
+ /* Use the default when datetime and timedelta are not involved */
+ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (type_num1 == NPY_TIMEDELTA) {
+ /* m8[<A>] - m8[<B>] => m8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)] */
+ if (type_num2 == NPY_TIMEDELTA) {
+ out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
+ PyArray_DESCR(operands[1]));
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+ }
+ /* m8[<A>] - int => m8[<A>] - m8[<A>] */
+ else if (PyTypeNum_ISINTEGER(type_num2) ||
+ PyTypeNum_ISBOOL(type_num2)) {
+ out_dtypes[0] = PyArray_DESCR(operands[0]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num2 = NPY_TIMEDELTA;
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else if (type_num1 == NPY_DATETIME) {
+ /* M8[<A>] - m8[<B>] => M8[<A>] - m8[<A>] */
+ /* M8[<A>] - int => M8[<A>] - m8[<A>] */
+ if (type_num2 == NPY_TIMEDELTA ||
+ PyTypeNum_ISINTEGER(type_num2) ||
+ PyTypeNum_ISBOOL(type_num2)) {
+ /* Make a new NPY_TIMEDELTA, and copy type1's metadata */
+ out_dtypes[1] = timedelta_dtype_with_copied_meta(
+ PyArray_DESCR(operands[0]));
+ if (out_dtypes[1] == NULL) {
+ return -1;
+ }
+ out_dtypes[0] = PyArray_DESCR(operands[0]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num2 = NPY_TIMEDELTA;
+ }
+ /* M8[<A>] - M8[<A>] (producing m8[<A>])*/
+ else if (type_num2 == NPY_DATETIME) {
+ PyArray_DatetimeMetaData *meta1, *meta2;
+
+ meta1 = get_datetime_metadata_from_dtype(
+ PyArray_DESCR(operands[0]));
+ if (meta1 == NULL) {
+ return -1;
+ }
+ meta2 = get_datetime_metadata_from_dtype(
+ PyArray_DESCR(operands[1]));
+ if (meta2 == NULL) {
+ return -1;
+ }
+
+ /* If the metadata matches up, the subtraction is ok */
+ if (meta1->num == meta2->num &&
+ meta1->base == meta2->base &&
+ meta1->events == meta2->events) {
+ out_dtypes[0] = PyArray_DESCR(operands[1]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ /* Make a new NPY_TIMEDELTA, and copy type1's metadata */
+ out_dtypes[2] = timedelta_dtype_with_copied_meta(
+ PyArray_DESCR(operands[0]));
+ if (out_dtypes[2] == NULL) {
+ return -1;
+ }
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) {
+ /* int - m8[<A>] => m8[<A>] - m8[<A>] */
+ if (type_num2 == NPY_TIMEDELTA) {
+ out_dtypes[0] = PyArray_DESCR(operands[1]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num1 = NPY_TIMEDELTA;
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else {
+ goto type_reso_error;
+ }
+
+ /* Check against the casting rules */
+ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
+ for (i = 0; i < 3; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
+ return -1;
+ }
+
+ /* Search in the functions list */
+ types = ufunc->types;
+ n = ufunc->ntypes;
+
+ for (i = 0; i < n; ++i) {
+ if (types[3*i] == type_num1 && types[3*i+1] == type_num2) {
+ *out_innerloop = ufunc->functions[i];
+ *out_innerloopdata = ufunc->data[i];
+ return 0;
+ }
+ }
+
+ PyErr_Format(PyExc_TypeError,
+ "internal error: could not find appropriate datetime "
+ "inner loop in %s ufunc", ufunc_name);
+ return -1;
+
+type_reso_error: {
+ PyObject *errmsg;
+ errmsg = PyUString_FromFormat("ufunc %s cannot use operands "
+ "with types ", ufunc_name);
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[0])));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" and "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[1])));
+ PyErr_SetObject(PyExc_TypeError, errmsg);
+ return -1;
+ }
+}
+
+/*
+ * This function applies the type resolution rules for multiplication.
+ * In particular, there are a number of special cases with datetime:
+ * int## * m8[<A>] => int64 * m8[<A>]
+ * m8[<A>] * int## => m8[<A>] * int64
+ * float## * m8[<A>] => float64 * m8[<A>]
+ * m8[<A>] * float## => m8[<A>] * float64
+ */
+NPY_NO_EXPORT int
+PyUFunc_MultiplicationTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int type_num1, type_num2;
+ char *types;
+ int i, n;
+ char *ufunc_name;
+
+ ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>";
+
+ type_num1 = PyArray_DESCR(operands[0])->type_num;
+ type_num2 = PyArray_DESCR(operands[1])->type_num;
+
+ /* Use the default when datetime and timedelta are not involved */
+ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (type_num1 == NPY_TIMEDELTA) {
+ /* m8[<A>] * int## => m8[<A>] * int64 */
+ if (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2)) {
+ out_dtypes[0] = PyArray_DESCR(operands[0]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = PyArray_DescrNewFromType(NPY_INT64);
+ if (out_dtypes[1] == NULL) {
+ Py_DECREF(out_dtypes[0]);
+ out_dtypes[0] = NULL;
+ return -1;
+ }
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num2 = NPY_INT64;
+ }
+ /* m8[<A>] * float## => m8[<A>] * float64 */
+ else if (PyTypeNum_ISFLOAT(type_num2)) {
+ out_dtypes[0] = PyArray_DESCR(operands[0]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = PyArray_DescrNewFromType(NPY_DOUBLE);
+ if (out_dtypes[1] == NULL) {
+ Py_DECREF(out_dtypes[0]);
+ out_dtypes[0] = NULL;
+ return -1;
+ }
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num2 = NPY_DOUBLE;
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) {
+ /* int## * m8[<A>] => int64 * m8[<A>] */
+ if (type_num2 == NPY_TIMEDELTA) {
+ out_dtypes[0] = PyArray_DescrNewFromType(NPY_INT64);
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+ out_dtypes[1] = PyArray_DESCR(operands[1]);
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[1];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num1 = NPY_INT64;
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else if (PyTypeNum_ISFLOAT(type_num1)) {
+ /* float## * m8[<A>] => float64 * m8[<A>] */
+ if (type_num2 == NPY_TIMEDELTA) {
+ out_dtypes[0] = PyArray_DescrNewFromType(NPY_DOUBLE);
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+ out_dtypes[1] = PyArray_DESCR(operands[1]);
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[1];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num1 = NPY_DOUBLE;
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else {
+ goto type_reso_error;
+ }
+
+ /* Check against the casting rules */
+ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
+ for (i = 0; i < 3; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
+ return -1;
+ }
+
+ /* Search in the functions list */
+ types = ufunc->types;
+ n = ufunc->ntypes;
+
+ for (i = 0; i < n; ++i) {
+ if (types[3*i] == type_num1 && types[3*i+1] == type_num2) {
+ *out_innerloop = ufunc->functions[i];
+ *out_innerloopdata = ufunc->data[i];
+ return 0;
+ }
+ }
+
+ PyErr_Format(PyExc_TypeError,
+ "internal error: could not find appropriate datetime "
+ "inner loop in %s ufunc", ufunc_name);
+ return -1;
+
+type_reso_error: {
+ PyObject *errmsg;
+ errmsg = PyUString_FromFormat("ufunc %s cannot use operands "
+ "with types ", ufunc_name);
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[0])));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" and "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[1])));
+ PyErr_SetObject(PyExc_TypeError, errmsg);
+ return -1;
+ }
+}
+
+/*
+ * This function applies the type resolution rules for division.
+ * In particular, there are a number of special cases with datetime:
+ * m8[<A>] / int## => m8[<A>] / int64
+ * m8[<A>] / float## => m8[<A>] / float64
+ */
+NPY_NO_EXPORT int
+PyUFunc_DivisionTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int type_num1, type_num2;
+ char *types;
+ int i, n;
+ char *ufunc_name;
+
+ ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>";
+
+ type_num1 = PyArray_DESCR(operands[0])->type_num;
+ type_num2 = PyArray_DESCR(operands[1])->type_num;
+
+ /* Use the default when datetime and timedelta are not involved */
+ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
+ return PyUFunc_DefaultTypeResolution(ufunc, casting, operands,
+ type_tup, out_dtypes, out_innerloop, out_innerloopdata);
+ }
+
+ if (type_num1 == NPY_TIMEDELTA) {
+ /* m8[<A>] / int## => m8[<A>] / int64 */
+ if (PyTypeNum_ISINTEGER(type_num2)) {
+ out_dtypes[0] = PyArray_DESCR(operands[0]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = PyArray_DescrNewFromType(NPY_INT64);
+ if (out_dtypes[1] == NULL) {
+ Py_DECREF(out_dtypes[0]);
+ out_dtypes[0] = NULL;
+ return -1;
+ }
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num2 = NPY_INT64;
+ }
+ /* m8[<A>] / float## => m8[<A>] / float64 */
+ else if (PyTypeNum_ISFLOAT(type_num2)) {
+ out_dtypes[0] = PyArray_DESCR(operands[0]);
+ Py_INCREF(out_dtypes[0]);
+ out_dtypes[1] = PyArray_DescrNewFromType(NPY_DOUBLE);
+ if (out_dtypes[1] == NULL) {
+ Py_DECREF(out_dtypes[0]);
+ out_dtypes[0] = NULL;
+ return -1;
+ }
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+
+ type_num2 = NPY_DOUBLE;
+ }
+ else {
+ goto type_reso_error;
+ }
+ }
+ else {
+ goto type_reso_error;
+ }
+
+ /* Check against the casting rules */
+ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
+ for (i = 0; i < 3; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
+ return -1;
+ }
+
+ /* Search in the functions list */
+ types = ufunc->types;
+ n = ufunc->ntypes;
+
+ for (i = 0; i < n; ++i) {
+ if (types[3*i] == type_num1 && types[3*i+1] == type_num2) {
+ *out_innerloop = ufunc->functions[i];
+ *out_innerloopdata = ufunc->data[i];
+ return 0;
+ }
+ }
+
+ PyErr_Format(PyExc_TypeError,
+ "internal error: could not find appropriate datetime "
+ "inner loop in %s ufunc", ufunc_name);
+ return -1;
+
+type_reso_error: {
+ PyObject *errmsg;
+ errmsg = PyUString_FromFormat("ufunc %s cannot use operands "
+ "with types ", ufunc_name);
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[0])));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" and "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[1])));
+ PyErr_SetObject(PyExc_TypeError, errmsg);
+ return -1;
+ }
+}
+
+/*UFUNC_API
+ *
+ * Validates that the input operands can be cast to
+ * the input types, and the output types can be cast to
+ * the output operands where provided.
+ *
+ * Returns 0 on success, -1 (with exception raised) on validation failure.
+ */
+NPY_NO_EXPORT int
+PyUFunc_ValidateCasting(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyArray_Descr **dtypes)
+{
+ int i, nin = ufunc->nin, nop = nin + ufunc->nout;
+ char *ufunc_name;
+
+ ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>";
+
+ for (i = 0; i < nop; ++i) {
+ if (i < nin) {
+ if (!PyArray_CanCastArrayTo(operands[i], dtypes[i], casting)) {
+ PyObject *errmsg;
+ errmsg = PyUString_FromFormat("Cannot cast ufunc %s "
+ "input from ", ufunc_name);
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[i])));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" to "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)dtypes[i]));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromFormat(" with casting rule %s",
+ _casting_to_string(casting)));
+ PyErr_SetObject(PyExc_TypeError, errmsg);
+ return -1;
+ }
+ } else if (operands[i] != NULL) {
+ if (!PyArray_CanCastTypeTo(dtypes[i],
+ PyArray_DESCR(operands[i]), casting)) {
+ PyObject *errmsg;
+ errmsg = PyUString_FromFormat("Cannot cast ufunc %s "
+ "output from ", ufunc_name);
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)dtypes[i]));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" to "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(operands[i])));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromFormat(" with casting rule %s",
+ _casting_to_string(casting)));
+ PyErr_SetObject(PyExc_TypeError, errmsg);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
static void
trivial_two_operand_loop(PyArrayObject **op,
PyUFuncGenericFunction innerloop,
@@ -2136,8 +3348,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
int nin, nout;
int i, idim, nop;
char *ufunc_name;
- int retval = -1, any_object = 0, subok = 1;
- NPY_CASTING input_casting;
+ int retval = -1, subok = 1;
PyArray_Descr *dtype[NPY_MAXARGS];
@@ -2174,8 +3385,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
*/
PyObject *arr_prep_args = NULL;
- int trivial_loop_ok = 0;
-
NPY_ORDER order = NPY_KEEPORDER;
/*
* Many things in NumPy do unsafe casting (doing int += float, etc).
@@ -2210,7 +3419,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
/* Get all the arguments */
retval = get_ufunc_arguments(self, args, kwds,
- op, &order, &casting, &extobj, &type_tup, &subok, &any_object);
+ op, &order, &casting, &extobj, &type_tup, &subok);
if (retval < 0) {
goto fail;
}
@@ -2292,25 +3501,9 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
NPY_UF_DBG_PRINT("Finding inner loop\n");
- /*
- * Decide the casting rules for inputs and outputs. We want
- * NPY_SAFE_CASTING or stricter, so that the loop selection code
- * doesn't choose an integer loop for float inputs, for example.
- */
- input_casting = (casting > NPY_SAFE_CASTING) ? NPY_SAFE_CASTING : casting;
- if (type_tup == NULL) {
- /* Find the best ufunc inner loop, and fill in the dtypes */
- retval = find_best_ufunc_inner_loop(self, op, input_casting, casting,
- buffersize, any_object, dtype,
- &innerloop, &innerloopdata, &trivial_loop_ok);
- } else {
- /* Find the specified ufunc inner loop, and fill in the dtypes */
- retval = find_specified_ufunc_inner_loop(self, type_tup,
- op, casting,
- buffersize, any_object, dtype,
- &innerloop, &innerloopdata, &trivial_loop_ok);
- }
+ retval = self->type_resolution_function(self, casting,
+ op, type_tup, dtype, &innerloop, &innerloopdata);
if (retval < 0) {
goto fail;
}
@@ -2408,7 +3601,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
* Set up the inner strides array. Because we're not doing
* buffering, the strides are fixed throughout the looping.
*/
- inner_strides = (npy_intp *)_pya_malloc(
+ inner_strides = (npy_intp *)PyArray_malloc(
NPY_SIZEOF_INTP * (nop+core_dim_ixs_size));
/* The strides after the first nop match core_dim_ixs */
core_dim_ixs = self->core_dim_ixs;
@@ -2504,7 +3697,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
goto fail;
}
- _pya_free(inner_strides);
+ PyArray_free(inner_strides);
NpyIter_Deallocate(iter);
/* The caller takes ownership of all the references in op */
for (i = 0; i < nop; ++i) {
@@ -2522,7 +3715,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
fail:
NPY_UF_DBG_PRINT1("Returning failure code %d\n", retval);
if (inner_strides) {
- _pya_free(inner_strides);
+ PyArray_free(inner_strides);
}
if (iter != NULL) {
NpyIter_Deallocate(iter);
@@ -2553,8 +3746,7 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
int nin, nout;
int i, nop;
char *ufunc_name;
- int retval = -1, any_object = 0, subok = 1;
- NPY_CASTING input_casting;
+ int retval = -1, subok = 1;
PyArray_Descr *dtype[NPY_MAXARGS];
@@ -2577,7 +3769,6 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
int trivial_loop_ok = 0;
- /* TODO: For 1.6, the default should probably be NPY_CORDER */
NPY_ORDER order = NPY_KEEPORDER;
/*
* Many things in NumPy do unsafe casting (doing int += float, etc).
@@ -2593,7 +3784,6 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
return -1;
}
- /* TODO: support generalized ufunc */
if (self->core_enabled) {
return PyUFunc_GeneralizedFunction(self, args, kwds, op);
}
@@ -2617,7 +3807,7 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
/* Get all the arguments */
retval = get_ufunc_arguments(self, args, kwds,
- op, &order, &casting, &extobj, &type_tup, &subok, &any_object);
+ op, &order, &casting, &extobj, &type_tup, &subok);
if (retval < 0) {
goto fail;
}
@@ -2640,26 +3830,19 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
NPY_UF_DBG_PRINT("Finding inner loop\n");
+ retval = self->type_resolution_function(self, casting,
+ op, type_tup, dtype, &innerloop, &innerloopdata);
+ if (retval < 0) {
+ goto fail;
+ }
+
/*
- * Decide the casting rules for inputs and outputs. We want
- * NPY_SAFE_CASTING or stricter, so that the loop selection code
- * doesn't choose an integer loop for float inputs, for example.
+ * This checks whether a trivial loop is ok,
+ * making copies of scalar and one dimensional operands if that will
+ * help.
*/
- input_casting = (casting > NPY_SAFE_CASTING) ? NPY_SAFE_CASTING : casting;
-
- if (type_tup == NULL) {
- /* Find the best ufunc inner loop, and fill in the dtypes */
- retval = find_best_ufunc_inner_loop(self, op, input_casting, casting,
- buffersize, any_object, dtype,
- &innerloop, &innerloopdata, &trivial_loop_ok);
- } else {
- /* Find the specified ufunc inner loop, and fill in the dtypes */
- retval = find_specified_ufunc_inner_loop(self, type_tup,
- op, casting,
- buffersize, any_object, dtype,
- &innerloop, &innerloopdata, &trivial_loop_ok);
- }
- if (retval < 0) {
+ trivial_loop_ok = check_for_trivial_loop(self, op, dtype, buffersize);
+ if (trivial_loop_ok < 0) {
goto fail;
}
@@ -2681,6 +3864,7 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
}
}
+
#if NPY_UF_DBG_TRACING
printf("input types:\n");
for (i = 0; i < nin; ++i) {
@@ -4177,16 +5361,17 @@ ufunc_generic_call(PyUFuncObject *self, PyObject *args, PyObject *kwds)
for(i = 0; i < self->nargs; i++) {
mps[i] = NULL;
}
+
errval = PyUFunc_GenericFunction(self, args, kwds, mps);
if (errval < 0) {
for (i = 0; i < self->nargs; i++) {
PyArray_XDECREF_ERR(mps[i]);
}
- if (errval == -1)
+ if (errval == -1) {
return NULL;
+ }
else if (self->nin == 2 && self->nout == 1) {
- /* To allow the other argument to be given a chance
- */
+ /* To allow the other argument to be given a chance */
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
@@ -4199,7 +5384,7 @@ ufunc_generic_call(PyUFuncObject *self, PyObject *args, PyObject *kwds)
/* Free the input references */
for (i = 0; i < self->nin; i++) {
- Py_DECREF(mps[i]);
+ Py_XDECREF(mps[i]);
}
/*
@@ -4424,7 +5609,7 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
{
PyUFuncObject *self;
- self = _pya_malloc(sizeof(PyUFuncObject));
+ self = PyArray_malloc(sizeof(PyUFuncObject));
if (self == NULL) {
return NULL;
}
@@ -4444,6 +5629,8 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
self->obj = NULL;
self->userloops=NULL;
+ self->type_resolution_function = &PyUFunc_DefaultTypeResolution;
+
if (name == NULL) {
self->name = "?";
}
@@ -4484,8 +5671,12 @@ PyUFunc_SetUsesArraysAsData(void **data, size_t i)
return 0;
}
-/* Return 1 if the given data pointer for the loop specifies that it needs the
+/*
+ * Return 1 if the given data pointer for the loop specifies that it needs the
* arrays as the data pointer.
+ *
+ * NOTE: This is easier to specify with the type_resolution_function
+ * in the ufunc object.
*/
static int
_does_loop_use_arrays(void *data)
@@ -4535,8 +5726,8 @@ _free_loop1d_list(PyUFunc_Loop1d *data)
{
while (data != NULL) {
PyUFunc_Loop1d *next = data->next;
- _pya_free(data->arg_types);
- _pya_free(data);
+ PyArray_free(data->arg_types);
+ PyArray_free(data);
data = next;
}
}
@@ -4586,11 +5777,11 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc,
if (key == NULL) {
return -1;
}
- funcdata = _pya_malloc(sizeof(PyUFunc_Loop1d));
+ funcdata = PyArray_malloc(sizeof(PyUFunc_Loop1d));
if (funcdata == NULL) {
goto fail;
}
- newtypes = _pya_malloc(sizeof(int)*ufunc->nargs);
+ newtypes = PyArray_malloc(sizeof(int)*ufunc->nargs);
if (newtypes == NULL) {
goto fail;
}
@@ -4645,8 +5836,8 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc,
/* just replace it with new function */
current->func = function;
current->data = data;
- _pya_free(newtypes);
- _pya_free(funcdata);
+ PyArray_free(newtypes);
+ PyArray_free(funcdata);
}
else {
/*
@@ -4669,8 +5860,8 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc,
fail:
Py_DECREF(key);
- _pya_free(funcdata);
- _pya_free(newtypes);
+ PyArray_free(funcdata);
+ PyArray_free(newtypes);
if (!PyErr_Occurred()) PyErr_NoMemory();
return -1;
}
@@ -4682,23 +5873,23 @@ static void
ufunc_dealloc(PyUFuncObject *self)
{
if (self->core_num_dims) {
- _pya_free(self->core_num_dims);
+ PyArray_free(self->core_num_dims);
}
if (self->core_dim_ixs) {
- _pya_free(self->core_dim_ixs);
+ PyArray_free(self->core_dim_ixs);
}
if (self->core_offsets) {
- _pya_free(self->core_offsets);
+ PyArray_free(self->core_offsets);
}
if (self->core_signature) {
- _pya_free(self->core_signature);
+ PyArray_free(self->core_signature);
}
if (self->ptr) {
- _pya_free(self->ptr);
+ PyArray_free(self->ptr);
}
Py_XDECREF(self->userloops);
Py_XDECREF(self->obj);
- _pya_free(self);
+ PyArray_free(self);
}
static PyObject *
@@ -4956,7 +6147,7 @@ ufunc_get_types(PyUFuncObject *self)
if (list == NULL) {
return NULL;
}
- t = _pya_malloc(no+ni+2);
+ t = PyArray_malloc(no+ni+2);
n = 0;
for (k = 0; k < nt; k++) {
for (j = 0; j<ni; j++) {
@@ -4972,7 +6163,7 @@ ufunc_get_types(PyUFuncObject *self)
str = PyUString_FromStringAndSize(t, no + ni + 2);
PyList_SET_ITEM(list, k, str);
}
- _pya_free(t);
+ PyArray_free(t);
return list;
}
diff --git a/numpy/core/src/umath/ufunc_object.h b/numpy/core/src/umath/ufunc_object.h
index a8886be05..2a5fd63a1 100644
--- a/numpy/core/src/umath/ufunc_object.h
+++ b/numpy/core/src/umath/ufunc_object.h
@@ -7,4 +7,75 @@ ufunc_geterr(PyObject *NPY_UNUSED(dummy), PyObject *args);
NPY_NO_EXPORT PyObject *
ufunc_seterr(PyObject *NPY_UNUSED(dummy), PyObject *args);
+NPY_NO_EXPORT int
+PyUFunc_SimpleBinaryComparisonTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+
+NPY_NO_EXPORT int
+PyUFunc_SimpleUnaryOperationTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+
+NPY_NO_EXPORT int
+PyUFunc_SimpleBinaryOperationTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+
+NPY_NO_EXPORT int
+PyUFunc_AbsoluteTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+
+NPY_NO_EXPORT int
+PyUFunc_AdditionTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+
+NPY_NO_EXPORT int
+PyUFunc_SubtractionTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+
+NPY_NO_EXPORT int
+PyUFunc_MultiplicationTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+NPY_NO_EXPORT int
+PyUFunc_DivisionTypeResolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+
#endif
diff --git a/numpy/core/src/umath/umathmodule.c.src b/numpy/core/src/umath/umathmodule.c.src
index 8d081f85b..b4cece358 100644
--- a/numpy/core/src/umath/umathmodule.c.src
+++ b/numpy/core/src/umath/umathmodule.c.src
@@ -43,6 +43,32 @@
static PyUFuncGenericFunction pyfunc_functions[] = {PyUFunc_On_Om};
+static int object_ufunc_type_resolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int i, nop = ufunc->nin + ufunc->nout;
+
+ out_dtypes[0] = PyArray_DescrFromType(NPY_OBJECT);
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+
+ for (i = 1; i < nop; ++i) {
+ out_dtypes[i] = out_dtypes[0];
+ Py_INCREF(out_dtypes[0]);
+ }
+
+ *out_innerloop = ufunc->functions[0];
+ *out_innerloopdata = ufunc->data[0];
+
+ return 0;
+}
+
static PyObject *
ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUSED(kwds)) {
/* Keywords are ignored for now */
@@ -62,7 +88,7 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS
PyErr_SetString(PyExc_TypeError, "function must be callable");
return NULL;
}
- self = _pya_malloc(sizeof(PyUFuncObject));
+ self = PyArray_malloc(sizeof(PyUFuncObject));
if (self == NULL) {
return NULL;
}
@@ -85,6 +111,8 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS
self->core_offsets = NULL;
self->core_signature = NULL;
+ self->type_resolution_function = &object_ufunc_type_resolution;
+
pyname = PyObject_GetAttrString(function, "__name__");
if (pyname) {
(void) PyString_AsStringAndSize(pyname, &fname, &fname_len);
@@ -115,7 +143,7 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS
if (i) {
offset[1] += (sizeof(void *)-i);
}
- self->ptr = _pya_malloc(offset[0] + offset[1] + sizeof(void *) +
+ self->ptr = PyArray_malloc(offset[0] + offset[1] + sizeof(void *) +
(fname_len + 14));
if (self->ptr == NULL) {
Py_XDECREF(pyname);
diff --git a/numpy/core/tests/test_arrayprint.py b/numpy/core/tests/test_arrayprint.py
index dd86fcbd9..b1dbb1d68 100644
--- a/numpy/core/tests/test_arrayprint.py
+++ b/numpy/core/tests/test_arrayprint.py
@@ -80,8 +80,6 @@ class TestArray2String(TestCase):
"[. o O]")
assert_(np.array2string(x, formatter={'int_kind':_format_function}) ==\
"[. o O]")
- assert_(np.array2string(x, formatter={'timeint':_format_function}) == \
- "[0 1 2]")
assert_(np.array2string(x, formatter={'all':lambda x: "%.4f" % x}) == \
"[0.0000 1.0000 2.0000]")
assert_(np.array2string(x, formatter={'int':lambda x: hex(x)}) == \
diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py
index 97fb1151f..bb6594a9a 100644
--- a/numpy/core/tests/test_datetime.py
+++ b/numpy/core/tests/test_datetime.py
@@ -1,9 +1,12 @@
-from os import path
+import os, pickle
+import numpy
import numpy as np
from numpy.testing import *
+from numpy.compat import asbytes
+import datetime
class TestDateTime(TestCase):
- def test_creation(self):
+ def test_datetime_dtype_creation(self):
for unit in ['Y', 'M', 'W', 'B', 'D',
'h', 'm', 's', 'ms', 'us',
'ns', 'ps', 'fs', 'as']:
@@ -11,7 +14,708 @@ class TestDateTime(TestCase):
assert_(dt1 == np.dtype('datetime64[750%s]' % unit))
dt2 = np.dtype('m8[%s]' % unit)
assert_(dt2 == np.dtype('timedelta64[%s]' % unit))
+
+ # Check that the parser rejects bad datetime types
+ assert_raises(TypeError, np.dtype, 'M8[badunit]')
+ assert_raises(TypeError, np.dtype, 'm8[badunit]')
+ assert_raises(TypeError, np.dtype, 'M8[YY]')
+ assert_raises(TypeError, np.dtype, 'm8[YY]')
+ assert_raises(TypeError, np.dtype, 'M4')
+ assert_raises(TypeError, np.dtype, 'm4')
+ assert_raises(TypeError, np.dtype, 'M7')
+ assert_raises(TypeError, np.dtype, 'm7')
+ assert_raises(TypeError, np.dtype, 'M16')
+ assert_raises(TypeError, np.dtype, 'm16')
+ def test_datetime_casting_rules(self):
+ # Cannot cast safely/same_kind between timedelta and datetime
+ assert_(not np.can_cast('m8', 'M8', casting='same_kind'))
+ assert_(not np.can_cast('M8', 'm8', casting='same_kind'))
+ assert_(not np.can_cast('m8', 'M8', casting='safe'))
+ assert_(not np.can_cast('M8', 'm8', casting='safe'))
+
+ # Can cast safely/same_kind from integer to timedelta
+ assert_(np.can_cast('i8', 'm8', casting='same_kind'))
+ assert_(np.can_cast('i8', 'm8', casting='safe'))
+
+ # Cannot cast safely/same_kind from float to timedelta
+ assert_(not np.can_cast('f4', 'm8', casting='same_kind'))
+ assert_(not np.can_cast('f4', 'm8', casting='safe'))
+
+ # Cannot cast safely/same_kind from integer to datetime
+ assert_(not np.can_cast('i8', 'M8', casting='same_kind'))
+ assert_(not np.can_cast('i8', 'M8', casting='safe'))
+
+ # Cannot cast safely/same_kind from bool to datetime
+ assert_(not np.can_cast('b1', 'M8', casting='same_kind'))
+ assert_(not np.can_cast('b1', 'M8', casting='safe'))
+ # Can cast safely/same_kind from bool to timedelta
+ assert_(np.can_cast('b1', 'm8', casting='same_kind'))
+ assert_(np.can_cast('b1', 'm8', casting='safe'))
+
+ # Can cast datetime safely from months/years to days
+ assert_(np.can_cast('M8[M]', 'M8[D]', casting='safe'))
+ assert_(np.can_cast('M8[Y]', 'M8[D]', casting='safe'))
+ # Cannot cast timedelta safely from months/years to days
+ assert_(not np.can_cast('m8[M]', 'm8[D]', casting='safe'))
+ assert_(not np.can_cast('m8[Y]', 'm8[D]', casting='safe'))
+ # Can cast same_kind from months/years to days
+ assert_(np.can_cast('M8[M]', 'M8[D]', casting='same_kind'))
+ assert_(np.can_cast('M8[Y]', 'M8[D]', casting='same_kind'))
+ assert_(np.can_cast('m8[M]', 'm8[D]', casting='same_kind'))
+ assert_(np.can_cast('m8[Y]', 'm8[D]', casting='same_kind'))
+
+ # Cannot cast safely if the integer multiplier doesn't divide
+ assert_(not np.can_cast('M8[7h]', 'M8[3h]', casting='safe'))
+ assert_(not np.can_cast('M8[3h]', 'M8[6h]', casting='safe'))
+ # But can cast same_kind
+ assert_(np.can_cast('M8[7h]', 'M8[3h]', casting='same_kind'))
+ # Can cast safely if the integer multiplier does divide
+ assert_(np.can_cast('M8[6h]', 'M8[3h]', casting='safe'))
+
+ def test_datetime_scalar_construction(self):
+ # Construct with different units
+ assert_equal(np.datetime64('1950-03-12', 'D'),
+ np.datetime64('1950-03-12'))
+ assert_equal(np.datetime64('1950-03-12', 's'),
+ np.datetime64('1950-03-12', 'm'))
+
+ # When constructing from a scalar or zero-dimensional array,
+ # it either keeps the units or you can override them.
+ a = np.datetime64('2000-03-18T16Z', 'h')
+ b = np.array('2000-03-18T16Z', dtype='M8[h]')
+
+ assert_equal(a.dtype, np.dtype('M8[h]'))
+ assert_equal(b.dtype, np.dtype('M8[h]'))
+
+ assert_equal(np.datetime64(a), a);
+ assert_equal(np.datetime64(a).dtype, np.dtype('M8[h]'))
+
+ assert_equal(np.datetime64(b), a)
+ assert_equal(np.datetime64(b).dtype, np.dtype('M8[h]'))
+
+ assert_equal(np.datetime64(a, 's'), a)
+ assert_equal(np.datetime64(a, 's').dtype, np.dtype('M8[s]'))
+
+ assert_equal(np.datetime64(b, 's'), a)
+ assert_equal(np.datetime64(b, 's').dtype, np.dtype('M8[s]'))
+
+ # Construction from datetime.date
+ assert_equal(np.datetime64('1945-03-25'),
+ np.datetime64(datetime.date(1945,3,25)))
+ assert_equal(np.datetime64('2045-03-25', 'D'),
+ np.datetime64(datetime.date(2045,3,25), 'D'))
+ # Construction from datetime.datetime
+ assert_equal(np.datetime64('1980-01-25T14:36:22.5Z'),
+ np.datetime64(datetime.datetime(1980,1,25,
+ 14,36,22,500000)))
+
+
+ def test_timedelta_scalar_construction(self):
+ # Construct with different units
+ assert_equal(np.timedelta64(7, 'D'),
+ np.timedelta64(1, 'W'))
+ assert_equal(np.timedelta64(120, 's'),
+ np.timedelta64(2, 'm'))
+
+ # When constructing from a scalar or zero-dimensional array,
+ # it either keeps the units or you can override them.
+ a = np.timedelta64(2, 'h')
+ b = np.array(2, dtype='m8[h]')
+
+ assert_equal(a.dtype, np.dtype('m8[h]'))
+ assert_equal(b.dtype, np.dtype('m8[h]'))
+
+ assert_equal(np.timedelta64(a), a);
+ assert_equal(np.timedelta64(a).dtype, np.dtype('m8[h]'))
+
+ assert_equal(np.timedelta64(b), a)
+ assert_equal(np.timedelta64(b).dtype, np.dtype('m8[h]'))
+
+ assert_equal(np.timedelta64(a, 's'), a)
+ assert_equal(np.timedelta64(a, 's').dtype, np.dtype('m8[s]'))
+
+ assert_equal(np.timedelta64(b, 's'), a)
+ assert_equal(np.timedelta64(b, 's').dtype, np.dtype('m8[s]'))
+
+ # Construction from datetime.timedelta
+ assert_equal(np.timedelta64(5, 'D'),
+ np.timedelta64(datetime.timedelta(days=5)))
+ assert_equal(np.timedelta64(102347621, 's'),
+ np.timedelta64(datetime.timedelta(seconds=102347621)))
+ assert_equal(np.timedelta64(-10234760000, 'us'),
+ np.timedelta64(datetime.timedelta(
+ microseconds=-10234760000)))
+ assert_equal(np.timedelta64(10234760000, 'us'),
+ np.timedelta64(datetime.timedelta(
+ microseconds=10234760000)))
+ assert_equal(np.timedelta64(1023476, 'ms'),
+ np.timedelta64(datetime.timedelta(milliseconds=1023476)))
+ assert_equal(np.timedelta64(10, 'm'),
+ np.timedelta64(datetime.timedelta(minutes=10)))
+ assert_equal(np.timedelta64(281, 'h'),
+ np.timedelta64(datetime.timedelta(hours=281)))
+ assert_equal(np.timedelta64(28, 'W'),
+ np.timedelta64(datetime.timedelta(weeks=28)))
+
+ def test_datetime_nat_casting(self):
+ a = np.array('NaT', dtype='M8[D]')
+ b = np.datetime64('NaT', '[D]')
+
+ # Arrays
+ assert_equal(a.astype('M8[s]'), np.array('NaT', dtype='M8[s]'))
+ assert_equal(a.astype('M8[ms]'), np.array('NaT', dtype='M8[ms]'))
+ assert_equal(a.astype('M8[M]'), np.array('NaT', dtype='M8[M]'))
+ assert_equal(a.astype('M8[Y]'), np.array('NaT', dtype='M8[Y]'))
+ assert_equal(a.astype('M8[W]'), np.array('NaT', dtype='M8[W]'))
+
+ # Scalars -> Scalars
+ assert_equal(np.datetime64(b, '[s]'), np.datetime64('NaT', '[s]'))
+ assert_equal(np.datetime64(b, '[ms]'), np.datetime64('NaT', '[ms]'))
+ assert_equal(np.datetime64(b, '[M]'), np.datetime64('NaT', '[M]'))
+ assert_equal(np.datetime64(b, '[Y]'), np.datetime64('NaT', '[Y]'))
+ assert_equal(np.datetime64(b, '[W]'), np.datetime64('NaT', '[W]'))
+
+ # Arrays -> Scalars
+ assert_equal(np.datetime64(a, '[s]'), np.datetime64('NaT', '[s]'))
+ assert_equal(np.datetime64(a, '[ms]'), np.datetime64('NaT', '[ms]'))
+ assert_equal(np.datetime64(a, '[M]'), np.datetime64('NaT', '[M]'))
+ assert_equal(np.datetime64(a, '[Y]'), np.datetime64('NaT', '[Y]'))
+ assert_equal(np.datetime64(a, '[W]'), np.datetime64('NaT', '[W]'))
+
+ def test_days_creation(self):
+ assert_equal(np.array('1599', dtype='M8[D]').astype('i8'),
+ (1600-1970)*365 - (1972-1600)/4 + 3 - 365)
+ assert_equal(np.array('1600', dtype='M8[D]').astype('i8'),
+ (1600-1970)*365 - (1972-1600)/4 + 3)
+ assert_equal(np.array('1601', dtype='M8[D]').astype('i8'),
+ (1600-1970)*365 - (1972-1600)/4 + 3 + 366)
+ assert_equal(np.array('1900', dtype='M8[D]').astype('i8'),
+ (1900-1970)*365 - (1970-1900)/4)
+ assert_equal(np.array('1901', dtype='M8[D]').astype('i8'),
+ (1900-1970)*365 - (1970-1900)/4 + 365)
+ assert_equal(np.array('1967', dtype='M8[D]').astype('i8'), -3*365 - 1)
+ assert_equal(np.array('1968', dtype='M8[D]').astype('i8'), -2*365 - 1)
+ assert_equal(np.array('1969', dtype='M8[D]').astype('i8'), -1*365)
+ assert_equal(np.array('1970', dtype='M8[D]').astype('i8'), 0*365)
+ assert_equal(np.array('1971', dtype='M8[D]').astype('i8'), 1*365)
+ assert_equal(np.array('1972', dtype='M8[D]').astype('i8'), 2*365)
+ assert_equal(np.array('1973', dtype='M8[D]').astype('i8'), 3*365 + 1)
+ assert_equal(np.array('1974', dtype='M8[D]').astype('i8'), 4*365 + 1)
+ assert_equal(np.array('2000', dtype='M8[D]').astype('i8'),
+ (2000 - 1970)*365 + (2000 - 1972)/4)
+ assert_equal(np.array('2001', dtype='M8[D]').astype('i8'),
+ (2000 - 1970)*365 + (2000 - 1972)/4 + 366)
+ assert_equal(np.array('2400', dtype='M8[D]').astype('i8'),
+ (2400 - 1970)*365 + (2400 - 1972)/4 - 3)
+ assert_equal(np.array('2401', dtype='M8[D]').astype('i8'),
+ (2400 - 1970)*365 + (2400 - 1972)/4 - 3 + 366)
+
+ assert_equal(np.array('1600-02-29', dtype='M8[D]').astype('i8'),
+ (1600-1970)*365 - (1972-1600)/4 + 3 + 31 + 28)
+ assert_equal(np.array('1600-03-01', dtype='M8[D]').astype('i8'),
+ (1600-1970)*365 - (1972-1600)/4 + 3 + 31 + 29)
+ assert_equal(np.array('2000-02-29', dtype='M8[D]').astype('i8'),
+ (2000 - 1970)*365 + (2000 - 1972)/4 + 31 + 28)
+ assert_equal(np.array('2000-03-01', dtype='M8[D]').astype('i8'),
+ (2000 - 1970)*365 + (2000 - 1972)/4 + 31 + 29)
+ assert_equal(np.array('2001-03-22', dtype='M8[D]').astype('i8'),
+ (2000 - 1970)*365 + (2000 - 1972)/4 + 366 + 31 + 28 + 21)
+
+ def test_days_to_pydate(self):
+ assert_equal(np.array('1599', dtype='M8[D]').astype('O'),
+ datetime.date(1599, 1, 1))
+ assert_equal(np.array('1600', dtype='M8[D]').astype('O'),
+ datetime.date(1600, 1, 1))
+ assert_equal(np.array('1601', dtype='M8[D]').astype('O'),
+ datetime.date(1601, 1, 1))
+ assert_equal(np.array('1900', dtype='M8[D]').astype('O'),
+ datetime.date(1900, 1, 1))
+ assert_equal(np.array('1901', dtype='M8[D]').astype('O'),
+ datetime.date(1901, 1, 1))
+ assert_equal(np.array('2000', dtype='M8[D]').astype('O'),
+ datetime.date(2000, 1, 1))
+ assert_equal(np.array('2001', dtype='M8[D]').astype('O'),
+ datetime.date(2001, 1, 1))
+ assert_equal(np.array('1600-02-29', dtype='M8[D]').astype('O'),
+ datetime.date(1600, 2, 29))
+ assert_equal(np.array('1600-03-01', dtype='M8[D]').astype('O'),
+ datetime.date(1600, 3, 1))
+ assert_equal(np.array('2001-03-22', dtype='M8[D]').astype('O'),
+ datetime.date(2001, 3, 22))
+
+
+ def test_dtype_comparison(self):
+ assert_(not (np.dtype('M8[us]') == np.dtype('M8[ms]')))
+ assert_(np.dtype('M8[us]') != np.dtype('M8[ms]'))
+ assert_(np.dtype('M8[D]') != np.dtype('M8[B]'))
+ assert_(np.dtype('M8[2D]') != np.dtype('M8[D]'))
+ assert_(np.dtype('M8[D]') != np.dtype('M8[2D]'))
+ assert_(np.dtype('M8[Y]//3') != np.dtype('M8[Y]'))
+
+ def test_pydatetime_creation(self):
+ a = np.array(['1960-03-12', datetime.date(1960, 3, 12)], dtype='M8[D]')
+ assert_equal(a[0], a[1])
+ a = np.array(['1960-03-12', datetime.date(1960, 3, 12)], dtype='M8[s]')
+ assert_equal(a[0], a[1])
+ a = np.array(['1999-12-31', datetime.date(1999, 12, 31)], dtype='M8[D]')
+ assert_equal(a[0], a[1])
+ a = np.array(['1999-12-31', datetime.date(1999, 12, 31)], dtype='M8[s]')
+ assert_equal(a[0], a[1])
+ a = np.array(['2000-01-01', datetime.date(2000, 1, 1)], dtype='M8[D]')
+ assert_equal(a[0], a[1])
+ a = np.array(['2000-01-01', datetime.date(2000, 1, 1)], dtype='M8[s]')
+ assert_equal(a[0], a[1])
+ # Will fail if the date changes during the exact right moment
+ a = np.array(['today', datetime.date.today()], dtype='M8[s]')
+ assert_equal(a[0], a[1])
+ # datetime.datetime.now() returns local time, not UTC
+ #a = np.array(['now', datetime.datetime.now()], dtype='M8[s]')
+ #assert_equal(a[0], a[1])
+
+ def test_pickle(self):
+ # Check that pickle roundtripping works
+ dt = np.dtype('M8[7D]//3')
+ assert_equal(dt, pickle.loads(pickle.dumps(dt)))
+ dt = np.dtype('M8[B]')
+ assert_equal(dt, pickle.loads(pickle.dumps(dt)))
+
+ def test_dtype_promotion(self):
+ # datetime <op> datetime computes the metadata gcd
+ # timedelta <op> timedelta computes the metadata gcd
+ for mM in ['m', 'M']:
+ assert_equal(
+ np.promote_types(np.dtype(mM+'8[2Y]'), np.dtype(mM+'8[2Y]')),
+ np.dtype(mM+'8[2Y]'))
+ assert_equal(
+ np.promote_types(np.dtype(mM+'8[12Y]'), np.dtype(mM+'8[15Y]')),
+ np.dtype(mM+'8[3Y]'))
+ assert_equal(
+ np.promote_types(np.dtype(mM+'8[62M]'), np.dtype(mM+'8[24M]')),
+ np.dtype(mM+'8[2M]'))
+ assert_equal(
+ np.promote_types(np.dtype(mM+'8[1W]'), np.dtype(mM+'8[2D]')),
+ np.dtype(mM+'8[1D]'))
+ assert_equal(
+ np.promote_types(np.dtype(mM+'8[W]'), np.dtype(mM+'8[13s]')),
+ np.dtype(mM+'8[s]'))
+ assert_equal(
+ np.promote_types(np.dtype(mM+'8[13W]'), np.dtype(mM+'8[49s]')),
+ np.dtype(mM+'8[7s]'))
+ # timedelta <op> timedelta raises when there is no reasonable gcd
+ assert_raises(TypeError, np.promote_types,
+ np.dtype('m8[Y]'), np.dtype('m8[D]'))
+ assert_raises(TypeError, np.promote_types,
+ np.dtype('m8[Y]'), np.dtype('m8[B]'))
+ assert_raises(TypeError, np.promote_types,
+ np.dtype('m8[D]'), np.dtype('m8[B]'))
+ assert_raises(TypeError, np.promote_types,
+ np.dtype('m8[M]'), np.dtype('m8[W]'))
+ # timedelta <op> timedelta may overflow with big unit ranges
+ assert_raises(OverflowError, np.promote_types,
+ np.dtype('m8[W]'), np.dtype('m8[fs]'))
+ assert_raises(OverflowError, np.promote_types,
+ np.dtype('m8[s]'), np.dtype('m8[as]'))
+
+
+ def test_pyobject_roundtrip(self):
+ # All datetime types should be able to roundtrip through object
+ a = np.array([0,0,0,0,0,0,0,0,0,
+ -1020040340, -2942398, -1, 0, 1, 234523453, 1199164176],
+ dtype=np.int64)
+ for unit in ['M8[as]', 'M8[16fs]', 'M8[ps]', 'M8[us]',
+ 'M8[as]//12', 'M8[us]//16', 'M8[D]', 'M8[D]//4',
+ 'M8[W]', 'M8[M]', 'M8[Y]']:
+ b = a.copy().view(dtype=unit)
+ b[0] = '-0001-01-01'
+ b[1] = '-0001-12-31'
+ b[2] = '0000-01-01'
+ b[3] = '0001-01-01'
+ b[4] = '1969-12-31T23:59:59.999999Z'
+ b[5] = '1970-01-01'
+ b[6] = '9999-12-31T23:59:59.999999Z'
+ b[7] = '10000-01-01'
+ b[8] = 'NaT'
+
+ assert_equal(b.astype(object).astype(unit), b,
+ "Error roundtripping unit %s" % unit)
+
+ def test_month_truncation(self):
+ # Make sure that months are truncating correctly
+ assert_equal(np.array('1945-03-01', dtype='M8[M]'),
+ np.array('1945-03-31', dtype='M8[M]'))
+ assert_equal(np.array('1969-11-01', dtype='M8[M]'),
+ np.array('1969-11-30T23:59:59.999999Z', dtype='M8[M]'))
+ assert_equal(np.array('1969-12-01', dtype='M8[M]'),
+ np.array('1969-12-31T23:59:59.999999Z', dtype='M8[M]'))
+ assert_equal(np.array('1970-01-01', dtype='M8[M]'),
+ np.array('1970-01-31T23:59:59.999999Z', dtype='M8[M]'))
+ assert_equal(np.array('1980-02-01', dtype='M8[M]'),
+ np.array('1980-02-29T23:59:59.999999Z', dtype='M8[M]'))
+
+ def test_different_unit_comparison(self):
+ # Check some years with units that won't overflow
+ for unit1 in ['Y', 'M', 'D', '6h', 'h', 'm', 's', '10ms',
+ 'ms', 'us']:
+ dt1 = np.dtype('M8[%s]' % unit1)
+ for unit2 in ['Y', 'M', 'D', 'h', 'm', 's', 'ms', 'us']:
+ dt2 = np.dtype('M8[%s]' % unit2)
+ assert_equal(np.array('1945', dtype=dt1),
+ np.array('1945', dtype=dt2))
+ assert_equal(np.array('1970', dtype=dt1),
+ np.array('1970', dtype=dt2))
+ assert_equal(np.array('9999', dtype=dt1),
+ np.array('9999', dtype=dt2))
+ assert_equal(np.array('10000', dtype=dt1),
+ np.array('10000-01-01', dtype=dt2))
+ assert_equal(np.datetime64('1945', unit1),
+ np.datetime64('1945', unit2))
+ assert_equal(np.datetime64('1970', unit1),
+ np.datetime64('1970', unit2))
+ assert_equal(np.datetime64('9999', unit1),
+ np.datetime64('9999', unit2))
+ assert_equal(np.datetime64('10000', unit1),
+ np.datetime64('10000-01-01', unit2))
+ # Check some days with units that won't overflow
+ for unit1 in ['D', '12h', 'h', 'm', 's', '4s', 'ms', 'us']:
+ dt1 = np.dtype('M8[%s]' % unit1)
+ for unit2 in ['D', 'h', 'm', 's', 'ms', 'us']:
+ dt2 = np.dtype('M8[%s]' % unit2)
+ assert_equal(np.array('1932-02-17', dtype=dt1),
+ np.array('1932-02-17T00:00:00Z', dtype=dt2))
+ assert_equal(np.array('10000-04-27', dtype=dt1),
+ np.array('10000-04-27T00:00:00Z', dtype=dt2))
+ assert_equal(np.array('today', dtype=dt1),
+ np.array('today', dtype=dt2))
+ assert_equal(np.datetime64('1932-02-17', unit1),
+ np.datetime64('1932-02-17T00:00:00Z', unit2))
+ assert_equal(np.datetime64('10000-04-27', unit1),
+ np.datetime64('10000-04-27T00:00:00Z', unit2))
+ assert_equal(np.datetime64('today', unit1),
+ np.datetime64('today', unit2))
+
+ # Shouldn't be able to compare datetime and timedelta
+ # TODO: Changing to 'same_kind' or 'safe' casting in the ufuncs by
+ # default is needed to properly catch this kind of thing...
+ a = np.array('2012-12-21', dtype='M8[D]')
+ b = np.array(3, dtype='m8[D]')
+ #assert_raises(TypeError, np.less, a, b)
+ assert_raises(TypeError, np.less, a, b, casting='same_kind')
+
+ def test_datetime_like(self):
+ a = np.array([3], dtype='m8[4D]//6')
+ b = np.array(['2012-12-21'], dtype='M8[D]//3')
+
+ assert_equal(np.ones_like(a).dtype, a.dtype)
+ assert_equal(np.zeros_like(a).dtype, a.dtype)
+ assert_equal(np.empty_like(a).dtype, a.dtype)
+ assert_equal(np.ones_like(b).dtype, b.dtype)
+ assert_equal(np.zeros_like(b).dtype, b.dtype)
+ assert_equal(np.empty_like(b).dtype, b.dtype)
+
+ def test_datetime_unary(self):
+ for tda, tdb, tdzero, tdone, tdmone in \
+ [
+ # One-dimensional arrays
+ (np.array([3], dtype='m8[D]'),
+ np.array([-3], dtype='m8[D]'),
+ np.array([0], dtype='m8[D]'),
+ np.array([1], dtype='m8[D]'),
+ np.array([-1], dtype='m8[D]')),
+ # NumPy scalars
+ (np.timedelta64(3, '[D]'),
+ np.timedelta64(-3, '[D]'),
+ np.timedelta64(0, '[D]'),
+ np.timedelta64(1, '[D]'),
+ np.timedelta64(-1, '[D]'))]:
+ # negative ufunc
+ assert_equal(-tdb, tda)
+ assert_equal((-tdb).dtype, tda.dtype)
+ assert_equal(np.negative(tdb), tda)
+ assert_equal(np.negative(tdb).dtype, tda.dtype)
+
+ # absolute ufunc
+ assert_equal(np.absolute(tdb), tda)
+ assert_equal(np.absolute(tdb).dtype, tda.dtype)
+
+ # sign ufunc
+ assert_equal(np.sign(tda), tdone)
+ assert_equal(np.sign(tdb), tdmone)
+ assert_equal(np.sign(tdzero), tdzero)
+ assert_equal(np.sign(tda).dtype, tda.dtype)
+
+ def test_datetime_add(self):
+ for dta, dtb, dtc, dtnat, tda, tdb, tdc in \
+ [
+ # One-dimensional arrays
+ (np.array(['2012-12-21'], dtype='M8[D]'),
+ np.array(['2012-12-24'], dtype='M8[D]'),
+ np.array(['1940-12-24'], dtype='M8[D]'),
+ np.array(['NaT'], dtype='M8[D]'),
+ np.array([3], dtype='m8[D]'),
+ np.array([11], dtype='m8[h]'),
+ np.array([3*24 + 11], dtype='m8[h]')),
+ # NumPy scalars
+ (np.datetime64('2012-12-21', '[D]'),
+ np.datetime64('2012-12-24', '[D]'),
+ np.datetime64('1940-12-24', '[D]'),
+ np.datetime64('NaT', '[D]'),
+ np.timedelta64(3, '[D]'),
+ np.timedelta64(11, '[h]'),
+ np.timedelta64(3*24 + 11, '[h]'))]:
+ # m8 + m8
+ assert_equal(tda + tdb, tdc)
+ assert_equal((tda + tdb).dtype, np.dtype('m8[h]'))
+ # m8 + bool
+ assert_equal(tdb + True, tdb + 1)
+ assert_equal((tdb + True).dtype, np.dtype('m8[h]'))
+ # m8 + int
+ assert_equal(tdb + 3*24, tdc)
+ assert_equal((tdb + 3*24).dtype, np.dtype('m8[h]'))
+ # bool + m8
+ assert_equal(False + tdb, tdb)
+ assert_equal((False + tdb).dtype, np.dtype('m8[h]'))
+ # int + m8
+ assert_equal(3*24 + tdb, tdc)
+ assert_equal((3*24 + tdb).dtype, np.dtype('m8[h]'))
+ # M8 + bool
+ assert_equal(dta + True, dta + 1)
+ assert_equal(dtnat + True, dtnat)
+ assert_equal((dta + True).dtype, np.dtype('M8[D]'))
+ # M8 + int
+ assert_equal(dta + 3, dtb)
+ assert_equal(dtnat + 3, dtnat)
+ assert_equal((dta + 3).dtype, np.dtype('M8[D]'))
+ # bool + M8
+ assert_equal(False + dta, dta)
+ assert_equal(False + dtnat, dtnat)
+ assert_equal((False + dta).dtype, np.dtype('M8[D]'))
+ # int + M8
+ assert_equal(3 + dta, dtb)
+ assert_equal(3 + dtnat, dtnat)
+ assert_equal((3 + dta).dtype, np.dtype('M8[D]'))
+ # M8 + m8
+ assert_equal(dta + tda, dtb)
+ assert_equal(dtnat + tda, dtnat)
+ assert_equal((dta + tda).dtype, np.dtype('M8[D]'))
+ # m8 + M8
+ assert_equal(tda + dta, dtb)
+ assert_equal(tda + dtnat, dtnat)
+ assert_equal((tda + dta).dtype, np.dtype('M8[D]'))
+
+ # In M8 + m8, the M8 controls the result type
+ assert_equal(dta + tdb, dta)
+ assert_equal((dta + tdb).dtype, np.dtype('M8[D]'))
+ assert_equal(dtc + tdb, dtc)
+ assert_equal((dtc + tdb).dtype, np.dtype('M8[D]'))
+ assert_equal(tdb + dta, dta)
+ assert_equal((tdb + dta).dtype, np.dtype('M8[D]'))
+ assert_equal(tdb + dtc, dtc)
+ assert_equal((tdb + dtc).dtype, np.dtype('M8[D]'))
+
+ # M8 + M8
+ assert_raises(TypeError, np.add, dta, dtb)
+
+ def test_datetime_subtract(self):
+ for dta, dtb, dtc, dtd, dtnat, tda, tdb, tdc in \
+ [
+ # One-dimensional arrays
+ (np.array(['2012-12-21'], dtype='M8[D]'),
+ np.array(['2012-12-24'], dtype='M8[D]'),
+ np.array(['1940-12-24'], dtype='M8[D]'),
+ np.array(['1940-12-24'], dtype='M8[h]'),
+ np.array(['NaT'], dtype='M8[D]'),
+ np.array([3], dtype='m8[D]'),
+ np.array([11], dtype='m8[h]'),
+ np.array([3*24 - 11], dtype='m8[h]')),
+ # NumPy scalars
+ (np.datetime64('2012-12-21', '[D]'),
+ np.datetime64('2012-12-24', '[D]'),
+ np.datetime64('1940-12-24', '[D]'),
+ np.datetime64('1940-12-24', '[h]'),
+ np.datetime64('NaT', '[D]'),
+ np.timedelta64(3, '[D]'),
+ np.timedelta64(11, '[h]'),
+ np.timedelta64(3*24 - 11, '[h]'))]:
+ # m8 - m8
+ assert_equal(tda - tdb, tdc)
+ assert_equal((tda - tdb).dtype, np.dtype('m8[h]'))
+ assert_equal(tdb - tda, -tdc)
+ assert_equal((tdb - tda).dtype, np.dtype('m8[h]'))
+ # m8 - bool
+ assert_equal(tdc - True, tdc - 1)
+ assert_equal((tdc - True).dtype, np.dtype('m8[h]'))
+ # m8 - int
+ assert_equal(tdc - 3*24, -tdb)
+ assert_equal((tdc - 3*24).dtype, np.dtype('m8[h]'))
+ # int - m8
+ assert_equal(False - tdb, -tdb)
+ assert_equal((False - tdb).dtype, np.dtype('m8[h]'))
+ # int - m8
+ assert_equal(3*24 - tdb, tdc)
+ assert_equal((3*24 - tdb).dtype, np.dtype('m8[h]'))
+ # M8 - bool
+ assert_equal(dtb - True, dtb - 1)
+ assert_equal(dtnat - True, dtnat)
+ assert_equal((dtb - True).dtype, np.dtype('M8[D]'))
+ # M8 - int
+ assert_equal(dtb - 3, dta)
+ assert_equal(dtnat - 3, dtnat)
+ assert_equal((dtb - 3).dtype, np.dtype('M8[D]'))
+ # M8 - m8
+ assert_equal(dtb - tda, dta)
+ assert_equal(dtnat - tda, dtnat)
+ assert_equal((dtb - tda).dtype, np.dtype('M8[D]'))
+
+ # In M8 - m8, the M8 controls the result type
+ assert_equal(dta - tdb, dta)
+ assert_equal((dta - tdb).dtype, np.dtype('M8[D]'))
+ assert_equal(dtc - tdb, dtc)
+ assert_equal((dtc - tdb).dtype, np.dtype('M8[D]'))
+
+ # M8 - M8 with different metadata
+ assert_raises(TypeError, np.subtract, dtc, dtd)
+ # m8 - M8
+ assert_raises(TypeError, np.subtract, tda, dta)
+ # bool - M8
+ assert_raises(TypeError, np.subtract, False, dta)
+ # int - M8
+ assert_raises(TypeError, np.subtract, 3, dta)
+
+ def test_datetime_multiply(self):
+ for dta, tda, tdb, tdc in \
+ [
+ # One-dimensional arrays
+ (np.array(['2012-12-21'], dtype='M8[D]'),
+ np.array([6], dtype='m8[h]'),
+ np.array([9], dtype='m8[h]'),
+ np.array([12], dtype='m8[h]')),
+ # NumPy scalars
+ (np.datetime64('2012-12-21', '[D]'),
+ np.timedelta64(6, '[h]'),
+ np.timedelta64(9, '[h]'),
+ np.timedelta64(12, '[h]'))]:
+ # m8 * int
+ assert_equal(tda * 2, tdc)
+ assert_equal((tda * 2).dtype, np.dtype('m8[h]'))
+ # int * m8
+ assert_equal(2 * tda, tdc)
+ assert_equal((2 * tda).dtype, np.dtype('m8[h]'))
+ # m8 * float
+ assert_equal(tda * 1.5, tdb)
+ assert_equal((tda * 1.5).dtype, np.dtype('m8[h]'))
+ # float * m8
+ assert_equal(1.5 * tda, tdb)
+ assert_equal((1.5 * tda).dtype, np.dtype('m8[h]'))
+
+ # m8 * m8
+ assert_raises(TypeError, np.multiply, tda, tdb)
+ # m8 * M8
+ assert_raises(TypeError, np.multiply, dta, tda)
+ # M8 * m8
+ assert_raises(TypeError, np.multiply, tda, dta)
+ # M8 * int
+ assert_raises(TypeError, np.multiply, dta, 2)
+ # int * M8
+ assert_raises(TypeError, np.multiply, 2, dta)
+ # M8 * float
+ assert_raises(TypeError, np.multiply, dta, 1.5)
+ # float * M8
+ assert_raises(TypeError, np.multiply, 1.5, dta)
+
+ def test_datetime_divide(self):
+ for dta, tda, tdb, tdc in \
+ [
+ # One-dimensional arrays
+ (np.array(['2012-12-21'], dtype='M8[D]'),
+ np.array([6], dtype='m8[h]'),
+ np.array([9], dtype='m8[h]'),
+ np.array([12], dtype='m8[h]')),
+ # NumPy scalars
+ (np.datetime64('2012-12-21', '[D]'),
+ np.timedelta64(6, '[h]'),
+ np.timedelta64(9, '[h]'),
+ np.timedelta64(12, '[h]'))]:
+ # m8 / int
+ assert_equal(tdc / 2, tda)
+ assert_equal((tdc / 2).dtype, np.dtype('m8[h]'))
+ # m8 / float
+ assert_equal(tda / 0.5, tdc)
+ assert_equal((tda / 0.5).dtype, np.dtype('m8[h]'))
+
+ # int / m8
+ assert_raises(TypeError, np.divide, 2, tdb)
+ # float / m8
+ assert_raises(TypeError, np.divide, 0.5, tdb)
+ # m8 / m8
+ assert_raises(TypeError, np.divide, tda, tdb)
+ # m8 / M8
+ assert_raises(TypeError, np.divide, dta, tda)
+ # M8 / m8
+ assert_raises(TypeError, np.divide, tda, dta)
+ # M8 / int
+ assert_raises(TypeError, np.divide, dta, 2)
+ # int / M8
+ assert_raises(TypeError, np.divide, 2, dta)
+ # M8 / float
+ assert_raises(TypeError, np.divide, dta, 1.5)
+ # float / M8
+ assert_raises(TypeError, np.divide, 1.5, dta)
+
+ def test_datetime_minmax(self):
+ # The metadata of the result should become the GCD
+ # of the operand metadata
+ a = np.array('1999-03-12T13Z', dtype='M8[2m]')
+ b = np.array('1999-03-12T12Z', dtype='M8[s]')
+ assert_equal(np.minimum(a,b), b)
+ assert_equal(np.minimum(a,b).dtype, np.dtype('M8[s]'))
+ assert_equal(np.fmin(a,b), b)
+ assert_equal(np.fmin(a,b).dtype, np.dtype('M8[s]'))
+ assert_equal(np.maximum(a,b), a)
+ assert_equal(np.maximum(a,b).dtype, np.dtype('M8[s]'))
+ assert_equal(np.fmax(a,b), a)
+ assert_equal(np.fmax(a,b).dtype, np.dtype('M8[s]'))
+ # Viewed as integers, the comparison is opposite because
+ # of the units chosen
+ assert_equal(np.minimum(a.view('i8'),b.view('i8')), a.view('i8'))
+
+ # Interaction with NaT
+ a = np.array('1999-03-12T13Z', dtype='M8[2m]')
+ dtnat = np.array('NaT', dtype='M8[D]')
+ assert_equal(np.minimum(a,dtnat), a)
+ assert_equal(np.minimum(dtnat,a), a)
+ assert_equal(np.maximum(a,dtnat), a)
+ assert_equal(np.maximum(dtnat,a), a)
+
+ # Also do timedelta
+ a = np.array(3, dtype='m8[h]')
+ b = np.array(3*3600 - 3, dtype='m8[s]')
+ assert_equal(np.minimum(a,b), b)
+ assert_equal(np.minimum(a,b).dtype, np.dtype('m8[s]'))
+ assert_equal(np.fmin(a,b), b)
+ assert_equal(np.fmin(a,b).dtype, np.dtype('m8[s]'))
+ assert_equal(np.maximum(a,b), a)
+ assert_equal(np.maximum(a,b).dtype, np.dtype('m8[s]'))
+ assert_equal(np.fmax(a,b), a)
+ assert_equal(np.fmax(a,b).dtype, np.dtype('m8[s]'))
+ # Viewed as integers, the comparison is opposite because
+ # of the units chosen
+ assert_equal(np.minimum(a.view('i8'),b.view('i8')), a.view('i8'))
+
+ # should raise between datetime and timedelta
+ #
+ # TODO: Allowing unsafe casting by
+ # default in ufuncs strikes again... :(
+ a = np.array(3, dtype='m8[h]')
+ b = np.array('1999-03-12T12Z', dtype='M8[s]')
+ #assert_raises(TypeError, np.minimum, a, b)
+ #assert_raises(TypeError, np.maximum, a, b)
+ #assert_raises(TypeError, np.fmin, a, b)
+ #assert_raises(TypeError, np.fmax, a, b)
+ assert_raises(TypeError, np.minimum, a, b, casting='same_kind')
+ assert_raises(TypeError, np.maximum, a, b, casting='same_kind')
+ assert_raises(TypeError, np.fmin, a, b, casting='same_kind')
+ assert_raises(TypeError, np.fmax, a, b, casting='same_kind')
def test_hours(self):
t = np.ones(3, dtype='M8[s]')
@@ -63,8 +767,102 @@ class TestDateTime(TestCase):
def test_divisor_conversion_as(self):
self.assertRaises(ValueError, lambda : np.dtype('M8[as/10]'))
+ def test_string_parser_variants(self):
+ # Allow space instead of 'T' between date and time
+ assert_equal(np.array(['1980-02-29T01:02:03'], np.dtype('M8')),
+ np.array(['1980-02-29 01:02:03'], np.dtype('M8')))
+ # Allow negative years
+ assert_equal(np.array(['-1980-02-29T01:02:03'], np.dtype('M8')),
+ np.array(['-1980-02-29 01:02:03'], np.dtype('M8')))
+ # UTC specifier
+ assert_equal(np.array(['-1980-02-29T01:02:03Z'], np.dtype('M8')),
+ np.array(['-1980-02-29 01:02:03Z'], np.dtype('M8')))
+ # Time zone offset
+ assert_equal(np.array(['1980-02-29T02:02:03Z'], np.dtype('M8')),
+ np.array(['1980-02-29 00:32:03-0130'], np.dtype('M8')))
+ assert_equal(np.array(['1980-02-28T22:32:03Z'], np.dtype('M8')),
+ np.array(['1980-02-29 00:02:03+01:30'], np.dtype('M8')))
+ assert_equal(np.array(['1980-02-29T02:32:03.506Z'], np.dtype('M8')),
+ np.array(['1980-02-29 00:32:03.506-02'], np.dtype('M8')))
+ assert_equal(np.datetime64('1977-03-02T12:30-0230'),
+ np.datetime64('1977-03-02T15:00Z'))
+
+
+ def test_string_parser_error_check(self):
+ # Arbitrary bad string
+ assert_raises(ValueError, np.array, ['badvalue'], np.dtype('M8'))
+ # Character after year must be '-'
+ assert_raises(ValueError, np.array, ['1980X'], np.dtype('M8'))
+ # Cannot have trailing '-'
+ assert_raises(ValueError, np.array, ['1980-'], np.dtype('M8'))
+ # Month must be in range [1,12]
+ assert_raises(ValueError, np.array, ['1980-00'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-13'], np.dtype('M8'))
+ # Month must have two digits
+ assert_raises(ValueError, np.array, ['1980-1'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-1-02'], np.dtype('M8'))
+ # 'Mor' is not a valid month
+ assert_raises(ValueError, np.array, ['1980-Mor'], np.dtype('M8'))
+ # Cannot have trailing '-'
+ assert_raises(ValueError, np.array, ['1980-01-'], np.dtype('M8'))
+ # Day must be in range [1,len(month)]
+ assert_raises(ValueError, np.array, ['1980-01-0'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-01-00'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-01-32'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1979-02-29'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-30'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-03-32'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-04-31'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-05-32'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-06-31'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-07-32'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-08-32'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-09-31'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-10-32'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-11-31'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-12-32'], np.dtype('M8'))
+ # Cannot have trailing characters
+ assert_raises(ValueError, np.array, ['1980-02-03%'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03 q'], np.dtype('M8'))
+
+ # Hours must be in range [0, 23]
+ assert_raises(ValueError, np.array, ['1980-02-03 25'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03T25'], np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03 24:01'],
+ np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03T24:01'],
+ np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03 -1'], np.dtype('M8'))
+ # No trailing ':'
+ assert_raises(ValueError, np.array, ['1980-02-03 01:'], np.dtype('M8'))
+ # Minutes must be in range [0, 59]
+ assert_raises(ValueError, np.array, ['1980-02-03 01:-1'],
+ np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03 01:60'],
+ np.dtype('M8'))
+ # No trailing ':'
+ assert_raises(ValueError, np.array, ['1980-02-03 01:60:'],
+ np.dtype('M8'))
+ # Seconds must be in range [0, 59]
+ assert_raises(ValueError, np.array, ['1980-02-03 01:10:-1'],
+ np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03 01:01:60'],
+ np.dtype('M8'))
+ # Timezone offset must within a reasonable range
+ assert_raises(ValueError, np.array, ['1980-02-03 01:01:00+0661'],
+ np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03 01:01:00+2500'],
+ np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03 01:01:00-0070'],
+ np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03 01:01:00-3000'],
+ np.dtype('M8'))
+ assert_raises(ValueError, np.array, ['1980-02-03 01:01:00-25:00'],
+ np.dtype('M8'))
+
+
def test_creation_overflow(self):
- date = '1980-03-23 20:00:00'
+ date = '1980-03-23 20:00:00Z'
timesteps = np.array([date], dtype='datetime64[s]')[0].astype(np.int64)
for unit in ['ms', 'us', 'ns']:
timesteps *= 1000
@@ -75,5 +873,130 @@ class TestDateTime(TestCase):
assert_equal(x[0].astype(np.int64), 322689600000000000)
+ def test_datetime_as_string(self):
+ # Check all the units with default string conversion
+ date = '1959-10-13T12:34:56.789012345678901234Z'
+
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'Y')),
+ '1959')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'M')),
+ '1959-10')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'D')),
+ '1959-10-13')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'h')),
+ '1959-10-13T12Z')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'm')),
+ '1959-10-13T12:34Z')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 's')),
+ '1959-10-13T12:34:56Z')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'ms')),
+ '1959-10-13T12:34:56.789Z')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'us')),
+ '1959-10-13T12:34:56.789012Z')
+
+ date = '1969-12-31T23:34:56.789012345678901234Z'
+
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'ns')),
+ '1969-12-31T23:34:56.789012345Z')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'ps')),
+ '1969-12-31T23:34:56.789012345678Z')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'fs')),
+ '1969-12-31T23:34:56.789012345678901Z')
+
+ date = '1969-12-31T23:59:57.789012345678901234Z'
+
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'as')),
+ date);
+ date = '1970-01-01T00:34:56.789012345678901234Z'
+
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'ns')),
+ '1970-01-01T00:34:56.789012345Z')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'ps')),
+ '1970-01-01T00:34:56.789012345678Z')
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'fs')),
+ '1970-01-01T00:34:56.789012345678901Z')
+
+ date = '1970-01-01T00:00:05.789012345678901234Z'
+
+ assert_equal(np.datetime_as_string(np.datetime64(date, 'as')),
+ date);
+
+ # String conversion with the unit= parameter
+ a = np.datetime64('2032-07-18T12:23:34.123456Z', 'us')
+ assert_equal(np.datetime_as_string(a, unit='Y'), '2032')
+ assert_equal(np.datetime_as_string(a, unit='M'), '2032-07')
+ assert_equal(np.datetime_as_string(a, unit='W'), '2032-07-18')
+ assert_equal(np.datetime_as_string(a, unit='D'), '2032-07-18')
+ assert_equal(np.datetime_as_string(a, unit='h'), '2032-07-18T12Z')
+ assert_equal(np.datetime_as_string(a, unit='m'),
+ '2032-07-18T12:23Z')
+ assert_equal(np.datetime_as_string(a, unit='s'),
+ '2032-07-18T12:23:34Z')
+ assert_equal(np.datetime_as_string(a, unit='ms'),
+ '2032-07-18T12:23:34.123Z')
+ assert_equal(np.datetime_as_string(a, unit='us'),
+ '2032-07-18T12:23:34.123456Z')
+ assert_equal(np.datetime_as_string(a, unit='ns'),
+ '2032-07-18T12:23:34.123456000Z')
+ assert_equal(np.datetime_as_string(a, unit='ps'),
+ '2032-07-18T12:23:34.123456000000Z')
+ assert_equal(np.datetime_as_string(a, unit='fs'),
+ '2032-07-18T12:23:34.123456000000000Z')
+ assert_equal(np.datetime_as_string(a, unit='as'),
+ '2032-07-18T12:23:34.123456000000000000Z')
+
+ # unit='auto' parameter
+ assert_equal(np.datetime_as_string(
+ np.datetime64('2032-07-18T12:23:34.123456Z', 'us'),
+ unit='auto'),
+ '2032-07-18T12:23:34.123456Z')
+ assert_equal(np.datetime_as_string(
+ np.datetime64('2032-07-18T12:23:34.12Z', 'us'),
+ unit='auto'),
+ '2032-07-18T12:23:34.120Z')
+ assert_equal(np.datetime_as_string(
+ np.datetime64('2032-07-18T12:23:34Z', 'us'),
+ unit='auto'),
+ '2032-07-18T12:23:34Z')
+ assert_equal(np.datetime_as_string(
+ np.datetime64('2032-07-18T12:23:00Z', 'us'),
+ unit='auto'),
+ '2032-07-18T12:23Z')
+ # 'auto' doesn't split up hour and minute
+ assert_equal(np.datetime_as_string(
+ np.datetime64('2032-07-18T12:00:00Z', 'us'),
+ unit='auto'),
+ '2032-07-18T12:00Z')
+ assert_equal(np.datetime_as_string(
+ np.datetime64('2032-07-18T00:00:00Z', 'us'),
+ unit='auto'),
+ '2032-07-18')
+ # 'auto' doesn't split up the date
+ assert_equal(np.datetime_as_string(
+ np.datetime64('2032-07-01T00:00:00Z', 'us'),
+ unit='auto'),
+ '2032-07-01')
+ assert_equal(np.datetime_as_string(
+ np.datetime64('2032-01-01T00:00:00Z', 'us'),
+ unit='auto'),
+ '2032-01-01')
+
+ # local=True
+ a = np.datetime64('2010-03-15T06:30Z', 'm')
+ assert_(np.datetime_as_string(a, local=True) != '2010-03-15T6:30Z')
+ # local=True with tzoffset
+ assert_equal(np.datetime_as_string(a, local=True, tzoffset=-60),
+ '2010-03-15T05:30-0100')
+ assert_equal(np.datetime_as_string(a, local=True, tzoffset=+30),
+ '2010-03-15T07:00+0030')
+ assert_equal(np.datetime_as_string(a, local=True, tzoffset=-5*60),
+ '2010-03-15T01:30-0500')
+
+class TestDateTimeData(TestCase):
+
+ def test_basic(self):
+ a = np.array(['1980-03-23'], dtype=np.datetime64)
+ assert_equal(np.datetime_data(a.dtype), (asbytes('us'), 1, 1))
+
if __name__ == "__main__":
run_module_suite()
diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py
index 40c3a3eea..c79b755be 100644
--- a/numpy/core/tests/test_dtype.py
+++ b/numpy/core/tests/test_dtype.py
@@ -33,6 +33,14 @@ class TestBuiltin(TestCase):
self.assertTrue(dt.byteorder != dt3.byteorder, "bogus test")
assert_dtype_equal(dt, dt3)
+ def test_invalid_types(self):
+ # Make sure invalid type strings raise exceptions
+ for typestr in ['O3', 'O5', 'O7', 'b3', 'h4', 'I5', 'l4', 'l8',
+ 'L4', 'L8', 'q8', 'q16', 'Q8', 'Q16', 'e3',
+ 'f5', 'd8', 't8', 'g12', 'g16']:
+ #print typestr
+ assert_raises(TypeError, np.dtype, typestr)
+
class TestRecord(TestCase):
def test_equivalent_record(self):
"""Test whether equivalent record dtypes hash the same."""
diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py
index d574c42f2..764bf9a41 100644
--- a/numpy/core/tests/test_regression.py
+++ b/numpy/core/tests/test_regression.py
@@ -1317,7 +1317,7 @@ class TestRegression(TestCase):
def test_ticket_1539(self):
dtypes = [x for x in np.typeDict.values()
if (issubclass(x, np.number)
- and not issubclass(x, np.timeinteger))]
+ and not issubclass(x, np.timedelta_))]
a = np.array([], dtypes[0])
failures = []
for x in dtypes:
diff --git a/numpy/lib/tests/test_type_check.py b/numpy/lib/tests/test_type_check.py
index c8bc87c6e..0f8927614 100644
--- a/numpy/lib/tests/test_type_check.py
+++ b/numpy/lib/tests/test_type_check.py
@@ -382,13 +382,5 @@ class TestArrayConversion(TestCase):
assert_equal(a.__class__,ndarray)
assert_(issubdtype(a.dtype,float))
-class TestDateTimeData(object):
-
- @dec.skipif(not _HAS_CTYPE, "ctypes not available on this python installation")
- def test_basic(self):
- a = array(['1980-03-23'], dtype=datetime64)
- assert_equal(datetime_data(a.dtype), (asbytes('us'), 1, 1, 1))
-
-
if __name__ == "__main__":
run_module_suite()
diff --git a/numpy/lib/type_check.py b/numpy/lib/type_check.py
index 0ce851fe4..edea02b62 100644
--- a/numpy/lib/type_check.py
+++ b/numpy/lib/type_check.py
@@ -3,7 +3,7 @@
__all__ = ['iscomplexobj','isrealobj','imag','iscomplex',
'isreal','nan_to_num','real','real_if_close',
'typename','asfarray','mintypecode','asscalar',
- 'common_type', 'datetime_data']
+ 'common_type']
import numpy.core.numeric as _nx
from numpy.core.numeric import asarray, asanyarray, array, isnan, \
@@ -601,47 +601,3 @@ def common_type(*arrays):
else:
return array_type[0][precision]
-def datetime_data(dtype):
- """Return (unit, numerator, denominator, events) from a datetime dtype
- """
- try:
- import ctypes
- except ImportError:
- raise RuntimeError("Cannot access date-time internals without ctypes installed")
-
- if dtype.kind not in ['m','M']:
- raise ValueError("Not a date-time dtype")
-
- obj = dtype.metadata[METADATA_DTSTR]
- class DATETIMEMETA(ctypes.Structure):
- _fields_ = [('base', ctypes.c_int),
- ('num', ctypes.c_int),
- ('den', ctypes.c_int),
- ('events', ctypes.c_int)]
-
- import sys
- if sys.version_info[:2] >= (3, 0):
- func = ctypes.pythonapi.PyCapsule_GetPointer
- func.argtypes = [ctypes.py_object, ctypes.c_char_p]
- func.restype = ctypes.c_void_p
- result = func(ctypes.py_object(obj), ctypes.c_char_p(None))
- else:
- func = ctypes.pythonapi.PyCObject_AsVoidPtr
- func.argtypes = [ctypes.py_object]
- func.restype = ctypes.c_void_p
- result = func(ctypes.py_object(obj))
- result = ctypes.cast(ctypes.c_void_p(result), ctypes.POINTER(DATETIMEMETA))
-
- struct = result[0]
- base = struct.base
-
- # FIXME: This needs to be kept consistent with enum in ndarrayobject.h
- from numpy.core.multiarray import DATETIMEUNITS
- obj = ctypes.py_object(DATETIMEUNITS)
- if sys.version_info[:2] >= (2,7):
- result = func(obj, ctypes.c_char_p(None))
- else:
- result = func(obj)
- _unitnum2name = ctypes.cast(ctypes.c_void_p(result), ctypes.POINTER(ctypes.c_char_p))
-
- return (_unitnum2name[base], struct.num, struct.den, struct.events)
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
index bbd855cf8..2cb888d55 100644
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -3527,10 +3527,10 @@ class MaskedArray(ndarray):
# convert to object array to make filled work
names = self.dtype.names
if names is None:
- res = self._data.astype("|O8")
+ res = self._data.astype("O")
res[m] = f
else:
- rdtype = _recursive_make_descr(self.dtype, "|O8")
+ rdtype = _recursive_make_descr(self.dtype, "O")
res = self._data.astype(rdtype)
_recursive_printoption(res, m, f)
else: