Skip to content

Commit e10b1c9

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 create functions instead of attributes Fix white space Modify tests Lint
1 parent 78c3ff9 commit e10b1c9

File tree

6 files changed

+179
-31
lines changed

6 files changed

+179
-31
lines changed

doc/source/api.rst

+6
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,8 @@ These can be accessed like ``Series.dt.<property>``.
582582
Series.dt.round
583583
Series.dt.floor
584584
Series.dt.ceil
585+
Series.dt.month_name
586+
Series.dt.day_name
585587

586588
**Timedelta Properties**
587589

@@ -1751,6 +1753,8 @@ Time-specific operations
17511753
DatetimeIndex.round
17521754
DatetimeIndex.floor
17531755
DatetimeIndex.ceil
1756+
DatetimeIndex.month_name
1757+
DatetimeIndex.day_name
17541758

17551759
Conversion
17561760
~~~~~~~~~~
@@ -1946,6 +1950,7 @@ Methods
19461950
Timestamp.combine
19471951
Timestamp.ctime
19481952
Timestamp.date
1953+
Timestamp.day_name
19491954
Timestamp.dst
19501955
Timestamp.floor
19511956
Timestamp.freq
@@ -1955,6 +1960,7 @@ Methods
19551960
Timestamp.isocalendar
19561961
Timestamp.isoformat
19571962
Timestamp.isoweekday
1963+
Timestamp.month_name
19581964
Timestamp.normalize
19591965
Timestamp.now
19601966
Timestamp.replace

pandas/_libs/tslibs/fields.pyx

+44-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ from np_datetime cimport (pandas_datetimestruct, pandas_timedeltastruct,
2020
dt64_to_dtstruct, td64_to_tdstruct)
2121
from nattype cimport NPY_NAT
2222

23+
from pandas._libs.tslibs.strptime import LocaleTime
24+
25+
import locale
26+
from pandas.util.testing import set_locale
2327

2428
def get_time_micros(ndarray[int64_t] dtindex):
2529
"""
@@ -85,7 +89,8 @@ def build_field_sarray(ndarray[int64_t] dtindex):
8589

8690
@cython.wraparound(False)
8791
@cython.boundscheck(False)
88-
def get_date_name_field(ndarray[int64_t] dtindex, object field):
92+
def get_date_name_field(ndarray[int64_t] dtindex, object field,
93+
object time_locale=None):
8994
"""
9095
Given a int64-based datetime index, return array of strings of date
9196
name based on requested field (e.g. weekday_name)
@@ -95,16 +100,15 @@ def get_date_name_field(ndarray[int64_t] dtindex, object field):
95100
ndarray[object] out
96101
pandas_datetimestruct dts
97102
int dow
98-
99-
_dayname = np.array(
100-
['Monday', 'Tuesday', 'Wednesday', 'Thursday',
101-
'Friday', 'Saturday', 'Sunday'],
102-
dtype=np.object_)
103+
object locale_time = LocaleTime()
103104

104105
count = len(dtindex)
105106
out = np.empty(count, dtype=object)
106107

107108
if field == 'weekday_name':
109+
_dayname = np.array(['Monday', 'Tuesday', 'Wednesday', 'Thursday',
110+
'Friday', 'Saturday', 'Sunday'],
111+
dtype=np.object_)
108112
for i in range(count):
109113
if dtindex[i] == NPY_NAT:
110114
out[i] = np.nan
@@ -113,6 +117,40 @@ def get_date_name_field(ndarray[int64_t] dtindex, object field):
113117
dt64_to_dtstruct(dtindex[i], &dts)
114118
dow = dayofweek(dts.year, dts.month, dts.day)
115119
out[i] = _dayname[dow]
120+
if field == 'day_name':
121+
if time_locale is None:
122+
_dayname = np.array(['monday', 'tuesday', 'wednesday', 'thursday',
123+
'friday', 'saturday', 'sunday'],
124+
dtype=np.object_)
125+
else:
126+
with set_locale(time_locale, locale.LC_TIME):
127+
locale_time = LocaleTime()
128+
_dayname = np.array(locale_time.f_weekday, dtype=np.object_)
129+
for i in range(count):
130+
if dtindex[i] == NPY_NAT:
131+
out[i] = np.nan
132+
continue
133+
134+
dt64_to_dtstruct(dtindex[i], &dts)
135+
dow = dayofweek(dts.year, dts.month, dts.day)
136+
out[i] = _dayname[dow].capitalize()
137+
return out
138+
elif field == 'month_name':
139+
if time_locale is None:
140+
_monthname = np.array(['', 'monday', 'tuesday', 'wednesday',
141+
'thursday', 'friday', 'saturday', 'sunday'],
142+
dtype=np.object_)
143+
else:
144+
with set_locale(time_locale, locale.LC_TIME):
145+
locale_time = LocaleTime()
146+
_monthname = np.array(locale_time.f_month, dtype=np.object_)
147+
for i in range(count):
148+
if dtindex[i] == NPY_NAT:
149+
out[i] = np.nan
150+
continue
151+
152+
dt64_to_dtstruct(dtindex[i], &dts)
153+
out[i] = _monthname[dts.month].capitalize()
116154
return out
117155

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

pandas/_libs/tslibs/nattype.pyx

+31-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,13 @@ _nat_scalar_rules[Py_GE] = False
4242
def _make_nan_func(func_name, cls):
4343
def f(*args, **kwargs):
4444
return np.nan
45+
4546
f.__name__ = func_name
46-
f.__doc__ = getattr(cls, func_name).__doc__
47+
if isinstance(cls, str):
48+
# passed the literal docstring directly
49+
f.__doc__ = cls
50+
else:
51+
f.__doc__ = getattr(cls, func_name).__doc__
4752
return f
4853

4954

@@ -320,7 +325,32 @@ class NaTType(_NaT):
320325
# nan methods
321326
weekday = _make_nan_func('weekday', datetime)
322327
isoweekday = _make_nan_func('isoweekday', datetime)
328+
month_name = _make_nan_func('month_name', # noqa:E128
329+
"""
330+
Return the month name of the Timestamp with specified locale.
323331
332+
Parameters
333+
----------
334+
time_locale : string, default None (English locale)
335+
locale determining the language in which to return the month name
336+
337+
Returns
338+
-------
339+
month_name : string
340+
""")
341+
day_name = _make_nan_func('day_name', # noqa:E128
342+
"""
343+
Return the day name of the Timestamp with specified locale.
344+
345+
Parameters
346+
----------
347+
time_locale : string, default None (English locale)
348+
locale determining the language in which to return the day name
349+
350+
Returns
351+
-------
352+
day_name : string
353+
""")
324354
# _nat_methods
325355
date = _make_nat_func('date', datetime)
326356

pandas/core/indexes/datetimes.py

+50
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ class DatetimeIndex(DatelikeOps, TimelikeOps, DatetimeIndexOpsMixin,
258258
to_pydatetime
259259
to_series
260260
to_frame
261+
month_name
262+
day_name
261263
262264
Notes
263265
-----
@@ -2070,6 +2072,54 @@ def to_julian_date(self):
20702072
self.nanosecond / 3600.0 / 1e+9
20712073
) / 24.0)
20722074

2075+
def month_name(self, time_locale=None):
2076+
"""
2077+
Return the month names of the DateTimeIndex with specified locale.
2078+
2079+
Parameters
2080+
----------
2081+
time_locale : string, default None (English locale)
2082+
locale determining the language in which to return the month name
2083+
2084+
Returns
2085+
-------
2086+
month_names : Index
2087+
Index of month names
2088+
"""
2089+
values = self.asi8
2090+
if self.tz is not None:
2091+
if self.tz is not utc:
2092+
values = self._local_timestamps()
2093+
2094+
result = fields.get_date_name_field(values, 'month_name',
2095+
time_locale=time_locale)
2096+
result = self._maybe_mask_results(result)
2097+
return Index(result, name=self.name)
2098+
2099+
def day_name(self, time_locale=None):
2100+
"""
2101+
Return the day names of the DateTimeIndex with specified locale.
2102+
2103+
Parameters
2104+
----------
2105+
time_locale : string, default None (English locale)
2106+
locale determining the language in which to return the day name
2107+
2108+
Returns
2109+
-------
2110+
month_names : Index
2111+
Index of day names
2112+
"""
2113+
values = self.asi8
2114+
if self.tz is not None:
2115+
if self.tz is not utc:
2116+
values = self._local_timestamps()
2117+
2118+
result = fields.get_date_name_field(values, 'day_name',
2119+
time_locale=time_locale)
2120+
result = self._maybe_mask_results(result)
2121+
return Index(result, name=self.name)
2122+
20732123

20742124
DatetimeIndex._add_comparison_methods()
20752125
DatetimeIndex._add_numeric_methods_disabled()

pandas/tests/indexes/datetimes/test_misc.py

+34-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,37 @@ 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(not tm.get_locales(), reason='No available locales')
332+
@pytest.mark.parametrize('time_locale', tm.get_locales())
333+
def test_datetime_name_accessors(self, time_locale):
334+
with tm.set_locale(time_locale, locale.LC_TIME):
335+
# GH 11128
336+
dti = DatetimeIndex(freq='D', start=datetime(1998, 1, 1),
337+
periods=365)
338+
english_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
339+
'Friday', 'Saturday', 'Sunday']
340+
for day, name, eng_name in zip(range(4, 11),
341+
calendar.day_name,
342+
english_days):
343+
# Test Monday -> Sunday
344+
assert dti.weekday_name[day] == eng_name
345+
assert dti.day_name(time_locale=time_locale) == name
346+
ts = Timestamp(datetime(2016, 4, day))
347+
assert ts.weekday_name == eng_name
348+
assert ts.day_name(time_locale=time_locale) == name
349+
350+
# GH 12805
351+
dti = DatetimeIndex(freq='M', start='2012', end='2013')
352+
# Test January -> December
353+
result = dti.month_name
354+
expected = Index([month.capitalize()
355+
for month in calendar.month_name[1:]])
356+
tm.assert_index_equal(result, expected)
357+
for date, expected in zip(dti, calendar.month_name[1:]):
358+
result = date.month_name(time_locale=time_locale)
359+
assert result == expected.capitalize()
360+
345361
def test_nanosecond_field(self):
346362
dti = DatetimeIndex(np.arange(10))
347363

pandas/tests/scalar/test_timestamp.py

+14-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
@@ -619,13 +620,20 @@ def check(value, equal):
619620
for end in ends:
620621
assert getattr(ts, end)
621622

622-
@pytest.mark.parametrize('data, expected',
623-
[(Timestamp('2017-08-28 23:00:00'), 'Monday'),
624-
(Timestamp('2017-08-28 23:00:00', tz='EST'),
625-
'Monday')])
626-
def test_weekday_name(self, data, expected):
623+
# GH 12806
624+
@pytest.mark.skipif(not tm.get_locales(), reason='No available locales')
625+
@pytest.mark.parametrize('data',
626+
[Timestamp('2017-08-28 23:00:00'),
627+
Timestamp('2017-08-28 23:00:00', tz='EST')])
628+
@pytest.mark.parametrize('time_locale', tm.get_locales())
629+
def test_day_name(self, data, time_locale):
627630
# GH 17354
628-
assert data.weekday_name == expected
631+
# Test .weekday_name and .day_name()
632+
assert data.weekday_name == 'Monday'
633+
with tm.set_locale(time_locale, locale.LC_TIME):
634+
expected = calendar.day_name[0].capitalize()
635+
result = data.day_name(time_locale)
636+
assert result == expected
629637

630638
def test_pprint(self):
631639
# GH12622

0 commit comments

Comments
 (0)