diff --git a/doc/source/whatsnew/v0.18.2.txt b/doc/source/whatsnew/v0.18.2.txt
index 64644bd9a7a26..90bca9e11826b 100644
--- a/doc/source/whatsnew/v0.18.2.txt
+++ b/doc/source/whatsnew/v0.18.2.txt
@@ -529,4 +529,5 @@ Bug Fixes
- Bug in ``Categorical.remove_unused_categories()`` changes ``.codes`` dtype to platform int (:issue:`13261`)
- Bug in ``groupby`` with ``as_index=False`` returns all NaN's when grouping on multiple columns including a categorical one (:issue:`13204`)
-- Bug where ``pd.read_gbq()`` could throw ``ImportError: No module named discovery`` as a result of a naming conflict with another python package called apiclient (:issue:`13454`)
+- Bug in ``.to_html``, ``.to_latex`` and ``.to_string`` silently ignore custom datetime formatter passed through the ``formatters`` key word (:issue:`10690`)
+- Bug where ``pd.read_gbq()`` could throw ``ImportError: No module named discovery`` as a result of a naming conflict with another python package called apiclient (:issue:`13454`)
\ No newline at end of file
diff --git a/pandas/formats/format.py b/pandas/formats/format.py
index a8e184ce94c89..0c6a15db4ccfe 100644
--- a/pandas/formats/format.py
+++ b/pandas/formats/format.py
@@ -2239,9 +2239,13 @@ def _format_strings(self):
""" we by definition have DO NOT have a TZ """
values = self.values
+
if not isinstance(values, DatetimeIndex):
values = DatetimeIndex(values)
+ if self.formatter is not None and callable(self.formatter):
+ return [self.formatter(x) for x in values]
+
fmt_values = format_array_from_datetime(
values.asi8.ravel(),
format=_get_format_datetime64_from_values(values,
diff --git a/pandas/tests/formats/test_format.py b/pandas/tests/formats/test_format.py
index e67fe2cddde77..c5e9c258b293a 100644
--- a/pandas/tests/formats/test_format.py
+++ b/pandas/tests/formats/test_format.py
@@ -456,6 +456,28 @@ def test_to_string_with_formatters(self):
'2 0x3 [ 3.0] -False-'))
self.assertEqual(result, result2)
+ def test_to_string_with_datetime64_monthformatter(self):
+ months = [datetime(2016, 1, 1), datetime(2016, 2, 2)]
+ x = DataFrame({'months': months})
+
+ def format_func(x):
+ return x.strftime('%Y-%m')
+ result = x.to_string(formatters={'months': format_func})
+ expected = 'months\n0 2016-01\n1 2016-02'
+ self.assertEqual(result.strip(), expected)
+
+ def test_to_string_with_datetime64_hourformatter(self):
+
+ x = DataFrame({'hod': pd.to_datetime(['10:10:10.100', '12:12:12.120'],
+ format='%H:%M:%S.%f')})
+
+ def format_func(x):
+ return x.strftime('%H:%M')
+
+ result = x.to_string(formatters={'hod': format_func})
+ expected = 'hod\n0 10:10\n1 12:12'
+ self.assertEqual(result.strip(), expected)
+
def test_to_string_with_formatters_unicode(self):
df = DataFrame({u('c/\u03c3'): [1, 2, 3]})
result = df.to_string(formatters={u('c/\u03c3'): lambda x: '%s' % x})
@@ -1233,6 +1255,63 @@ def test_to_html_index_formatter(self):
self.assertEqual(result, expected)
+ def test_to_html_datetime64_monthformatter(self):
+ months = [datetime(2016, 1, 1), datetime(2016, 2, 2)]
+ x = DataFrame({'months': months})
+
+ def format_func(x):
+ return x.strftime('%Y-%m')
+ result = x.to_html(formatters={'months': format_func})
+ expected = """\
+
+
+
+ |
+ months |
+
+
+
+
+ 0 |
+ 2016-01 |
+
+
+ 1 |
+ 2016-02 |
+
+
+
"""
+ self.assertEqual(result, expected)
+
+ def test_to_html_datetime64_hourformatter(self):
+
+ x = DataFrame({'hod': pd.to_datetime(['10:10:10.100', '12:12:12.120'],
+ format='%H:%M:%S.%f')})
+
+ def format_func(x):
+ return x.strftime('%H:%M')
+ result = x.to_html(formatters={'hod': format_func})
+ expected = """\
+
+
+
+ |
+ hod |
+
+
+
+
+ 0 |
+ 10:10 |
+
+
+ 1 |
+ 12:12 |
+
+
+
"""
+ self.assertEqual(result, expected)
+
def test_to_html_regression_GH6098(self):
df = DataFrame({u('clé1'): [u('a'), u('a'), u('b'), u('b'), u('a')],
u('clé2'): [u('1er'), u('2ème'), u('1er'), u('2ème'),
@@ -2775,6 +2854,33 @@ def test_to_latex_format(self):
self.assertEqual(withindex_result, withindex_expected)
+ def test_to_latex_with_formatters(self):
+ df = DataFrame({'int': [1, 2, 3],
+ 'float': [1.0, 2.0, 3.0],
+ 'object': [(1, 2), True, False],
+ 'datetime64': [datetime(2016, 1, 1),
+ datetime(2016, 2, 5),
+ datetime(2016, 3, 3)]})
+
+ formatters = {'int': lambda x: '0x%x' % x,
+ 'float': lambda x: '[% 4.1f]' % x,
+ 'object': lambda x: '-%s-' % str(x),
+ 'datetime64': lambda x: x.strftime('%Y-%m'),
+ '__index__': lambda x: 'index: %s' % x}
+ result = df.to_latex(formatters=dict(formatters))
+
+ expected = r"""\begin{tabular}{llrrl}
+\toprule
+{} & datetime64 & float & int & object \\
+\midrule
+index: 0 & 2016-01 & [ 1.0] & 0x1 & -(1, 2)- \\
+index: 1 & 2016-02 & [ 2.0] & 0x2 & -True- \\
+index: 2 & 2016-03 & [ 3.0] & 0x3 & -False- \\
+\bottomrule
+\end{tabular}
+"""
+ self.assertEqual(result, expected)
+
def test_to_latex_multiindex(self):
df = DataFrame({('x', 'y'): ['a']})
result = df.to_latex()
@@ -4161,6 +4267,28 @@ def test_dates_display(self):
self.assertEqual(result[1].strip(), "NaT")
self.assertEqual(result[4].strip(), "2013-01-01 09:00:00.000000004")
+ def test_datetime64formatter_yearmonth(self):
+ x = Series([datetime(2016, 1, 1), datetime(2016, 2, 2)])
+
+ def format_func(x):
+ return x.strftime('%Y-%m')
+
+ formatter = fmt.Datetime64Formatter(x, formatter=format_func)
+ result = formatter.get_result()
+ self.assertEqual(result, ['2016-01', '2016-02'])
+
+ def test_datetime64formatter_hoursecond(self):
+
+ x = Series(pd.to_datetime(['10:10:10.100', '12:12:12.120'],
+ format='%H:%M:%S.%f'))
+
+ def format_func(x):
+ return x.strftime('%H:%M')
+
+ formatter = fmt.Datetime64Formatter(x, formatter=format_func)
+ result = formatter.get_result()
+ self.assertEqual(result, ['10:10', '12:12'])
+
class TestNaTFormatting(tm.TestCase):