Skip to content

Commit 0aceb38

Browse files
Merge pull request pandas-dev#10171 from jreback/dt_format
BUG: consistent datetime display format with < ms pandas-dev#10170
2 parents 1a709c3 + 61da6da commit 0aceb38

File tree

4 files changed

+83
-29
lines changed

4 files changed

+83
-29
lines changed

doc/source/whatsnew/v0.17.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Bug Fixes
6868
- Bug in ``NaT`` raises ``AttributeError`` when accessing to ``daysinmonth``, ``dayofweek`` properties. (:issue:`10096`)
6969

7070
- Bug in getting timezone data with ``dateutil`` on various platforms ( :issue:`9059`, :issue:`8639`, :issue:`9663`, :issue:`10121`)
71-
71+
- Bug in display datetimes with mixed frequencies uniformly; display 'ms' datetimes to the proper precision. (:issue:`10170`)
7272

7373

7474
- Bug in ``DatetimeIndex`` and ``TimedeltaIndex`` names are lost after timedelta arithmetics ( :issue:`9926`)

pandas/tests/test_format.py

+40-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from numpy.random import randn
1515
import numpy as np
1616

17-
from pandas import DataFrame, Series, Index, Timestamp, MultiIndex
17+
from pandas import DataFrame, Series, Index, Timestamp, MultiIndex, date_range, NaT
1818

1919
import pandas.core.format as fmt
2020
import pandas.util.testing as tm
@@ -2495,7 +2495,7 @@ def test_to_string(self):
24952495

24962496
def test_freq_name_separation(self):
24972497
s = Series(np.random.randn(10),
2498-
index=pd.date_range('1/1/2000', periods=10), name=0)
2498+
index=date_range('1/1/2000', periods=10), name=0)
24992499

25002500
result = repr(s)
25012501
self.assertTrue('Freq: D, Name: 0' in result)
@@ -2556,7 +2556,6 @@ def test_float_trim_zeros(self):
25562556

25572557
def test_datetimeindex(self):
25582558

2559-
from pandas import date_range, NaT
25602559
index = date_range('20130102',periods=6)
25612560
s = Series(1,index=index)
25622561
result = s.to_string()
@@ -2574,7 +2573,6 @@ def test_datetimeindex(self):
25742573

25752574
def test_timedelta64(self):
25762575

2577-
from pandas import date_range
25782576
from datetime import datetime, timedelta
25792577

25802578
Series(np.array([1100, 20], dtype='timedelta64[ns]')).to_string()
@@ -3179,6 +3177,44 @@ def test_date_nanos(self):
31793177
result = fmt.Datetime64Formatter(x).get_result()
31803178
self.assertEqual(result[0].strip(), "1970-01-01 00:00:00.000000200")
31813179

3180+
def test_dates_display(self):
3181+
3182+
# 10170
3183+
# make sure that we are consistently display date formatting
3184+
x = Series(date_range('20130101 09:00:00',periods=5,freq='D'))
3185+
x.iloc[1] = np.nan
3186+
result = fmt.Datetime64Formatter(x).get_result()
3187+
self.assertEqual(result[0].strip(), "2013-01-01 09:00:00")
3188+
self.assertEqual(result[1].strip(), "NaT")
3189+
self.assertEqual(result[4].strip(), "2013-01-05 09:00:00")
3190+
3191+
x = Series(date_range('20130101 09:00:00',periods=5,freq='s'))
3192+
x.iloc[1] = np.nan
3193+
result = fmt.Datetime64Formatter(x).get_result()
3194+
self.assertEqual(result[0].strip(), "2013-01-01 09:00:00")
3195+
self.assertEqual(result[1].strip(), "NaT")
3196+
self.assertEqual(result[4].strip(), "2013-01-01 09:00:04")
3197+
3198+
x = Series(date_range('20130101 09:00:00',periods=5,freq='ms'))
3199+
x.iloc[1] = np.nan
3200+
result = fmt.Datetime64Formatter(x).get_result()
3201+
self.assertEqual(result[0].strip(), "2013-01-01 09:00:00.000")
3202+
self.assertEqual(result[1].strip(), "NaT")
3203+
self.assertEqual(result[4].strip(), "2013-01-01 09:00:00.004")
3204+
3205+
x = Series(date_range('20130101 09:00:00',periods=5,freq='us'))
3206+
x.iloc[1] = np.nan
3207+
result = fmt.Datetime64Formatter(x).get_result()
3208+
self.assertEqual(result[0].strip(), "2013-01-01 09:00:00.000000")
3209+
self.assertEqual(result[1].strip(), "NaT")
3210+
self.assertEqual(result[4].strip(), "2013-01-01 09:00:00.000004")
3211+
3212+
x = Series(date_range('20130101 09:00:00',periods=5,freq='N'))
3213+
x.iloc[1] = np.nan
3214+
result = fmt.Datetime64Formatter(x).get_result()
3215+
self.assertEqual(result[0].strip(), "2013-01-01 09:00:00.000000000")
3216+
self.assertEqual(result[1].strip(), "NaT")
3217+
self.assertEqual(result[4].strip(), "2013-01-01 09:00:00.000000004")
31823218

31833219
class TestNaTFormatting(tm.TestCase):
31843220
def test_repr(self):

pandas/tseries/tests/test_timeseries.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ def test_series_repr_nat(self):
791791
series = Series([0, 1000, 2000, iNaT], dtype='M8[ns]')
792792

793793
result = repr(series)
794-
expected = ('0 1970-01-01 00:00:00\n'
794+
expected = ('0 1970-01-01 00:00:00.000000\n'
795795
'1 1970-01-01 00:00:00.000001\n'
796796
'2 1970-01-01 00:00:00.000002\n'
797797
'3 NaT\n'

pandas/tslib.pyx

+41-23
Original file line numberDiff line numberDiff line change
@@ -1418,20 +1418,36 @@ def format_array_from_datetime(ndarray[int64_t] values, object tz=None, object f
14181418
"""
14191419
cdef:
14201420
int64_t val, ns, N = len(values)
1421+
ndarray[int64_t] consider_values
1422+
bint show_ms = 0, show_us = 0, show_ns = 0, basic_format = 0
14211423
ndarray[object] result = np.empty(N, dtype=object)
14221424
object ts, res
14231425
pandas_datetimestruct dts
14241426

14251427
if na_rep is None:
14261428
na_rep = 'NaT'
14271429

1430+
# if we don't have a format nor tz, then choose
1431+
# a format based on precision
1432+
basic_format = format is None and tz is None
1433+
if basic_format:
1434+
consider_values = values[values != iNaT]
1435+
show_ns = (consider_values%1000).any()
1436+
1437+
if not show_ns:
1438+
consider_values //= 1000
1439+
show_us = (consider_values%1000).any()
1440+
1441+
if not show_ms:
1442+
consider_values //= 1000
1443+
show_ms = (consider_values%1000).any()
1444+
14281445
for i in range(N):
1429-
val = values[i]
1446+
val = values[i]
14301447

1431-
if val == iNaT:
1432-
result[i] = na_rep
1433-
else:
1434-
if format is None and tz is None:
1448+
if val == iNaT:
1449+
result[i] = na_rep
1450+
elif basic_format:
14351451

14361452
pandas_datetime_to_datetimestruct(val, PANDAS_FR_ns, &dts)
14371453
res = '%d-%.2d-%.2d %.2d:%.2d:%.2d' % (dts.year,
@@ -1441,27 +1457,29 @@ def format_array_from_datetime(ndarray[int64_t] values, object tz=None, object f
14411457
dts.min,
14421458
dts.sec)
14431459

1444-
ns = dts.ps / 1000
1445-
1446-
if ns != 0:
1447-
res += '.%.9d' % (ns + 1000 * dts.us)
1448-
elif dts.us != 0:
1449-
res += '.%.6d' % dts.us
1460+
if show_ns:
1461+
ns = dts.ps / 1000
1462+
res += '.%.9d' % (ns + 1000 * dts.us)
1463+
elif show_us:
1464+
res += '.%.6d' % dts.us
1465+
elif show_ms:
1466+
res += '.%.3d' % (dts.us/1000)
14501467

14511468
result[i] = res
14521469

1453-
else:
1454-
ts = Timestamp(val, tz=tz)
1455-
if format is None:
1456-
result[i] = str(ts)
1457-
else:
1458-
1459-
# invalid format string
1460-
# requires dates > 1900
1461-
try:
1462-
result[i] = ts.strftime(format)
1463-
except ValueError:
1464-
result[i] = str(ts)
1470+
else:
1471+
1472+
ts = Timestamp(val, tz=tz)
1473+
if format is None:
1474+
result[i] = str(ts)
1475+
else:
1476+
1477+
# invalid format string
1478+
# requires dates > 1900
1479+
try:
1480+
result[i] = ts.strftime(format)
1481+
except ValueError:
1482+
result[i] = str(ts)
14651483

14661484
return result
14671485

0 commit comments

Comments
 (0)