diff --git a/pandas/period.pyx b/pandas/period.pyx index 2913235c32017..946736a3fe82c 100644 --- a/pandas/period.pyx +++ b/pandas/period.pyx @@ -1,30 +1,616 @@ from datetime import datetime, date, timedelta import operator + +from numpy cimport (int8_t, int32_t, int64_t, import_array, ndarray, + NPY_INT64, NPY_DATETIME, NPY_TIMEDELTA) import numpy as np cdef extern from "datetime_helper.h": double total_seconds(object) +from libc.stdlib cimport free + from pandas import compat -from pandas.tseries import frequencies -from pandas.tseries.frequencies import get_freq_code as _gfc from pandas.tseries import offsets from pandas.tseries.tools import parse_time_string +from datetime cimport * +cimport util cimport lib import lib from pandas import tslib -from tslib import Timedelta, Timestamp - +from tslib import Timedelta, Timestamp, iNaT, NaT +from tslib import have_pytz, _get_utcoffset +from tslib cimport ( + maybe_get_tz, + _is_utc, + _is_tzlocal, + _get_dst_info, +) + +from sys import version_info + +cdef bint PY2 = version_info[0] == 2 + +cdef int64_t NPY_NAT = util.get_nat() + + +cdef extern from "period.h": + ctypedef struct date_info: + int64_t absdate + double abstime + double second + int minute + int hour + int day + int month + int quarter + int year + int day_of_week + int day_of_year + int calendar + + ctypedef struct asfreq_info: + int from_week_end + int to_week_end + + int from_a_year_end + int to_a_year_end + + int from_q_year_end + int to_q_year_end + + ctypedef int64_t (*freq_conv_func)(int64_t, char, asfreq_info*) + + void initialize_daytime_conversion_factor_matrix() + int64_t asfreq(int64_t dtordinal, int freq1, int freq2, char relation) except INT32_MIN + freq_conv_func get_asfreq_func(int fromFreq, int toFreq) + void get_asfreq_info(int fromFreq, int toFreq, asfreq_info *af_info) + + int64_t get_period_ordinal(int year, int month, int day, + int hour, int minute, int second, int microseconds, int picoseconds, + int freq) except INT32_MIN + + int64_t get_python_ordinal(int64_t period_ordinal, int freq) except INT32_MIN + + int get_date_info(int64_t ordinal, int freq, date_info *dinfo) except INT32_MIN + double getAbsTime(int, int64_t, int64_t) + + int pyear(int64_t ordinal, int freq) except INT32_MIN + int pqyear(int64_t ordinal, int freq) except INT32_MIN + int pquarter(int64_t ordinal, int freq) except INT32_MIN + int pmonth(int64_t ordinal, int freq) except INT32_MIN + int pday(int64_t ordinal, int freq) except INT32_MIN + int pweekday(int64_t ordinal, int freq) except INT32_MIN + int pday_of_week(int64_t ordinal, int freq) except INT32_MIN + int pday_of_year(int64_t ordinal, int freq) except INT32_MIN + int pweek(int64_t ordinal, int freq) except INT32_MIN + int phour(int64_t ordinal, int freq) except INT32_MIN + int pminute(int64_t ordinal, int freq) except INT32_MIN + int psecond(int64_t ordinal, int freq) except INT32_MIN + char *c_strftime(date_info *dinfo, char *fmt) + int get_yq(int64_t ordinal, int freq, int *quarter, int *year) + +initialize_daytime_conversion_factor_matrix() -#--------------- # Period logic +#---------------------------------------------------------------------- + +cdef inline int64_t apply_mult(int64_t period_ord, int64_t mult): + """ + Get freq+multiple ordinal value from corresponding freq-only ordinal value. + For example, 5min ordinal will be 1/5th the 1min ordinal (rounding down to + integer). + """ + if mult == 1: + return period_ord + + return (period_ord - 1) // mult + +cdef inline int64_t remove_mult(int64_t period_ord_w_mult, int64_t mult): + """ + Get freq-only ordinal value from corresponding freq+multiple ordinal. + """ + if mult == 1: + return period_ord_w_mult + + return period_ord_w_mult * mult + 1; + +def dt64arr_to_periodarr(ndarray[int64_t] dtarr, int freq, tz=None): + """ + Convert array of datetime64 values (passed in as 'i8' dtype) to a set of + periods corresponding to desired frequency, per period convention. + """ + cdef: + ndarray[int64_t] out + Py_ssize_t i, l + pandas_datetimestruct dts + + l = len(dtarr) + + out = np.empty(l, dtype='i8') + + if tz is None: + for i in range(l): + if dtarr[i] == iNaT: + out[i] = iNaT + continue + pandas_datetime_to_datetimestruct(dtarr[i], PANDAS_FR_ns, &dts) + out[i] = get_period_ordinal(dts.year, dts.month, dts.day, + dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) + else: + out = localize_dt64arr_to_period(dtarr, freq, tz) + return out + +def periodarr_to_dt64arr(ndarray[int64_t] periodarr, int freq): + """ + Convert array to datetime64 values from a set of ordinals corresponding to + periods per period convention. + """ + cdef: + ndarray[int64_t] out + Py_ssize_t i, l + + l = len(periodarr) + + out = np.empty(l, dtype='i8') + + for i in range(l): + if periodarr[i] == iNaT: + out[i] = iNaT + continue + out[i] = period_ordinal_to_dt64(periodarr[i], freq) + + return out + +cdef char START = 'S' +cdef char END = 'E' + +cpdef int64_t period_asfreq(int64_t period_ordinal, int freq1, int freq2, + bint end): + """ + Convert period ordinal from one frequency to another, and if upsampling, + choose to use start ('S') or end ('E') of period. + """ + cdef: + int64_t retval + + if period_ordinal == iNaT: + return iNaT + + if end: + retval = asfreq(period_ordinal, freq1, freq2, END) + else: + retval = asfreq(period_ordinal, freq1, freq2, START) + + if retval == INT32_MIN: + raise ValueError('Frequency conversion failed') + + return retval + +def period_asfreq_arr(ndarray[int64_t] arr, int freq1, int freq2, bint end): + """ + Convert int64-array of period ordinals from one frequency to another, and + if upsampling, choose to use start ('S') or end ('E') of period. + """ + cdef: + ndarray[int64_t] result + Py_ssize_t i, n + freq_conv_func func + asfreq_info finfo + int64_t val, ordinal + char relation + + n = len(arr) + result = np.empty(n, dtype=np.int64) + + func = get_asfreq_func(freq1, freq2) + get_asfreq_info(freq1, freq2, &finfo) + + if end: + relation = END + else: + relation = START + + mask = arr == iNaT + if mask.any(): # NaT process + for i in range(n): + val = arr[i] + if val != iNaT: + val = func(val, relation, &finfo) + if val == INT32_MIN: + raise ValueError("Unable to convert to desired frequency.") + result[i] = val + else: + for i in range(n): + val = func(arr[i], relation, &finfo) + if val == INT32_MIN: + raise ValueError("Unable to convert to desired frequency.") + result[i] = val + + return result + +def period_ordinal(int y, int m, int d, int h, int min, int s, int us, int ps, int freq): + cdef: + int64_t ordinal + + return get_period_ordinal(y, m, d, h, min, s, us, ps, freq) + + +cpdef int64_t period_ordinal_to_dt64(int64_t ordinal, int freq): + cdef: + pandas_datetimestruct dts + date_info dinfo + float subsecond_fraction + + if ordinal == iNaT: + return NPY_NAT + + get_date_info(ordinal, freq, &dinfo) + + dts.year = dinfo.year + dts.month = dinfo.month + dts.day = dinfo.day + dts.hour = dinfo.hour + dts.min = dinfo.minute + dts.sec = int(dinfo.second) + subsecond_fraction = dinfo.second - dts.sec + dts.us = int((subsecond_fraction) * 1e6) + dts.ps = int(((subsecond_fraction) * 1e6 - dts.us) * 1e6) + + return pandas_datetimestruct_to_datetime(PANDAS_FR_ns, &dts) + +def period_format(int64_t value, int freq, object fmt=None): + cdef: + int freq_group + + if value == iNaT: + return repr(NaT) + + if fmt is None: + freq_group = (freq // 1000) * 1000 + if freq_group == 1000: # FR_ANN + fmt = b'%Y' + elif freq_group == 2000: # FR_QTR + fmt = b'%FQ%q' + elif freq_group == 3000: # FR_MTH + fmt = b'%Y-%m' + elif freq_group == 4000: # WK + left = period_asfreq(value, freq, 6000, 0) + right = period_asfreq(value, freq, 6000, 1) + return '%s/%s' % (period_format(left, 6000), + period_format(right, 6000)) + elif (freq_group == 5000 # BUS + or freq_group == 6000): # DAY + fmt = b'%Y-%m-%d' + elif freq_group == 7000: # HR + fmt = b'%Y-%m-%d %H:00' + elif freq_group == 8000: # MIN + fmt = b'%Y-%m-%d %H:%M' + elif freq_group == 9000: # SEC + fmt = b'%Y-%m-%d %H:%M:%S' + elif freq_group == 10000: # MILLISEC + fmt = b'%Y-%m-%d %H:%M:%S.%l' + elif freq_group == 11000: # MICROSEC + fmt = b'%Y-%m-%d %H:%M:%S.%u' + elif freq_group == 12000: # NANOSEC + fmt = b'%Y-%m-%d %H:%M:%S.%n' + else: + raise ValueError('Unknown freq: %d' % freq) + + return _period_strftime(value, freq, fmt) + + +cdef list extra_fmts = [(b"%q", b"^`AB`^"), + (b"%f", b"^`CD`^"), + (b"%F", b"^`EF`^"), + (b"%l", b"^`GH`^"), + (b"%u", b"^`IJ`^"), + (b"%n", b"^`KL`^")] + +cdef list str_extra_fmts = ["^`AB`^", "^`CD`^", "^`EF`^", "^`GH`^", "^`IJ`^", "^`KL`^"] + +cdef object _period_strftime(int64_t value, int freq, object fmt): + import sys + + cdef: + Py_ssize_t i + date_info dinfo + char *formatted + object pat, repl, result + list found_pat = [False] * len(extra_fmts) + int year, quarter + + if PyUnicode_Check(fmt): + fmt = fmt.encode('utf-8') + + get_date_info(value, freq, &dinfo) + for i in range(len(extra_fmts)): + pat = extra_fmts[i][0] + repl = extra_fmts[i][1] + if pat in fmt: + fmt = fmt.replace(pat, repl) + found_pat[i] = True + + formatted = c_strftime(&dinfo, fmt) + + result = util.char_to_string(formatted) + free(formatted) + + for i in range(len(extra_fmts)): + if found_pat[i]: + if get_yq(value, freq, &quarter, &year) < 0: + raise ValueError('Unable to get quarter and year') + + if i == 0: + repl = '%d' % quarter + elif i == 1: # %f, 2-digit year + repl = '%.2d' % (year % 100) + elif i == 2: + repl = '%d' % year + elif i == 3: + repl = '%03d' % (value % 1000) + elif i == 4: + repl = '%06d' % (value % 1000000) + elif i == 5: + repl = '%09d' % (value % 1000000000) + + result = result.replace(str_extra_fmts[i], repl) + + if PY2: + result = result.decode('utf-8', 'ignore') + + return result + +# period accessors + +ctypedef int (*accessor)(int64_t ordinal, int freq) except INT32_MIN + +def get_period_field(int code, int64_t value, int freq): + cdef accessor f = _get_accessor_func(code) + if f is NULL: + raise ValueError('Unrecognized period code: %d' % code) + if value == iNaT: + return np.nan + return f(value, freq) + +def get_period_field_arr(int code, ndarray[int64_t] arr, int freq): + cdef: + Py_ssize_t i, sz + ndarray[int64_t] out + accessor f + + f = _get_accessor_func(code) + if f is NULL: + raise ValueError('Unrecognized period code: %d' % code) + + sz = len(arr) + out = np.empty(sz, dtype=np.int64) + + for i in range(sz): + if arr[i] == iNaT: + out[i] = -1 + continue + out[i] = f(arr[i], freq) + + return out + + + +cdef accessor _get_accessor_func(int code): + if code == 0: + return &pyear + elif code == 1: + return &pqyear + elif code == 2: + return &pquarter + elif code == 3: + return &pmonth + elif code == 4: + return &pday + elif code == 5: + return &phour + elif code == 6: + return &pminute + elif code == 7: + return &psecond + elif code == 8: + return &pweek + elif code == 9: + return &pday_of_year + elif code == 10: + return &pweekday + return NULL + + +def extract_ordinals(ndarray[object] values, freq): + cdef: + Py_ssize_t i, n = len(values) + ndarray[int64_t] ordinals = np.empty(n, dtype=np.int64) + object p + + for i in range(n): + p = values[i] + ordinals[i] = p.ordinal + if p.freq != freq: + raise ValueError("%s is wrong freq" % p) + + return ordinals + +cpdef resolution(ndarray[int64_t] stamps, tz=None): + cdef: + Py_ssize_t i, n = len(stamps) + pandas_datetimestruct dts + int reso = D_RESO, curr_reso + + if tz is not None: + tz = maybe_get_tz(tz) + return _reso_local(stamps, tz) + else: + for i in range(n): + if stamps[i] == NPY_NAT: + continue + pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, &dts) + curr_reso = _reso_stamp(&dts) + if curr_reso < reso: + reso = curr_reso + return reso + +US_RESO = 0 +MS_RESO = 1 +S_RESO = 2 +T_RESO = 3 +H_RESO = 4 +D_RESO = 5 + +cdef inline int _reso_stamp(pandas_datetimestruct *dts): + if dts.us != 0: + if dts.us % 1000 == 0: + return MS_RESO + return US_RESO + elif dts.sec != 0: + return S_RESO + elif dts.min != 0: + return T_RESO + elif dts.hour != 0: + return H_RESO + return D_RESO + +cdef _reso_local(ndarray[int64_t] stamps, object tz): + cdef: + Py_ssize_t n = len(stamps) + int reso = D_RESO, curr_reso + ndarray[int64_t] trans, deltas, pos + pandas_datetimestruct dts + + if _is_utc(tz): + for i in range(n): + if stamps[i] == NPY_NAT: + continue + pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, &dts) + curr_reso = _reso_stamp(&dts) + if curr_reso < reso: + reso = curr_reso + elif _is_tzlocal(tz): + for i in range(n): + if stamps[i] == NPY_NAT: + continue + pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, + &dts) + dt = datetime(dts.year, dts.month, dts.day, dts.hour, + dts.min, dts.sec, dts.us, tz) + delta = int(total_seconds(_get_utcoffset(tz, dt))) * 1000000000 + pandas_datetime_to_datetimestruct(stamps[i] + delta, + PANDAS_FR_ns, &dts) + curr_reso = _reso_stamp(&dts) + if curr_reso < reso: + reso = curr_reso + else: + # Adjust datetime64 timestamp, recompute datetimestruct + trans, deltas, typ = _get_dst_info(tz) + + _pos = trans.searchsorted(stamps, side='right') - 1 + if _pos.dtype != np.int64: + _pos = _pos.astype(np.int64) + pos = _pos + + # statictzinfo + if typ not in ['pytz','dateutil']: + for i in range(n): + if stamps[i] == NPY_NAT: + continue + pandas_datetime_to_datetimestruct(stamps[i] + deltas[0], + PANDAS_FR_ns, &dts) + curr_reso = _reso_stamp(&dts) + if curr_reso < reso: + reso = curr_reso + else: + for i in range(n): + if stamps[i] == NPY_NAT: + continue + pandas_datetime_to_datetimestruct(stamps[i] + deltas[pos[i]], + PANDAS_FR_ns, &dts) + curr_reso = _reso_stamp(&dts) + if curr_reso < reso: + reso = curr_reso + + return reso + + +# period helpers + +cdef ndarray[int64_t] localize_dt64arr_to_period(ndarray[int64_t] stamps, + int freq, object tz): + cdef: + Py_ssize_t n = len(stamps) + ndarray[int64_t] result = np.empty(n, dtype=np.int64) + ndarray[int64_t] trans, deltas, pos + pandas_datetimestruct dts + + if not have_pytz: + raise Exception('Could not find pytz module') + + if _is_utc(tz): + for i in range(n): + if stamps[i] == NPY_NAT: + result[i] = NPY_NAT + continue + pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, &dts) + result[i] = get_period_ordinal(dts.year, dts.month, dts.day, + dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) + + elif _is_tzlocal(tz): + for i in range(n): + if stamps[i] == NPY_NAT: + result[i] = NPY_NAT + continue + pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, + &dts) + dt = datetime(dts.year, dts.month, dts.day, dts.hour, + dts.min, dts.sec, dts.us, tz) + delta = int(total_seconds(_get_utcoffset(tz, dt))) * 1000000000 + pandas_datetime_to_datetimestruct(stamps[i] + delta, + PANDAS_FR_ns, &dts) + result[i] = get_period_ordinal(dts.year, dts.month, dts.day, + dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) + else: + # Adjust datetime64 timestamp, recompute datetimestruct + trans, deltas, typ = _get_dst_info(tz) + + _pos = trans.searchsorted(stamps, side='right') - 1 + if _pos.dtype != np.int64: + _pos = _pos.astype(np.int64) + pos = _pos + + # statictzinfo + if typ not in ['pytz','dateutil']: + for i in range(n): + if stamps[i] == NPY_NAT: + result[i] = NPY_NAT + continue + pandas_datetime_to_datetimestruct(stamps[i] + deltas[0], + PANDAS_FR_ns, &dts) + result[i] = get_period_ordinal(dts.year, dts.month, dts.day, + dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) + else: + for i in range(n): + if stamps[i] == NPY_NAT: + result[i] = NPY_NAT + continue + pandas_datetime_to_datetimestruct(stamps[i] + deltas[pos[i]], + PANDAS_FR_ns, &dts) + result[i] = get_period_ordinal(dts.year, dts.month, dts.day, + dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) + + return result + def _period_field_accessor(name, alias): def f(self): + from pandas.tseries.frequencies import get_freq_code as _gfc base, mult = _gfc(self.freq) - return tslib.get_period_field(alias, self.ordinal, base) + return get_period_field(alias, self.ordinal, base) f.__name__ = name return property(f) @@ -63,6 +649,8 @@ class Period(object): def __init__(self, value=None, freq=None, ordinal=None, year=None, month=1, quarter=None, day=1, hour=0, minute=0, second=0): + from pandas.tseries import frequencies + from pandas.tseries.frequencies import get_freq_code as _gfc # freq points to a tuple (base, mult); base is one of the defined # periods such as A, Q, etc. Every five minutes would be, e.g., @@ -135,7 +723,7 @@ class Period(object): raise ValueError('Only mult == 1 supported') if self.ordinal is None: - self.ordinal = tslib.period_ordinal(dt.year, dt.month, dt.day, + self.ordinal = period_ordinal(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, 0, base) @@ -143,6 +731,7 @@ class Period(object): def __eq__(self, other): if isinstance(other, Period): + from pandas.tseries.frequencies import get_freq_code as _gfc if other.freq != self.freq: raise ValueError("Cannot compare non-conforming periods") if self.ordinal == tslib.iNaT or other.ordinal == tslib.iNaT: @@ -158,6 +747,7 @@ class Period(object): return hash((self.ordinal, self.freq)) def _add_delta(self, other): + from pandas.tseries import frequencies if isinstance(other, (timedelta, np.timedelta64, offsets.Tick, Timedelta)): offset = frequencies.to_offset(self.freq) if isinstance(offset, offsets.Tick): @@ -251,6 +841,7 @@ class Period(object): ------- resampled : Period """ + from pandas.tseries.frequencies import get_freq_code as _gfc how = _validate_end_alias(how) base1, mult1 = _gfc(self.freq) base2, mult2 = _gfc(freq) @@ -259,7 +850,7 @@ class Period(object): raise ValueError('Only mult == 1 supported') end = how == 'E' - new_ordinal = tslib.period_asfreq(self.ordinal, base1, base2, end) + new_ordinal = period_asfreq(self.ordinal, base1, base2, end) return Period(ordinal=new_ordinal, freq=base2) @@ -293,6 +884,8 @@ class Period(object): ------- Timestamp """ + from pandas.tseries import frequencies + from pandas.tseries.frequencies import get_freq_code as _gfc how = _validate_end_alias(how) if freq is None: @@ -302,7 +895,7 @@ class Period(object): base, mult = _gfc(freq) val = self.asfreq(freq, how) - dt64 = tslib.period_ordinal_to_dt64(val.ordinal, base) + dt64 = period_ordinal_to_dt64(val.ordinal, base) return Timestamp(dt64, tz=tz) year = _period_field_accessor('year', 0) @@ -328,8 +921,10 @@ class Period(object): return self.__unicode__() def __repr__(self): + from pandas.tseries import frequencies + from pandas.tseries.frequencies import get_freq_code as _gfc base, mult = _gfc(self.freq) - formatted = tslib.period_format(self.ordinal, base) + formatted = period_format(self.ordinal, base) freqstr = frequencies._reverse_period_code_map[base] return "Period('%s', '%s')" % (formatted, freqstr) @@ -341,8 +936,9 @@ class Period(object): Invoked by unicode(df) in py2 only. Yields a Unicode String in both py2/py3. """ + from pandas.tseries.frequencies import get_freq_code as _gfc base, mult = _gfc(self.freq) - formatted = tslib.period_format(self.ordinal, base) + formatted = period_format(self.ordinal, base) value = ("%s" % formatted) return value @@ -483,12 +1079,14 @@ class Period(object): >>> a.strftime('%b. %d, %Y was a %A') 'Jan. 01, 2001 was a Monday' """ + from pandas.tseries.frequencies import get_freq_code as _gfc base, mult = _gfc(self.freq) - return tslib.period_format(self.ordinal, base, fmt) + return period_format(self.ordinal, base, fmt) def _ordinal_from_fields(year, month, quarter, day, hour, minute, second, freq): + from pandas.tseries.frequencies import get_freq_code as _gfc base, mult = _gfc(freq) if mult != 1: raise ValueError('Only mult == 1 supported') @@ -496,7 +1094,7 @@ def _ordinal_from_fields(year, month, quarter, day, hour, minute, if quarter is not None: year, month = _quarter_to_myear(year, quarter, freq) - return tslib.period_ordinal(year, month, day, hour, minute, second, 0, 0, base) + return period_ordinal(year, month, day, hour, minute, second, 0, 0, base) def _quarter_to_myear(year, quarter, freq): @@ -504,6 +1102,7 @@ def _quarter_to_myear(year, quarter, freq): if quarter <= 0 or quarter > 4: raise ValueError('Quarter must be 1 <= q <= 4') + from pandas.tseries import frequencies mnum = frequencies._month_numbers[frequencies._get_rule_month(freq)] + 1 month = (mnum + (quarter - 1) * 3) % 12 + 1 if month > mnum: diff --git a/pandas/tests/test_tseries.py b/pandas/tests/test_tseries.py index af68e7059f8ac..b8e5f23d825f0 100644 --- a/pandas/tests/test_tseries.py +++ b/pandas/tests/test_tseries.py @@ -7,6 +7,7 @@ import pandas.util.testing as tm from pandas.compat import range, lrange, zip import pandas.lib as lib +import pandas.period as period import pandas.algos as algos @@ -731,12 +732,10 @@ def test_to_datetime_bijective(self): class TestPeriodField(tm.TestCase): def test_get_period_field_raises_on_out_of_range(self): - from pandas import tslib - self.assertRaises(ValueError, tslib.get_period_field, -1, 0, 0) + self.assertRaises(ValueError, period.get_period_field, -1, 0, 0) def test_get_period_field_array_raises_on_out_of_range(self): - from pandas import tslib - self.assertRaises(ValueError, tslib.get_period_field_arr, -1, np.empty(1), 0) + self.assertRaises(ValueError, period.get_period_field_arr, -1, np.empty(1), 0) if __name__ == '__main__': import nose diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 0ec225d77f5e2..26c0d26482001 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -12,6 +12,7 @@ import pandas.core.common as com import pandas.lib as lib import pandas.tslib as tslib +import pandas.period as period from pandas.tslib import Timedelta class FreqGroup(object): @@ -31,12 +32,12 @@ class FreqGroup(object): class Resolution(object): - RESO_US = tslib.US_RESO - RESO_MS = tslib.MS_RESO - RESO_SEC = tslib.S_RESO - RESO_MIN = tslib.T_RESO - RESO_HR = tslib.H_RESO - RESO_DAY = tslib.D_RESO + RESO_US = period.US_RESO + RESO_MS = period.MS_RESO + RESO_SEC = period.S_RESO + RESO_MIN = period.T_RESO + RESO_HR = period.H_RESO + RESO_DAY = period.D_RESO _reso_str_map = { RESO_US: 'microsecond', diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index 34cbfe0a3abda..2de1a004879bc 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -28,6 +28,7 @@ from pandas.lib import Timestamp import pandas.lib as lib import pandas.tslib as tslib +import pandas.period as period import pandas.algos as _algos import pandas.index as _index @@ -1461,7 +1462,7 @@ def is_normalized(self): @cache_readonly def _resolution(self): - return tslib.resolution(self.asi8, self.tz) + return period.resolution(self.asi8, self.tz) def equals(self, other): """ diff --git a/pandas/tseries/period.py b/pandas/tseries/period.py index 8a0ac0488ec44..71f97926f8b3f 100644 --- a/pandas/tseries/period.py +++ b/pandas/tseries/period.py @@ -13,7 +13,9 @@ import pandas.tseries.offsets as offsets from pandas.period import Period +import pandas.period as period from pandas.period import ( + get_period_field_arr, _validate_end_alias, _quarter_to_myear, ) @@ -32,7 +34,7 @@ def _field_accessor(name, alias, docstring=None): def f(self): base, mult = _gfc(self.freq) - return tslib.get_period_field_arr(alias, self.values, base) + return get_period_field_arr(alias, self.values, base) f.__name__ = name f.__doc__ = docstring return property(f) @@ -41,7 +43,7 @@ def f(self): def _get_ordinals(data, freq): f = lambda x: Period(x, freq=freq).ordinal if isinstance(data[0], Period): - return tslib.extract_ordinals(data, freq) + return period.extract_ordinals(data, freq) else: return lib.map_infer(data, f) @@ -51,7 +53,7 @@ def dt64arr_to_periodarr(data, freq, tz): raise ValueError('Wrong dtype: %s' % data.dtype) base, mult = _gfc(freq) - return tslib.dt64arr_to_periodarr(data.view('i8'), base, tz) + return period.dt64arr_to_periodarr(data.view('i8'), base, tz) # --- Period index sketch @@ -236,7 +238,7 @@ def _from_arraylike(cls, data, freq, tz): else: base1, _ = _gfc(data.freq) base2, _ = _gfc(freq) - data = tslib.period_asfreq_arr(data.values, base1, + data = period.period_asfreq_arr(data.values, base1, base2, 1) else: if freq is None and len(data) > 0: @@ -363,7 +365,7 @@ def asfreq(self, freq=None, how='E'): raise ValueError('Only mult == 1 supported') end = how == 'E' - new_data = tslib.period_asfreq_arr(self.values, base1, base2, end) + new_data = period.period_asfreq_arr(self.values, base1, base2, end) return self._simple_new(new_data, self.name, freq=freq) def to_datetime(self, dayfirst=False): @@ -431,7 +433,7 @@ def to_timestamp(self, freq=None, how='start'): base, mult = _gfc(freq) new_data = self.asfreq(freq, how) - new_data = tslib.periodarr_to_dt64arr(new_data.values, base) + new_data = period.periodarr_to_dt64arr(new_data.values, base) return DatetimeIndex(new_data, freq='infer', name=self.name) def _add_delta(self, other): @@ -881,7 +883,7 @@ def _range_from_fields(year=None, month=None, quarter=None, day=None, year, quarter = _make_field_arrays(year, quarter) for y, q in zip(year, quarter): y, m = _quarter_to_myear(y, q, freq) - val = tslib.period_ordinal(y, m, 1, 1, 1, 1, 0, 0, base) + val = period.period_ordinal(y, m, 1, 1, 1, 1, 0, 0, base) ordinals.append(val) else: base, mult = _gfc(freq) @@ -890,7 +892,7 @@ def _range_from_fields(year=None, month=None, quarter=None, day=None, arrays = _make_field_arrays(year, month, day, hour, minute, second) for y, mth, d, h, mn, s in zip(*arrays): - ordinals.append(tslib.period_ordinal(y, mth, d, h, mn, s, 0, 0, base)) + ordinals.append(period.period_ordinal(y, mth, d, h, mn, s, 0, 0, base)) return np.array(ordinals, dtype=np.int64), freq diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py index 8140d289f8c8a..0197dd7eaf74b 100644 --- a/pandas/tseries/tests/test_tslib.py +++ b/pandas/tseries/tests/test_tslib.py @@ -3,10 +3,12 @@ import numpy as np from pandas import tslib +from pandas import period import datetime from pandas.core.api import Timestamp, Series, Timedelta -from pandas.tslib import period_asfreq, period_ordinal, get_timezone +from pandas.tslib import get_timezone +from pandas.period import period_asfreq, period_ordinal from pandas.tseries.index import date_range from pandas.tseries.frequencies import get_freq import pandas.tseries.offsets as offsets @@ -764,11 +766,11 @@ def test_addition_subtraction_preserve_frequency(self): def test_resolution(self): for freq, expected in zip(['A', 'Q', 'M', 'D', 'H', 'T', 'S', 'L', 'U'], - [tslib.D_RESO, tslib.D_RESO, tslib.D_RESO, tslib.D_RESO, - tslib.H_RESO, tslib.T_RESO,tslib.S_RESO, tslib.MS_RESO, tslib.US_RESO]): + [period.D_RESO, period.D_RESO, period.D_RESO, period.D_RESO, + period.H_RESO, period.T_RESO, period.S_RESO, period.MS_RESO, period.US_RESO]): for tz in [None, 'Asia/Tokyo', 'US/Eastern', 'dateutil/US/Eastern']: idx = date_range(start='2013-04-01', periods=30, freq=freq, tz=tz) - result = tslib.resolution(idx.asi8, idx.tz) + result = period.resolution(idx.asi8, idx.tz) self.assertEqual(result, expected) diff --git a/pandas/tslib.pxd b/pandas/tslib.pxd index 1452dbdca03ee..d8fc57fe85bfd 100644 --- a/pandas/tslib.pxd +++ b/pandas/tslib.pxd @@ -2,3 +2,7 @@ from numpy cimport ndarray, int64_t cdef convert_to_tsobject(object, object, object) cdef convert_to_timedelta64(object, object, object) +cpdef object maybe_get_tz(object) +cdef bint _is_utc(object) +cdef bint _is_tzlocal(object) +cdef object _get_dst_info(object) diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index c7c35564c1e5a..85cb50b8f18ae 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -3574,572 +3574,6 @@ cpdef normalize_date(object dt): else: raise TypeError('Unrecognized type: %s' % type(dt)) -cdef ndarray[int64_t] localize_dt64arr_to_period(ndarray[int64_t] stamps, - int freq, object tz): - cdef: - Py_ssize_t n = len(stamps) - ndarray[int64_t] result = np.empty(n, dtype=np.int64) - ndarray[int64_t] trans, deltas, pos - pandas_datetimestruct dts - - if not have_pytz: - raise Exception('Could not find pytz module') - - if _is_utc(tz): - for i in range(n): - if stamps[i] == NPY_NAT: - result[i] = NPY_NAT - continue - pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, &dts) - result[i] = get_period_ordinal(dts.year, dts.month, dts.day, - dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) - - elif _is_tzlocal(tz): - for i in range(n): - if stamps[i] == NPY_NAT: - result[i] = NPY_NAT - continue - pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, - &dts) - dt = datetime(dts.year, dts.month, dts.day, dts.hour, - dts.min, dts.sec, dts.us, tz) - delta = int(total_seconds(_get_utcoffset(tz, dt))) * 1000000000 - pandas_datetime_to_datetimestruct(stamps[i] + delta, - PANDAS_FR_ns, &dts) - result[i] = get_period_ordinal(dts.year, dts.month, dts.day, - dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) - else: - # Adjust datetime64 timestamp, recompute datetimestruct - trans, deltas, typ = _get_dst_info(tz) - - _pos = trans.searchsorted(stamps, side='right') - 1 - if _pos.dtype != np.int64: - _pos = _pos.astype(np.int64) - pos = _pos - - # statictzinfo - if typ not in ['pytz','dateutil']: - for i in range(n): - if stamps[i] == NPY_NAT: - result[i] = NPY_NAT - continue - pandas_datetime_to_datetimestruct(stamps[i] + deltas[0], - PANDAS_FR_ns, &dts) - result[i] = get_period_ordinal(dts.year, dts.month, dts.day, - dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) - else: - for i in range(n): - if stamps[i] == NPY_NAT: - result[i] = NPY_NAT - continue - pandas_datetime_to_datetimestruct(stamps[i] + deltas[pos[i]], - PANDAS_FR_ns, &dts) - result[i] = get_period_ordinal(dts.year, dts.month, dts.day, - dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) - - return result - - -cdef extern from "period.h": - ctypedef struct date_info: - int64_t absdate - double abstime - double second - int minute - int hour - int day - int month - int quarter - int year - int day_of_week - int day_of_year - int calendar - - ctypedef struct asfreq_info: - int from_week_end - int to_week_end - - int from_a_year_end - int to_a_year_end - - int from_q_year_end - int to_q_year_end - - ctypedef int64_t (*freq_conv_func)(int64_t, char, asfreq_info*) - - void initialize_daytime_conversion_factor_matrix() - int64_t asfreq(int64_t dtordinal, int freq1, int freq2, char relation) except INT32_MIN - freq_conv_func get_asfreq_func(int fromFreq, int toFreq) - void get_asfreq_info(int fromFreq, int toFreq, asfreq_info *af_info) - - int64_t get_period_ordinal(int year, int month, int day, - int hour, int minute, int second, int microseconds, int picoseconds, - int freq) except INT32_MIN - - int64_t get_python_ordinal(int64_t period_ordinal, int freq) except INT32_MIN - - int get_date_info(int64_t ordinal, int freq, date_info *dinfo) except INT32_MIN - double getAbsTime(int, int64_t, int64_t) - - int pyear(int64_t ordinal, int freq) except INT32_MIN - int pqyear(int64_t ordinal, int freq) except INT32_MIN - int pquarter(int64_t ordinal, int freq) except INT32_MIN - int pmonth(int64_t ordinal, int freq) except INT32_MIN - int pday(int64_t ordinal, int freq) except INT32_MIN - int pweekday(int64_t ordinal, int freq) except INT32_MIN - int pday_of_week(int64_t ordinal, int freq) except INT32_MIN - int pday_of_year(int64_t ordinal, int freq) except INT32_MIN - int pweek(int64_t ordinal, int freq) except INT32_MIN - int phour(int64_t ordinal, int freq) except INT32_MIN - int pminute(int64_t ordinal, int freq) except INT32_MIN - int psecond(int64_t ordinal, int freq) except INT32_MIN - char *c_strftime(date_info *dinfo, char *fmt) - int get_yq(int64_t ordinal, int freq, int *quarter, int *year) - -initialize_daytime_conversion_factor_matrix() - -# Period logic -#---------------------------------------------------------------------- - -cdef inline int64_t apply_mult(int64_t period_ord, int64_t mult): - """ - Get freq+multiple ordinal value from corresponding freq-only ordinal value. - For example, 5min ordinal will be 1/5th the 1min ordinal (rounding down to - integer). - """ - if mult == 1: - return period_ord - - return (period_ord - 1) // mult - -cdef inline int64_t remove_mult(int64_t period_ord_w_mult, int64_t mult): - """ - Get freq-only ordinal value from corresponding freq+multiple ordinal. - """ - if mult == 1: - return period_ord_w_mult - - return period_ord_w_mult * mult + 1; - -def dt64arr_to_periodarr(ndarray[int64_t] dtarr, int freq, tz=None): - """ - Convert array of datetime64 values (passed in as 'i8' dtype) to a set of - periods corresponding to desired frequency, per period convention. - """ - cdef: - ndarray[int64_t] out - Py_ssize_t i, l - pandas_datetimestruct dts - - l = len(dtarr) - - out = np.empty(l, dtype='i8') - - if tz is None: - for i in range(l): - if dtarr[i] == iNaT: - out[i] = iNaT - continue - pandas_datetime_to_datetimestruct(dtarr[i], PANDAS_FR_ns, &dts) - out[i] = get_period_ordinal(dts.year, dts.month, dts.day, - dts.hour, dts.min, dts.sec, dts.us, dts.ps, freq) - else: - out = localize_dt64arr_to_period(dtarr, freq, tz) - return out - -def periodarr_to_dt64arr(ndarray[int64_t] periodarr, int freq): - """ - Convert array to datetime64 values from a set of ordinals corresponding to - periods per period convention. - """ - cdef: - ndarray[int64_t] out - Py_ssize_t i, l - - l = len(periodarr) - - out = np.empty(l, dtype='i8') - - for i in range(l): - if periodarr[i] == iNaT: - out[i] = iNaT - continue - out[i] = period_ordinal_to_dt64(periodarr[i], freq) - - return out - -cdef char START = 'S' -cdef char END = 'E' - -cpdef int64_t period_asfreq(int64_t period_ordinal, int freq1, int freq2, - bint end): - """ - Convert period ordinal from one frequency to another, and if upsampling, - choose to use start ('S') or end ('E') of period. - """ - cdef: - int64_t retval - - if period_ordinal == iNaT: - return iNaT - - if end: - retval = asfreq(period_ordinal, freq1, freq2, END) - else: - retval = asfreq(period_ordinal, freq1, freq2, START) - - if retval == INT32_MIN: - raise ValueError('Frequency conversion failed') - - return retval - -def period_asfreq_arr(ndarray[int64_t] arr, int freq1, int freq2, bint end): - """ - Convert int64-array of period ordinals from one frequency to another, and - if upsampling, choose to use start ('S') or end ('E') of period. - """ - cdef: - ndarray[int64_t] result - Py_ssize_t i, n - freq_conv_func func - asfreq_info finfo - int64_t val, ordinal - char relation - - n = len(arr) - result = np.empty(n, dtype=np.int64) - - func = get_asfreq_func(freq1, freq2) - get_asfreq_info(freq1, freq2, &finfo) - - if end: - relation = END - else: - relation = START - - mask = arr == iNaT - if mask.any(): # NaT process - for i in range(n): - val = arr[i] - if val != iNaT: - val = func(val, relation, &finfo) - if val == INT32_MIN: - raise ValueError("Unable to convert to desired frequency.") - result[i] = val - else: - for i in range(n): - val = func(arr[i], relation, &finfo) - if val == INT32_MIN: - raise ValueError("Unable to convert to desired frequency.") - result[i] = val - - return result - -def period_ordinal(int y, int m, int d, int h, int min, int s, int us, int ps, int freq): - cdef: - int64_t ordinal - - return get_period_ordinal(y, m, d, h, min, s, us, ps, freq) - - -cpdef int64_t period_ordinal_to_dt64(int64_t ordinal, int freq): - cdef: - pandas_datetimestruct dts - date_info dinfo - float subsecond_fraction - - if ordinal == iNaT: - return NPY_NAT - - get_date_info(ordinal, freq, &dinfo) - - dts.year = dinfo.year - dts.month = dinfo.month - dts.day = dinfo.day - dts.hour = dinfo.hour - dts.min = dinfo.minute - dts.sec = int(dinfo.second) - subsecond_fraction = dinfo.second - dts.sec - dts.us = int((subsecond_fraction) * 1e6) - dts.ps = int(((subsecond_fraction) * 1e6 - dts.us) * 1e6) - - return pandas_datetimestruct_to_datetime(PANDAS_FR_ns, &dts) - -def period_format(int64_t value, int freq, object fmt=None): - cdef: - int freq_group - - if value == iNaT: - return repr(NaT) - - if fmt is None: - freq_group = (freq // 1000) * 1000 - if freq_group == 1000: # FR_ANN - fmt = b'%Y' - elif freq_group == 2000: # FR_QTR - fmt = b'%FQ%q' - elif freq_group == 3000: # FR_MTH - fmt = b'%Y-%m' - elif freq_group == 4000: # WK - left = period_asfreq(value, freq, 6000, 0) - right = period_asfreq(value, freq, 6000, 1) - return '%s/%s' % (period_format(left, 6000), - period_format(right, 6000)) - elif (freq_group == 5000 # BUS - or freq_group == 6000): # DAY - fmt = b'%Y-%m-%d' - elif freq_group == 7000: # HR - fmt = b'%Y-%m-%d %H:00' - elif freq_group == 8000: # MIN - fmt = b'%Y-%m-%d %H:%M' - elif freq_group == 9000: # SEC - fmt = b'%Y-%m-%d %H:%M:%S' - elif freq_group == 10000: # MILLISEC - fmt = b'%Y-%m-%d %H:%M:%S.%l' - elif freq_group == 11000: # MICROSEC - fmt = b'%Y-%m-%d %H:%M:%S.%u' - elif freq_group == 12000: # NANOSEC - fmt = b'%Y-%m-%d %H:%M:%S.%n' - else: - raise ValueError('Unknown freq: %d' % freq) - - return _period_strftime(value, freq, fmt) - - -cdef list extra_fmts = [(b"%q", b"^`AB`^"), - (b"%f", b"^`CD`^"), - (b"%F", b"^`EF`^"), - (b"%l", b"^`GH`^"), - (b"%u", b"^`IJ`^"), - (b"%n", b"^`KL`^")] - -cdef list str_extra_fmts = ["^`AB`^", "^`CD`^", "^`EF`^", "^`GH`^", "^`IJ`^", "^`KL`^"] - -cdef object _period_strftime(int64_t value, int freq, object fmt): - import sys - - cdef: - Py_ssize_t i - date_info dinfo - char *formatted - object pat, repl, result - list found_pat = [False] * len(extra_fmts) - int year, quarter - - if PyUnicode_Check(fmt): - fmt = fmt.encode('utf-8') - - get_date_info(value, freq, &dinfo) - for i in range(len(extra_fmts)): - pat = extra_fmts[i][0] - repl = extra_fmts[i][1] - if pat in fmt: - fmt = fmt.replace(pat, repl) - found_pat[i] = True - - formatted = c_strftime(&dinfo, fmt) - - result = util.char_to_string(formatted) - free(formatted) - - for i in range(len(extra_fmts)): - if found_pat[i]: - if get_yq(value, freq, &quarter, &year) < 0: - raise ValueError('Unable to get quarter and year') - - if i == 0: - repl = '%d' % quarter - elif i == 1: # %f, 2-digit year - repl = '%.2d' % (year % 100) - elif i == 2: - repl = '%d' % year - elif i == 3: - repl = '%03d' % (value % 1000) - elif i == 4: - repl = '%06d' % (value % 1000000) - elif i == 5: - repl = '%09d' % (value % 1000000000) - - result = result.replace(str_extra_fmts[i], repl) - - if PY2: - result = result.decode('utf-8', 'ignore') - - return result - -# period accessors - -ctypedef int (*accessor)(int64_t ordinal, int freq) except INT32_MIN - -def get_period_field(int code, int64_t value, int freq): - cdef accessor f = _get_accessor_func(code) - if f is NULL: - raise ValueError('Unrecognized period code: %d' % code) - if value == iNaT: - return np.nan - return f(value, freq) - -def get_period_field_arr(int code, ndarray[int64_t] arr, int freq): - cdef: - Py_ssize_t i, sz - ndarray[int64_t] out - accessor f - - f = _get_accessor_func(code) - if f is NULL: - raise ValueError('Unrecognized period code: %d' % code) - - sz = len(arr) - out = np.empty(sz, dtype=np.int64) - - for i in range(sz): - if arr[i] == iNaT: - out[i] = -1 - continue - out[i] = f(arr[i], freq) - - return out - - - -cdef accessor _get_accessor_func(int code): - if code == 0: - return &pyear - elif code == 1: - return &pqyear - elif code == 2: - return &pquarter - elif code == 3: - return &pmonth - elif code == 4: - return &pday - elif code == 5: - return &phour - elif code == 6: - return &pminute - elif code == 7: - return &psecond - elif code == 8: - return &pweek - elif code == 9: - return &pday_of_year - elif code == 10: - return &pweekday - return NULL - - -def extract_ordinals(ndarray[object] values, freq): - cdef: - Py_ssize_t i, n = len(values) - ndarray[int64_t] ordinals = np.empty(n, dtype=np.int64) - object p - - for i in range(n): - p = values[i] - ordinals[i] = p.ordinal - if p.freq != freq: - raise ValueError("%s is wrong freq" % p) - - return ordinals - -cpdef resolution(ndarray[int64_t] stamps, tz=None): - cdef: - Py_ssize_t i, n = len(stamps) - pandas_datetimestruct dts - int reso = D_RESO, curr_reso - - if tz is not None: - tz = maybe_get_tz(tz) - return _reso_local(stamps, tz) - else: - for i in range(n): - if stamps[i] == NPY_NAT: - continue - pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, &dts) - curr_reso = _reso_stamp(&dts) - if curr_reso < reso: - reso = curr_reso - return reso - -US_RESO = 0 -MS_RESO = 1 -S_RESO = 2 -T_RESO = 3 -H_RESO = 4 -D_RESO = 5 - -cdef inline int _reso_stamp(pandas_datetimestruct *dts): - if dts.us != 0: - if dts.us % 1000 == 0: - return MS_RESO - return US_RESO - elif dts.sec != 0: - return S_RESO - elif dts.min != 0: - return T_RESO - elif dts.hour != 0: - return H_RESO - return D_RESO - -cdef _reso_local(ndarray[int64_t] stamps, object tz): - cdef: - Py_ssize_t n = len(stamps) - int reso = D_RESO, curr_reso - ndarray[int64_t] trans, deltas, pos - pandas_datetimestruct dts - - if _is_utc(tz): - for i in range(n): - if stamps[i] == NPY_NAT: - continue - pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, &dts) - curr_reso = _reso_stamp(&dts) - if curr_reso < reso: - reso = curr_reso - elif _is_tzlocal(tz): - for i in range(n): - if stamps[i] == NPY_NAT: - continue - pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, - &dts) - dt = datetime(dts.year, dts.month, dts.day, dts.hour, - dts.min, dts.sec, dts.us, tz) - delta = int(total_seconds(_get_utcoffset(tz, dt))) * 1000000000 - pandas_datetime_to_datetimestruct(stamps[i] + delta, - PANDAS_FR_ns, &dts) - curr_reso = _reso_stamp(&dts) - if curr_reso < reso: - reso = curr_reso - else: - # Adjust datetime64 timestamp, recompute datetimestruct - trans, deltas, typ = _get_dst_info(tz) - - _pos = trans.searchsorted(stamps, side='right') - 1 - if _pos.dtype != np.int64: - _pos = _pos.astype(np.int64) - pos = _pos - - # statictzinfo - if typ not in ['pytz','dateutil']: - for i in range(n): - if stamps[i] == NPY_NAT: - continue - pandas_datetime_to_datetimestruct(stamps[i] + deltas[0], - PANDAS_FR_ns, &dts) - curr_reso = _reso_stamp(&dts) - if curr_reso < reso: - reso = curr_reso - else: - for i in range(n): - if stamps[i] == NPY_NAT: - continue - pandas_datetime_to_datetimestruct(stamps[i] + deltas[pos[i]], - PANDAS_FR_ns, &dts) - curr_reso = _reso_stamp(&dts) - if curr_reso < reso: - reso = curr_reso - - return reso #---------------------------------------------------------------------- # Don't even ask diff --git a/setup.py b/setup.py index f90e238ad9dc1..321ffb36d76f2 100755 --- a/setup.py +++ b/setup.py @@ -462,7 +462,11 @@ def pxd(name): 'sources': ['pandas/src/datetime/np_datetime.c', 'pandas/src/datetime/np_datetime_strings.c', 'pandas/src/period.c']}, - period=dict(pyxfile='period'), + period=dict(pyxfile='period', + depends=tseries_depends, + sources=['pandas/src/datetime/np_datetime.c', + 'pandas/src/datetime/np_datetime_strings.c', + 'pandas/src/period.c']), index={'pyxfile': 'index', 'sources': ['pandas/src/datetime/np_datetime.c', 'pandas/src/datetime/np_datetime_strings.c']},