Skip to content

Commit c4837dc

Browse files
committed
Add month names
Fix localiztion of months Add series test and whatsnew notes Modify test and lint Add Timestamp rst Address comments Lint, move whatsnew, remove hardcoded Monday, check that get_locale is None
1 parent 3493aba commit c4837dc

File tree

9 files changed

+77
-37
lines changed

9 files changed

+77
-37
lines changed

doc/source/api.rst

+3
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ These can be accessed like ``Series.dt.<property>``.
497497
Series.dt.time
498498
Series.dt.year
499499
Series.dt.month
500+
Series.dt.month_name
500501
Series.dt.day
501502
Series.dt.hour
502503
Series.dt.minute
@@ -1537,6 +1538,7 @@ Time/Date Components
15371538

15381539
DatetimeIndex.year
15391540
DatetimeIndex.month
1541+
DatetimeIndex.month_name
15401542
DatetimeIndex.day
15411543
DatetimeIndex.hour
15421544
DatetimeIndex.minute
@@ -1757,6 +1759,7 @@ Properties
17571759
Timestamp.microsecond
17581760
Timestamp.min
17591761
Timestamp.month
1762+
Timestamp.month_name
17601763
Timestamp.nanosecond
17611764
Timestamp.quarter
17621765
Timestamp.resolution

doc/source/whatsnew/v0.22.0.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Other Enhancements
2424

2525
- Better support for :func:`Dataframe.style.to_excel` output with the ``xlsxwriter`` engine. (:issue:`16149`)
2626
- :func:`pandas.tseries.frequencies.to_offset` now accepts leading '+' signs e.g. '+1h'. (:issue:`18171`)
27+
- :attr:`Timestamp.month_name`, :attr:`DatetimeIndex.month_name`, and :attr:`Series.dt.month_name` are now available (:issue:`12805`)
2728
-
2829

2930
.. _whatsnew_0220.api_breaking:
@@ -91,7 +92,7 @@ Bug Fixes
9192
Conversion
9293
^^^^^^^^^^
9394

94-
-
95+
- Bug in :attr:`Timestamp.weekday_name` and :attr:`DatetimeIndex.weekday_name` not returning locale aware values (:issue:`12806`)
9596
-
9697
-
9798

pandas/_libs/tslib.pyx

+10-4
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ from khash cimport (
6868
kh_resize_int64, kh_get_int64)
6969

7070
from .tslibs.parsing import parse_datetime_string
71+
from .tslibs.strptime import LocaleTime
7172

7273
cimport cython
7374

@@ -533,10 +534,15 @@ class Timestamp(_Timestamp):
533534

534535
@property
535536
def weekday_name(self):
536-
cdef dict wdays = {0: 'Monday', 1: 'Tuesday', 2: 'Wednesday',
537-
3: 'Thursday', 4: 'Friday', 5: 'Saturday',
538-
6: 'Sunday'}
539-
return wdays[self.weekday()]
537+
cdef object locale_time = LocaleTime()
538+
cdef dict wdays = dict(enumerate(locale_time.f_weekday))
539+
return wdays[self.weekday()].capitalize()
540+
541+
@property
542+
def month_name(self):
543+
cdef object locale_time = LocaleTime()
544+
cdef dict months = dict(enumerate(locale_time.f_month))
545+
return months[self.month].capitalize()
540546

541547
@property
542548
def dayofyear(self):

pandas/_libs/tslibs/fields.pyx

+15-6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ cimport util
2828

2929
cdef int64_t NPY_NAT = util.get_nat()
3030

31+
from pandas._libs.tslibs.strptime import LocaleTime
32+
3133

3234
def build_field_sarray(ndarray[int64_t] dtindex):
3335
"""
@@ -83,24 +85,31 @@ def get_date_name_field(ndarray[int64_t] dtindex, object field):
8385
ndarray[object] out
8486
pandas_datetimestruct dts
8587
int dow
86-
87-
_dayname = np.array(
88-
['Monday', 'Tuesday', 'Wednesday', 'Thursday',
89-
'Friday', 'Saturday', 'Sunday'],
90-
dtype=np.object_)
88+
object locale_time = LocaleTime()
9189

9290
count = len(dtindex)
9391
out = np.empty(count, dtype=object)
9492

9593
if field == 'weekday_name':
94+
_dayname = np.array(locale_time.f_weekday, dtype=np.object_)
9695
for i in range(count):
9796
if dtindex[i] == NPY_NAT:
9897
out[i] = np.nan
9998
continue
10099

101100
dt64_to_dtstruct(dtindex[i], &dts)
102101
dow = dayofweek(dts.year, dts.month, dts.day)
103-
out[i] = _dayname[dow]
102+
out[i] = _dayname[dow].capitalize()
103+
return out
104+
elif field == 'month_name':
105+
_monthname = np.array(locale_time.f_month, dtype=np.object_)
106+
for i in range(count):
107+
if dtindex[i] == NPY_NAT:
108+
out[i] = np.nan
109+
continue
110+
111+
dt64_to_dtstruct(dtindex[i], &dts)
112+
out[i] = _monthname[dts.month].capitalize()
104113
return out
105114

106115
raise ValueError("Field %s not supported" % field)

pandas/_libs/tslibs/nattype.pyx

+1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ class NaTType(_NaT):
301301
daysinmonth = property(fget=lambda self: np.nan)
302302
dayofweek = property(fget=lambda self: np.nan)
303303
weekday_name = property(fget=lambda self: np.nan)
304+
month_name = property(fget=lambda self: np.nan)
304305

305306
# inject Timedelta properties
306307
days = property(fget=lambda self: np.nan)

pandas/core/indexes/datetimes.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def _join_i8_wrapper(joinf, **kwargs):
248248
_bool_ops = ['is_month_start', 'is_month_end',
249249
'is_quarter_start', 'is_quarter_end', 'is_year_start',
250250
'is_year_end', 'is_leap_year']
251-
_object_ops = ['weekday_name', 'freq', 'tz']
251+
_object_ops = ['weekday_name', 'month_name', 'freq', 'tz']
252252
_field_ops = ['year', 'month', 'day', 'hour', 'minute', 'second',
253253
'weekofyear', 'week', 'weekday', 'dayofweek',
254254
'dayofyear', 'quarter', 'days_in_month',
@@ -1581,6 +1581,11 @@ def _set_freq(self, value):
15811581
'weekday_name',
15821582
"The name of day in a week (ex: Friday)\n\n.. versionadded:: 0.18.1")
15831583

1584+
month_name = _field_accessor(
1585+
'month_name',
1586+
'month_name',
1587+
"The name of month in a year (ex: May)\n\n.. versionadded:: 0.21.1")
1588+
15841589
dayofyear = _field_accessor('dayofyear', 'doy',
15851590
"The ordinal day of the year")
15861591
quarter = _field_accessor('quarter', 'q', "The quarter of the date")

pandas/tests/indexes/datetimes/test_misc.py

+28-18
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import locale
2+
import calendar
3+
14
import pytest
25

36
import numpy as np
@@ -173,7 +176,6 @@ def test_normalize(self):
173176
class TestDatetime64(object):
174177

175178
def test_datetimeindex_accessors(self):
176-
177179
dti_naive = DatetimeIndex(freq='D', start=datetime(1998, 1, 1),
178180
periods=365)
179181
# GH 13303
@@ -220,23 +222,6 @@ def test_datetimeindex_accessors(self):
220222
assert not dti.is_year_end[0]
221223
assert dti.is_year_end[364]
222224

223-
# GH 11128
224-
assert dti.weekday_name[4] == u'Monday'
225-
assert dti.weekday_name[5] == u'Tuesday'
226-
assert dti.weekday_name[6] == u'Wednesday'
227-
assert dti.weekday_name[7] == u'Thursday'
228-
assert dti.weekday_name[8] == u'Friday'
229-
assert dti.weekday_name[9] == u'Saturday'
230-
assert dti.weekday_name[10] == u'Sunday'
231-
232-
assert Timestamp('2016-04-04').weekday_name == u'Monday'
233-
assert Timestamp('2016-04-05').weekday_name == u'Tuesday'
234-
assert Timestamp('2016-04-06').weekday_name == u'Wednesday'
235-
assert Timestamp('2016-04-07').weekday_name == u'Thursday'
236-
assert Timestamp('2016-04-08').weekday_name == u'Friday'
237-
assert Timestamp('2016-04-09').weekday_name == u'Saturday'
238-
assert Timestamp('2016-04-10').weekday_name == u'Sunday'
239-
240225
assert len(dti.year) == 365
241226
assert len(dti.month) == 365
242227
assert len(dti.day) == 365
@@ -342,6 +327,31 @@ def test_datetimeindex_accessors(self):
342327
assert dates.weekofyear.tolist() == expected
343328
assert [d.weekofyear for d in dates] == expected
344329

330+
# GH 12806
331+
@pytest.mark.skipif(tm.get_locales() is None or len(tm.get_locales()) == 0,
332+
reason='No available locales')
333+
@pytest.mark.parametrize('time_locale', tm.get_locales())
334+
def test_datetime_name_accessors(self, time_locale):
335+
with tm.set_locale(time_locale, locale.LC_TIME):
336+
# GH 11128
337+
dti = DatetimeIndex(freq='D', start=datetime(1998, 1, 1),
338+
periods=365)
339+
for day, name in zip(range(4, 11), calendar.day_name):
340+
# Test Monday -> Sunday
341+
assert dti.weekday_name[day] == name.capitalize()
342+
date = datetime(2016, 4, day)
343+
assert Timestamp(date).weekday_name == name.capitalize()
344+
345+
# GH 12805
346+
dti = DatetimeIndex(freq='M', start='2012', end='2013')
347+
# Test January -> December
348+
result = dti.month_name
349+
expected = Index([month.capitalize()
350+
for month in calendar.month_name[1:]])
351+
tm.assert_index_equal(result, expected)
352+
for date, result in zip(dti, calendar.month_name[1:]):
353+
assert date.month_name == result.capitalize()
354+
345355
def test_nanosecond_field(self):
346356
dti = DatetimeIndex(np.arange(10))
347357

pandas/tests/scalar/test_timestamp.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
""" test the scalar Timestamp """
22

33
import sys
4+
import locale
45
import pytz
56
import pytest
67
import dateutil
@@ -597,13 +598,17 @@ def check(value, equal):
597598
for end in ends:
598599
assert getattr(ts, end)
599600

600-
@pytest.mark.parametrize('data, expected',
601-
[(Timestamp('2017-08-28 23:00:00'), 'Monday'),
602-
(Timestamp('2017-08-28 23:00:00', tz='EST'),
603-
'Monday')])
604-
def test_weekday_name(self, data, expected):
601+
# GH 12806
602+
@pytest.mark.skipif(tm.get_locales() is None or len(tm.get_locales()) == 0,
603+
reason='No available locales')
604+
@pytest.mark.parametrize('data',
605+
[Timestamp('2017-08-28 23:00:00'),
606+
Timestamp('2017-08-28 23:00:00', tz='EST')])
607+
@pytest.mark.parametrize('time_locale', tm.get_locales())
608+
def test_weekday_name(self, data, time_locale):
605609
# GH 17354
606-
assert data.weekday_name == expected
610+
with tm.set_locale(time_locale, locale.LC_TIME):
611+
assert data.weekday_name == calendar.day_name[0].capitalize()
607612

608613
def test_pprint(self):
609614
# GH12622

pandas/tests/series/test_datetime_values.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def test_dt_namespace_accessor(self):
3232
ok_for_dt = DatetimeIndex._datetimelike_ops
3333
ok_for_dt_methods = ['to_period', 'to_pydatetime', 'tz_localize',
3434
'tz_convert', 'normalize', 'strftime', 'round',
35-
'floor', 'ceil', 'weekday_name']
35+
'floor', 'ceil', 'weekday_name', 'month_name']
3636
ok_for_td = TimedeltaIndex._datetimelike_ops
3737
ok_for_td_methods = ['components', 'to_pytimedelta', 'total_seconds',
3838
'round', 'floor', 'ceil']

0 commit comments

Comments
 (0)