Repository: pandas-dev/pandas

Issue:
ENH: Use LocaleTime for date_field_name output
Instantiate LocaleTime and use the property .f_weekday to generate weekday and month names.

Currently datename strings are pulled from a hard coded (English) array in get_date_name_field() in tslib.py, better to pull these from a Locale adjusted array.
### Code Sample:

pd.Timestamp("2016-04-06").weekday_name
### Expected Output

e.g. for the a platform with Dutch locale:
'woensdag'



Discussion:

Resolving patch:
diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py
--- a/pandas/core/indexes/datetimes.py
+++ b/pandas/core/indexes/datetimes.py
@@ -231,7 +231,6 @@ class DatetimeIndex(DatelikeOps, TimelikeOps, DatetimeIndexOpsMixin,
     week
     dayofweek
     weekday
-    weekday_name
     quarter
     tz
     freq
@@ -260,6 +259,8 @@ class DatetimeIndex(DatelikeOps, TimelikeOps, DatetimeIndexOpsMixin,
     to_pydatetime
     to_series
     to_frame
+    month_name
+    day_name
 
     Notes
     -----
@@ -318,7 +319,7 @@ def _add_comparison_methods(cls):
     _datetimelike_methods = ['to_period', 'tz_localize',
                              'tz_convert',
                              'normalize', 'strftime', 'round', 'floor',
-                             'ceil']
+                             'ceil', 'month_name', 'day_name']
 
     _is_numeric_dtype = False
     _infer_as_myclass = True
@@ -1713,7 +1714,7 @@ def freq(self, value):
     weekday_name = _field_accessor(
         'weekday_name',
         'weekday_name',
-        "The name of day in a week (ex: Friday)\n\n.. versionadded:: 0.18.1")
+        "The name of day in a week (ex: Friday)\n\n.. deprecated:: 0.23.0")
 
     dayofyear = _field_accessor('dayofyear', 'doy',
                                 "The ordinal day of the year")
@@ -2097,6 +2098,58 @@ def to_julian_date(self):
                              self.nanosecond / 3600.0 / 1e+9
                              ) / 24.0)
 
+    def month_name(self, locale=None):
+        """
+        Return the month names of the DateTimeIndex with specified locale.
+
+        Parameters
+        ----------
+        locale : string, default None (English locale)
+            locale determining the language in which to return the month name
+
+        Returns
+        -------
+        month_names : Index
+            Index of month names
+
+        .. versionadded:: 0.23.0
+        """
+        values = self.asi8
+        if self.tz is not None:
+            if self.tz is not utc:
+                values = self._local_timestamps()
+
+        result = fields.get_date_name_field(values, 'month_name',
+                                            locale=locale)
+        result = self._maybe_mask_results(result)
+        return Index(result, name=self.name)
+
+    def day_name(self, locale=None):
+        """
+        Return the day names of the DateTimeIndex with specified locale.
+
+        Parameters
+        ----------
+        locale : string, default None (English locale)
+            locale determining the language in which to return the day name
+
+        Returns
+        -------
+        month_names : Index
+            Index of day names
+
+        .. versionadded:: 0.23.0
+        """
+        values = self.asi8
+        if self.tz is not None:
+            if self.tz is not utc:
+                values = self._local_timestamps()
+
+        result = fields.get_date_name_field(values, 'day_name',
+                                            locale=locale)
+        result = self._maybe_mask_results(result)
+        return Index(result, name=self.name)
+
 
 DatetimeIndex._add_comparison_methods()
 DatetimeIndex._add_numeric_methods_disabled()


Test patch:
diff --git a/pandas/tests/indexes/datetimes/test_misc.py b/pandas/tests/indexes/datetimes/test_misc.py
--- a/pandas/tests/indexes/datetimes/test_misc.py
+++ b/pandas/tests/indexes/datetimes/test_misc.py
@@ -1,3 +1,6 @@
+import locale
+import calendar
+
 import pytest
 
 import numpy as np
@@ -87,7 +90,6 @@ def test_range_edges(self):
 class TestDatetime64(object):
 
     def test_datetimeindex_accessors(self):
-
         dti_naive = DatetimeIndex(freq='D', start=datetime(1998, 1, 1),
                                   periods=365)
         # GH 13303
@@ -134,23 +136,6 @@ def test_datetimeindex_accessors(self):
             assert not dti.is_year_end[0]
             assert dti.is_year_end[364]
 
-            # GH 11128
-            assert dti.weekday_name[4] == u'Monday'
-            assert dti.weekday_name[5] == u'Tuesday'
-            assert dti.weekday_name[6] == u'Wednesday'
-            assert dti.weekday_name[7] == u'Thursday'
-            assert dti.weekday_name[8] == u'Friday'
-            assert dti.weekday_name[9] == u'Saturday'
-            assert dti.weekday_name[10] == u'Sunday'
-
-            assert Timestamp('2016-04-04').weekday_name == u'Monday'
-            assert Timestamp('2016-04-05').weekday_name == u'Tuesday'
-            assert Timestamp('2016-04-06').weekday_name == u'Wednesday'
-            assert Timestamp('2016-04-07').weekday_name == u'Thursday'
-            assert Timestamp('2016-04-08').weekday_name == u'Friday'
-            assert Timestamp('2016-04-09').weekday_name == u'Saturday'
-            assert Timestamp('2016-04-10').weekday_name == u'Sunday'
-
             assert len(dti.year) == 365
             assert len(dti.month) == 365
             assert len(dti.day) == 365
@@ -256,6 +241,54 @@ def test_datetimeindex_accessors(self):
         assert dates.weekofyear.tolist() == expected
         assert [d.weekofyear for d in dates] == expected
 
+    # GH 12806
+    @pytest.mark.parametrize('time_locale', [
+        None] if tm.get_locales() is None else [None] + tm.get_locales())
+    def test_datetime_name_accessors(self, time_locale):
+        # Test Monday -> Sunday and January -> December, in that sequence
+        if time_locale is None:
+            # If the time_locale is None, day-name and month_name should
+            # return the english attributes
+            expected_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
+                             'Friday', 'Saturday', 'Sunday']
+            expected_months = ['January', 'February', 'March', 'April', 'May',
+                               'June', 'July', 'August', 'September',
+                               'October', 'November', 'December']
+        else:
+            with tm.set_locale(time_locale, locale.LC_TIME):
+                expected_days = calendar.day_name[:]
+                expected_months = calendar.month_name[1:]
+
+        # GH 11128
+        dti = DatetimeIndex(freq='D', start=datetime(1998, 1, 1),
+                            periods=365)
+        english_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
+                        'Friday', 'Saturday', 'Sunday']
+        for day, name, eng_name in zip(range(4, 11),
+                                       expected_days,
+                                       english_days):
+            name = name.capitalize()
+            assert dti.weekday_name[day] == eng_name
+            assert dti.day_name(locale=time_locale)[day] == name
+            ts = Timestamp(datetime(2016, 4, day))
+            assert ts.weekday_name == eng_name
+            assert ts.day_name(locale=time_locale) == name
+        dti = dti.append(DatetimeIndex([pd.NaT]))
+        assert np.isnan(dti.day_name(locale=time_locale)[-1])
+        ts = Timestamp(pd.NaT)
+        assert np.isnan(ts.day_name(locale=time_locale))
+
+        # GH 12805
+        dti = DatetimeIndex(freq='M', start='2012', end='2013')
+        result = dti.month_name(locale=time_locale)
+        expected = Index([month.capitalize() for month in expected_months])
+        tm.assert_index_equal(result, expected)
+        for date, expected in zip(dti, expected_months):
+            result = date.month_name(locale=time_locale)
+            assert result == expected.capitalize()
+        dti = dti.append(DatetimeIndex([pd.NaT]))
+        assert np.isnan(dti.month_name(locale=time_locale)[-1])
+
     def test_nanosecond_field(self):
         dti = DatetimeIndex(np.arange(10))
 
diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py
--- a/pandas/tests/scalar/timestamp/test_timestamp.py
+++ b/pandas/tests/scalar/timestamp/test_timestamp.py
@@ -4,6 +4,7 @@
 import pytest
 import dateutil
 import calendar
+import locale
 import numpy as np
 
 from dateutil.tz import tzutc
@@ -21,7 +22,7 @@
 from pandas.errors import OutOfBoundsDatetime
 from pandas.compat import long, PY3
 from pandas.compat.numpy import np_datetime64_compat
-from pandas import Timestamp, Period, Timedelta
+from pandas import Timestamp, Period, Timedelta, NaT
 
 
 class TestTimestampProperties(object):
@@ -95,13 +96,33 @@ def check(value, equal):
         for end in ends:
             assert getattr(ts, end)
 
-    @pytest.mark.parametrize('data, expected',
-                             [(Timestamp('2017-08-28 23:00:00'), 'Monday'),
-                              (Timestamp('2017-08-28 23:00:00', tz='EST'),
-                               'Monday')])
-    def test_weekday_name(self, data, expected):
+    # GH 12806
+    @pytest.mark.parametrize('data',
+                             [Timestamp('2017-08-28 23:00:00'),
+                              Timestamp('2017-08-28 23:00:00', tz='EST')])
+    @pytest.mark.parametrize('time_locale', [
+        None] if tm.get_locales() is None else [None] + tm.get_locales())
+    def test_names(self, data, time_locale):
         # GH 17354
-        assert data.weekday_name == expected
+        # Test .weekday_name, .day_name(), .month_name
+        with tm.assert_produces_warning(DeprecationWarning,
+                                        check_stacklevel=False):
+            assert data.weekday_name == 'Monday'
+        if time_locale is None:
+            expected_day = 'Monday'
+            expected_month = 'August'
+        else:
+            with tm.set_locale(time_locale, locale.LC_TIME):
+                expected_day = calendar.day_name[0].capitalize()
+                expected_month = calendar.month_name[8].capitalize()
+
+        assert data.day_name(time_locale) == expected_day
+        assert data.month_name(time_locale) == expected_month
+
+        # Test NaT
+        nan_ts = Timestamp(NaT)
+        assert np.isnan(nan_ts.day_name(time_locale))
+        assert np.isnan(nan_ts.month_name(time_locale))
 
     @pytest.mark.parametrize('tz', [None, 'UTC', 'US/Eastern', 'Asia/Tokyo'])
     def test_is_leap_year(self, tz):
diff --git a/pandas/tests/series/test_datetime_values.py b/pandas/tests/series/test_datetime_values.py
--- a/pandas/tests/series/test_datetime_values.py
+++ b/pandas/tests/series/test_datetime_values.py
@@ -1,6 +1,8 @@
 # coding=utf-8
 # pylint: disable-msg=E1101,W0612
 
+import locale
+import calendar
 import pytest
 
 from datetime import datetime, date
@@ -32,7 +34,7 @@ def test_dt_namespace_accessor(self):
         ok_for_dt = DatetimeIndex._datetimelike_ops
         ok_for_dt_methods = ['to_period', 'to_pydatetime', 'tz_localize',
                              'tz_convert', 'normalize', 'strftime', 'round',
-                             'floor', 'ceil', 'weekday_name']
+                             'floor', 'ceil', 'day_name', 'month_name']
         ok_for_td = TimedeltaIndex._datetimelike_ops
         ok_for_td_methods = ['components', 'to_pytimedelta', 'total_seconds',
                              'round', 'floor', 'ceil']
@@ -274,6 +276,46 @@ def test_dt_accessor_no_new_attributes(self):
                                     "You cannot add any new attribute"):
             s.dt.xlabel = "a"
 
+    @pytest.mark.parametrize('time_locale', [
+        None] if tm.get_locales() is None else [None] + tm.get_locales())
+    def test_dt_accessor_datetime_name_accessors(self, time_locale):
+        # Test Monday -> Sunday and January -> December, in that sequence
+        if time_locale is None:
+            # If the time_locale is None, day-name and month_name should
+            # return the english attributes
+            expected_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
+                             'Friday', 'Saturday', 'Sunday']
+            expected_months = ['January', 'February', 'March', 'April', 'May',
+                               'June', 'July', 'August', 'September',
+                               'October', 'November', 'December']
+        else:
+            with tm.set_locale(time_locale, locale.LC_TIME):
+                expected_days = calendar.day_name[:]
+                expected_months = calendar.month_name[1:]
+
+        s = Series(DatetimeIndex(freq='D', start=datetime(1998, 1, 1),
+                                 periods=365))
+        english_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
+                        'Friday', 'Saturday', 'Sunday']
+        for day, name, eng_name in zip(range(4, 11),
+                                       expected_days,
+                                       english_days):
+            name = name.capitalize()
+            assert s.dt.weekday_name[day] == eng_name
+            assert s.dt.day_name(locale=time_locale)[day] == name
+        s = s.append(Series([pd.NaT]))
+        assert np.isnan(s.dt.day_name(locale=time_locale).iloc[-1])
+
+        s = Series(DatetimeIndex(freq='M', start='2012', end='2013'))
+        result = s.dt.month_name(locale=time_locale)
+        expected = Series([month.capitalize() for month in expected_months])
+        tm.assert_series_equal(result, expected)
+        for s_date, expected in zip(s, expected_months):
+            result = s_date.month_name(locale=time_locale)
+            assert result == expected.capitalize()
+        s = s.append(Series([pd.NaT]))
+        assert np.isnan(s.dt.month_name(locale=time_locale).iloc[-1])
+
     def test_strftime(self):
         # GH 10086
         s = Series(date_range('20130101', periods=5))