diff --git a/doc/source/timeseries.rst b/doc/source/timeseries.rst index d816bc171c300..8f3d2fe8eb079 100644 --- a/doc/source/timeseries.rst +++ b/doc/source/timeseries.rst @@ -413,7 +413,7 @@ regularity will result in a ``DatetimeIndex`` (but frequency is lost): Time/Date Components ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -There are several time/date properties that one can access from ``Timestamp`` or a collection of timestamps like a ``DateTimeIndex``. +There are several time/date properties that one can access from ``Timestamp`` or a collection of timestamps like a ``DateTimeIndex``. .. csv-table:: :header: "Property", "Description" @@ -604,7 +604,7 @@ in the usual way. # Skip new years dt = datetime(2013, 12, 17) dt + bmth_us - + # Define date index with custom offset from pandas import DatetimeIndex DatetimeIndex(start='20100101',end='20120101',freq=bmth_us) @@ -789,8 +789,8 @@ methods to return a list of holidays and only ``rules`` need to be defined in a specific holiday calendar class. Further, ``start_date`` and ``end_date`` class attributes determine over what date range holidays are generated. These should be overwritten on the ``AbstractHolidayCalendar`` class to have the range -apply to all calendar subclasses. ``USFederalHolidayCalendar`` is the -only calendar that exists and primarily serves as an example for developing +apply to all calendar subclasses. ``USFederalHolidayCalendar`` is the +only calendar that exists and primarily serves as an example for developing other calendars. For holidays that occur on fixed dates (e.g., US Memorial Day or July 4th) an @@ -823,12 +823,12 @@ An example of how holidays and holiday calendars are defined: cal = ExampleCalendar() cal.holidays(datetime(2012, 1, 1), datetime(2012, 12, 31)) -Using this calendar, creating an index or doing offset arithmetic skips weekends +Using this calendar, creating an index or doing offset arithmetic skips weekends and holidays (i.e., Memorial Day/July 4th). .. ipython:: python - DatetimeIndex(start='7/1/2012', end='7/10/2012', + DatetimeIndex(start='7/1/2012', end='7/10/2012', freq=CDay(calendar=cal)).to_pydatetime() offset = CustomBusinessDay(calendar=cal) datetime(2012, 5, 25) + offset @@ -840,11 +840,11 @@ Ranges are defined by the ``start_date`` and ``end_date`` class attributes of ``AbstractHolidayCalendar``. The defaults are below. .. ipython:: python - + AbstractHolidayCalendar.start_date AbstractHolidayCalendar.end_date -These dates can be overwritten by setting the attributes as +These dates can be overwritten by setting the attributes as datetime/Timestamp/string. .. ipython:: python @@ -1120,7 +1120,7 @@ Passing string represents lower frequency than `PeriodIndex` returns partial sli dfp dfp['2013-01-01 10H'] -As the same as `DatetimeIndex`, the endpoints will be included in the result. Below example slices data starting from 10:00 to 11:59. +As the same as `DatetimeIndex`, the endpoints will be included in the result. Below example slices data starting from 10:00 to 11:59. .. ipython:: python diff --git a/pandas/core/base.py b/pandas/core/base.py index 68200f5ed8d31..0e7bc0fee8a48 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -201,16 +201,6 @@ def __unicode__(self): return "%s(%s, dtype='%s')" % (type(self).__name__, prepr, self.dtype) -# facilitate the properties on the wrapped ops -def _field_accessor(name, docstring=None): - op_accessor = '_{0}'.format(name) - def f(self): - return self._ops_compat(name,op_accessor) - - f.__name__ = name - f.__doc__ = docstring - return property(f) - class IndexOpsMixin(object): """ common ops mixin to support a unified inteface / docs for Series / Index """ @@ -219,24 +209,9 @@ def _is_allowed_index_op(self, name): raise TypeError("cannot perform an {name} operations on this type {typ}".format( name=name,typ=type(self._get_access_object()))) - def _is_allowed_datetime_index_op(self, name): - if not self._allow_datetime_index_ops: - raise TypeError("cannot perform an {name} operations on this type {typ}".format( - name=name,typ=type(self._get_access_object()))) - - def _is_allowed_period_index_op(self, name): - if not self._allow_period_index_ops: - raise TypeError("cannot perform an {name} operations on this type {typ}".format( - name=name,typ=type(self._get_access_object()))) - def _ops_compat(self, name, op_accessor): - from pandas.tseries.index import DatetimeIndex - from pandas.tseries.period import PeriodIndex + obj = self._get_access_object() - if isinstance(obj, DatetimeIndex): - self._is_allowed_datetime_index_op(name) - elif isinstance(obj, PeriodIndex): - self._is_allowed_period_index_op(name) try: return self._wrap_access_object(getattr(obj,op_accessor)) except AttributeError: @@ -336,6 +311,44 @@ def factorize(self, sort=False, na_sentinel=-1): from pandas.core.algorithms import factorize return factorize(self, sort=sort, na_sentinel=na_sentinel) +# facilitate the properties on the wrapped ops +def _field_accessor(name, docstring=None): + op_accessor = '_{0}'.format(name) + def f(self): + return self._ops_compat(name,op_accessor) + + f.__name__ = name + f.__doc__ = docstring + return property(f) + +class DatetimeIndexOpsMixin(object): + """ common ops mixin to support a unified inteface datetimelike Index """ + + def _is_allowed_datetime_index_op(self, name): + if not self._allow_datetime_index_ops: + raise TypeError("cannot perform an {name} operations on this type {typ}".format( + name=name,typ=type(self._get_access_object()))) + + def _is_allowed_period_index_op(self, name): + if not self._allow_period_index_ops: + raise TypeError("cannot perform an {name} operations on this type {typ}".format( + name=name,typ=type(self._get_access_object()))) + + def _ops_compat(self, name, op_accessor): + + from pandas.tseries.index import DatetimeIndex + from pandas.tseries.period import PeriodIndex + obj = self._get_access_object() + if isinstance(obj, DatetimeIndex): + self._is_allowed_datetime_index_op(name) + elif isinstance(obj, PeriodIndex): + self._is_allowed_period_index_op(name) + try: + return self._wrap_access_object(getattr(obj,op_accessor)) + except AttributeError: + raise TypeError("cannot perform an {name} operations on this type {typ}".format( + name=name,typ=type(obj))) + date = _field_accessor('date','Returns numpy array of datetime.date. The date part of the Timestamps') time = _field_accessor('time','Returns numpy array of datetime.time. The time part of the Timestamps') year = _field_accessor('year', "The year of the datetime") diff --git a/pandas/tests/test_base.py b/pandas/tests/test_base.py index 49430be8c98a6..39d8eb8360244 100644 --- a/pandas/tests/test_base.py +++ b/pandas/tests/test_base.py @@ -4,7 +4,7 @@ import pandas.compat as compat import pandas as pd from pandas.compat import u, StringIO -from pandas.core.base import FrozenList, FrozenNDArray +from pandas.core.base import FrozenList, FrozenNDArray, DatetimeIndexOpsMixin from pandas.util.testing import assertRaisesRegexp, assert_isinstance from pandas import Series, Index, Int64Index, DatetimeIndex, PeriodIndex from pandas import _np_version_under1p7 @@ -181,7 +181,13 @@ def check_ops_properties(self, props, filter=None, ignore_failures=False): # we mostly care about Series hwere anyhow if not ignore_failures: for o in self.not_valid_objs: - self.assertRaises(TypeError, lambda : getattr(o,op)) + + # an object that is datetimelike will raise a TypeError, otherwise + # an AttributeError + if issubclass(type(o), DatetimeIndexOpsMixin): + self.assertRaises(TypeError, lambda : getattr(o,op)) + else: + self.assertRaises(AttributeError, lambda : getattr(o,op)) class TestIndexOps(Ops): @@ -462,6 +468,13 @@ def test_ops_properties_basic(self): for op in ['year','day','second','weekday']: self.assertRaises(TypeError, lambda x: getattr(self.dt_series,op)) + # attribute access should still work! + s = Series(dict(year=2000,month=1,day=10)) + self.assertEquals(s.year,2000) + self.assertEquals(s.month,1) + self.assertEquals(s.day,10) + self.assertRaises(AttributeError, lambda : s.weekday) + class TestPeriodIndexOps(Ops): _allowed = '_allow_period_index_ops' diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index e0ae6a30cbf1a..c3c7ae9bacbcd 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -5617,12 +5617,6 @@ def test_asfreq(self): self.assertEqual(len(result), 0) self.assertIsNot(result, ts) - def test_weekday(self): - # Just run the function - def f(): - self.ts.weekday - self.assertRaises(TypeError, f) - def test_diff(self): # Just run the function self.ts.diff() diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index c1a22f547a86c..0e18509d9bc26 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -15,6 +15,7 @@ from pandas.tseries.frequencies import ( infer_freq, to_offset, get_period_alias, Resolution, get_reso_string, get_offset) +from pandas.core.base import DatetimeIndexOpsMixin from pandas.tseries.offsets import DateOffset, generate_range, Tick, CDay from pandas.tseries.tools import parse_time_string, normalize_date from pandas.util.decorators import cache_readonly @@ -102,7 +103,7 @@ def _ensure_datetime64(other): _midnight = time(0, 0) -class DatetimeIndex(Int64Index): +class DatetimeIndex(DatetimeIndexOpsMixin, Int64Index): """ Immutable ndarray of datetime64 data, represented internally as int64, and which can be boxed to Timestamp objects that are subclasses of datetime and diff --git a/pandas/tseries/period.py b/pandas/tseries/period.py index 1d528c9545863..5516c4634ea58 100644 --- a/pandas/tseries/period.py +++ b/pandas/tseries/period.py @@ -8,6 +8,7 @@ from pandas.tseries.frequencies import (get_freq_code as _gfc, _month_numbers, FreqGroup) from pandas.tseries.index import DatetimeIndex, Int64Index, Index +from pandas.core.base import DatetimeIndexOpsMixin from pandas.tseries.tools import parse_time_string import pandas.tseries.frequencies as _freq_mod @@ -500,7 +501,7 @@ def wrapper(self, other): return wrapper -class PeriodIndex(Int64Index): +class PeriodIndex(DatetimeIndexOpsMixin, Int64Index): """ Immutable ndarray holding ordinal values indicating regular periods in time such as particular years, quarters, months, etc. A value of 1 is the