diff --git a/doc/source/whatsnew/v0.18.1.txt b/doc/source/whatsnew/v0.18.1.txt index 2545407ce43c9..103159ae33d88 100644 --- a/doc/source/whatsnew/v0.18.1.txt +++ b/doc/source/whatsnew/v0.18.1.txt @@ -143,3 +143,5 @@ Bug Fixes - Bug in ``pivot_table`` when ``margins=True`` and ``dropna=True`` where nulls still contributed to margin count (:issue:`12577`) + +- Bug in ``Timestamp.__repr__`` that caused ``pprint`` to fail in nested structures (:issue:`12622`) diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py index ecbe2827f5447..f0d5bf7e046c3 100644 --- a/pandas/tseries/tests/test_tslib.py +++ b/pandas/tseries/tests/test_tslib.py @@ -453,6 +453,25 @@ def test_nat_fields(self): self.assertTrue(np.isnan(ts.daysinmonth)) self.assertTrue(np.isnan(ts.days_in_month)) + def test_pprint(self): + # GH12622 + import pprint + nested_obj = {'foo': 1, + 'bar': [{'w': {'a': Timestamp('2011-01-01')}}] * 10} + result = pprint.pformat(nested_obj, width=50) + expected = r'''{'bar': [{'w': {'a': Timestamp('2011-01-01 00:00:00')}}, + {'w': {'a': Timestamp('2011-01-01 00:00:00')}}, + {'w': {'a': Timestamp('2011-01-01 00:00:00')}}, + {'w': {'a': Timestamp('2011-01-01 00:00:00')}}, + {'w': {'a': Timestamp('2011-01-01 00:00:00')}}, + {'w': {'a': Timestamp('2011-01-01 00:00:00')}}, + {'w': {'a': Timestamp('2011-01-01 00:00:00')}}, + {'w': {'a': Timestamp('2011-01-01 00:00:00')}}, + {'w': {'a': Timestamp('2011-01-01 00:00:00')}}, + {'w': {'a': Timestamp('2011-01-01 00:00:00')}}], + 'foo': 1}''' + self.assertEqual(result, expected) + class TestDatetimeParsingWrappers(tm.TestCase): def test_does_not_convert_mixed_integer(self): diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index 04ee609814b5b..dc089785238d9 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -316,50 +316,6 @@ class Timestamp(_Timestamp): return ts_base - def __repr__(self): - stamp = self._repr_base - zone = None - - try: - stamp += self.strftime('%z') - if self.tzinfo: - zone = _get_zone(self.tzinfo) - except ValueError: - year2000 = self.replace(year=2000) - stamp += year2000.strftime('%z') - if self.tzinfo: - zone = _get_zone(self.tzinfo) - - try: - stamp += zone.strftime(' %%Z') - except: - pass - - tz = ", tz='{0}'".format(zone) if zone is not None else "" - offset = ", offset='{0}'".format(self.offset.freqstr) if self.offset is not None else "" - - return "Timestamp('{stamp}'{tz}{offset})".format(stamp=stamp, tz=tz, offset=offset) - - @property - def _date_repr(self): - # Ideal here would be self.strftime("%Y-%m-%d"), but - # the datetime strftime() methods require year >= 1900 - return '%d-%.2d-%.2d' % (self.year, self.month, self.day) - - @property - def _time_repr(self): - result = '%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second) - - if self.nanosecond != 0: - result += '.%.9d' % (self.nanosecond + 1000 * self.microsecond) - elif self.microsecond != 0: - result += '.%.6d' % self.microsecond - - return result - - @property - def _repr_base(self): - return '%s %s' % (self._date_repr, self._time_repr) def _round(self, freq, rounder): @@ -977,6 +933,30 @@ cdef class _Timestamp(datetime): self._assert_tzawareness_compat(other) return _cmp_scalar(self.value, ots.value, op) + def __repr__(self): + stamp = self._repr_base + zone = None + + try: + stamp += self.strftime('%z') + if self.tzinfo: + zone = _get_zone(self.tzinfo) + except ValueError: + year2000 = self.replace(year=2000) + stamp += year2000.strftime('%z') + if self.tzinfo: + zone = _get_zone(self.tzinfo) + + try: + stamp += zone.strftime(' %%Z') + except: + pass + + tz = ", tz='{0}'".format(zone) if zone is not None else "" + offset = ", offset='{0}'".format(self.offset.freqstr) if self.offset is not None else "" + + return "Timestamp('{stamp}'{tz}{offset})".format(stamp=stamp, tz=tz, offset=offset) + cdef bint _compare_outside_nanorange(_Timestamp self, datetime other, int op) except -1: cdef datetime dtval = self.to_datetime() @@ -1098,6 +1078,27 @@ cdef class _Timestamp(datetime): out = get_start_end_field(np.array([self.value], dtype=np.int64), field, freqstr, month_kw) return out[0] + property _repr_base: + def __get__(self): + return '%s %s' % (self._date_repr, self._time_repr) + + property _date_repr: + def __get__(self): + # Ideal here would be self.strftime("%Y-%m-%d"), but + # the datetime strftime() methods require year >= 1900 + return '%d-%.2d-%.2d' % (self.year, self.month, self.day) + + property _time_repr: + def __get__(self): + result = '%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second) + + if self.nanosecond != 0: + result += '.%.9d' % (self.nanosecond + 1000 * self.microsecond) + elif self.microsecond != 0: + result += '.%.6d' % self.microsecond + + return result + property asm8: def __get__(self): return np.datetime64(self.value, 'ns')