Skip to content

Commit 05dc7c1

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 Rebase and add depreciation message Deprecate weekday_name, create localizing function in ccalendar.pyx clean up imports Address user comments Try to address ci issues
1 parent 8f1dfa7 commit 05dc7c1

File tree

8 files changed

+242
-40
lines changed

8 files changed

+242
-40
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

@@ -1760,6 +1762,8 @@ Time-specific operations
17601762
DatetimeIndex.round
17611763
DatetimeIndex.floor
17621764
DatetimeIndex.ceil
1765+
DatetimeIndex.month_name
1766+
DatetimeIndex.day_name
17631767

17641768
Conversion
17651769
~~~~~~~~~~
@@ -1955,6 +1959,7 @@ Methods
19551959
Timestamp.combine
19561960
Timestamp.ctime
19571961
Timestamp.date
1962+
Timestamp.day_name
19581963
Timestamp.dst
19591964
Timestamp.floor
19601965
Timestamp.freq
@@ -1964,6 +1969,7 @@ Methods
19641969
Timestamp.isocalendar
19651970
Timestamp.isoformat
19661971
Timestamp.isoweekday
1972+
Timestamp.month_name
19671973
Timestamp.normalize
19681974
Timestamp.now
19691975
Timestamp.replace

doc/source/whatsnew/v0.23.0.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ Other Enhancements
306306
- Added :func:`SeriesGroupBy.is_monotonic_increasing` and :func:`SeriesGroupBy.is_monotonic_decreasing` (:issue:`17015`)
307307
- For subclassed ``DataFrames``, :func:`DataFrame.apply` will now preserve the ``Series`` subclass (if defined) when passing the data to the applied function (:issue:`19822`)
308308
- :func:`DataFrame.from_dict` now accepts a ``columns`` argument that can be used to specify the column names when ``orient='index'`` is used (:issue:`18529`)
309-
309+
- :meth:`Timestamp.month_name`, :meth:`DatetimeIndex.month_name`, and :meth:`Series.dt.month_name` are now available (:issue:`12805`)
310+
- :meth:`Timestamp.day_name` and :meth:`DatetimeIndex.day_name` are now available to return day names with a specified locale (:issue:`12806`)
310311

311312
.. _whatsnew_0230.api_breaking:
312313

@@ -641,6 +642,7 @@ Deprecations
641642
- The ``broadcast`` parameter of ``.apply()`` is deprecated in favor of ``result_type='broadcast'`` (:issue:`18577`)
642643
- The ``reduce`` parameter of ``.apply()`` is deprecated in favor of ``result_type='reduce'`` (:issue:`18577`)
643644
- The ``order`` parameter of :func:`factorize` is deprecated and will be removed in a future release (:issue:`19727`)
645+
- :attr:`Timestamp.weekday_name`, :attr:`DatetimeIndex.weekday_name`, and :attr:`Series.dt.weekday_name` are deprecated in favor of :meth:`Timestamp.day_name`, :meth:`DatetimeIndex.day_name`, and :meth:`Series.dt.day_name` (:issue:`12806`)
644646

645647
.. _whatsnew_0230.prior_deprecations:
646648

pandas/_libs/tslibs/ccalendar.pyx

+32
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,18 @@ cdef int32_t* _month_offset = [
3535
# Canonical location for other modules to find name constants
3636
MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL',
3737
'AUG', 'SEP', 'OCT', 'NOV', 'DEC']
38+
# The first blank line is consistent with calendar.month_name in the calendar
39+
# standard library
40+
MONTHS_FULL = ['', 'January', 'February', 'March', 'April', 'May', 'June',
41+
'July', 'August', 'September', 'October', 'November',
42+
'December']
3843
MONTH_NUMBERS = {name: num for num, name in enumerate(MONTHS)}
3944
MONTH_ALIASES = {(num + 1): name for num, name in enumerate(MONTHS)}
4045
MONTH_TO_CAL_NUM = {name: num + 1 for num, name in enumerate(MONTHS)}
4146

4247
DAYS = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN']
48+
DAYS_FULL = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
49+
'Saturday', 'Sunday']
4350
int_to_weekday = {num: name for num, name in enumerate(DAYS)}
4451
weekday_to_int = {int_to_weekday[key]: key for key in int_to_weekday}
4552

@@ -199,3 +206,28 @@ cpdef int32_t get_day_of_year(int year, int month, int day) nogil:
199206

200207
day_of_year = mo_off + day
201208
return day_of_year
209+
210+
211+
cpdef get_locale_names(object name_type, object time_locale=None):
212+
"""Returns an array of localized day or month names
213+
214+
Parameters
215+
----------
216+
name_type : string, attribute of LocaleTime() in which to return localized
217+
names
218+
locale : string
219+
220+
Returns
221+
-------
222+
names : list
223+
224+
"""
225+
cdef:
226+
list locale_names
227+
228+
import locale
229+
from pandas.util.testing import set_locale
230+
from strptime import LocaleTime
231+
with set_locale(time_locale, locale.LC_TIME):
232+
locale_names = getattr(LocaleTime(), name_type)
233+
return locale_names

pandas/_libs/tslibs/fields.pyx

+33-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ cimport numpy as cnp
1313
from numpy cimport ndarray, int64_t, int32_t, int8_t
1414
cnp.import_array()
1515

16-
16+
from ccalendar import get_locale_names, MONTHS_FULL, DAYS_FULL
1717
from ccalendar cimport (get_days_in_month, is_leapyear, dayofweek,
1818
get_week_of_year, get_day_of_year)
1919
from np_datetime cimport (pandas_datetimestruct, pandas_timedeltastruct,
@@ -85,7 +85,8 @@ def build_field_sarray(ndarray[int64_t] dtindex):
8585

8686
@cython.wraparound(False)
8787
@cython.boundscheck(False)
88-
def get_date_name_field(ndarray[int64_t] dtindex, object field):
88+
def get_date_name_field(ndarray[int64_t] dtindex, object field,
89+
object time_locale=None):
8990
"""
9091
Given a int64-based datetime index, return array of strings of date
9192
name based on requested field (e.g. weekday_name)
@@ -96,15 +97,11 @@ def get_date_name_field(ndarray[int64_t] dtindex, object field):
9697
pandas_datetimestruct dts
9798
int dow
9899

99-
_dayname = np.array(
100-
['Monday', 'Tuesday', 'Wednesday', 'Thursday',
101-
'Friday', 'Saturday', 'Sunday'],
102-
dtype=np.object_)
103-
104100
count = len(dtindex)
105101
out = np.empty(count, dtype=object)
106102

107103
if field == 'weekday_name':
104+
_dayname = np.array(DAYS_FULL, dtype=np.object_)
108105
for i in range(count):
109106
if dtindex[i] == NPY_NAT:
110107
out[i] = np.nan
@@ -114,6 +111,35 @@ def get_date_name_field(ndarray[int64_t] dtindex, object field):
114111
dow = dayofweek(dts.year, dts.month, dts.day)
115112
out[i] = _dayname[dow]
116113
return out
114+
elif field == 'day_name':
115+
if time_locale is None:
116+
_dayname = np.array(DAYS_FULL, dtype=np.object_)
117+
else:
118+
_dayname = np.array(get_locale_names('f_weekday', time_locale),
119+
dtype=np.object_)
120+
for i in range(count):
121+
if dtindex[i] == NPY_NAT:
122+
out[i] = np.nan
123+
continue
124+
125+
dt64_to_dtstruct(dtindex[i], &dts)
126+
dow = dayofweek(dts.year, dts.month, dts.day)
127+
out[i] = _dayname[dow].capitalize()
128+
return out
129+
elif field == 'month_name':
130+
if time_locale is None:
131+
_monthname = np.array(MONTHS_FULL, dtype=np.object_)
132+
else:
133+
_monthname = np.array(get_locale_names('f_month', time_locale),
134+
dtype=np.object_)
135+
for i in range(count):
136+
if dtindex[i] == NPY_NAT:
137+
out[i] = np.nan
138+
continue
139+
140+
dt64_to_dtstruct(dtindex[i], &dts)
141+
out[i] = _monthname[dts.month].capitalize()
142+
return out
117143

118144
raise ValueError("Field %s not supported" % field)
119145

pandas/_libs/tslibs/nattype.pyx

+32-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# -*- coding: utf-8 -*-
22
# cython: profile=False
3-
import warnings
43

54
from cpython cimport (
65
PyFloat_Check, PyComplex_Check,
@@ -39,24 +38,19 @@ _nat_scalar_rules[Py_GE] = False
3938
# ----------------------------------------------------------------------
4039

4140

42-
def _make_nan_func(func_name, cls):
41+
def _make_nan_func(func_name, doc):
4342
def f(*args, **kwargs):
4443
return np.nan
4544
f.__name__ = func_name
46-
f.__doc__ = getattr(cls, func_name).__doc__
45+
f.__doc__ = doc
4746
return f
4847

4948

50-
def _make_nat_func(func_name, cls):
49+
def _make_nat_func(func_name, doc):
5150
def f(*args, **kwargs):
5251
return NaT
53-
5452
f.__name__ = func_name
55-
if isinstance(cls, str):
56-
# passed the literal docstring directly
57-
f.__doc__ = cls
58-
else:
59-
f.__doc__ = getattr(cls, func_name).__doc__
53+
f.__doc__ = doc
6054
return f
6155

6256

@@ -318,11 +312,36 @@ class NaTType(_NaT):
318312
# These are the ones that can get their docstrings from datetime.
319313

320314
# nan methods
321-
weekday = _make_nan_func('weekday', datetime)
322-
isoweekday = _make_nan_func('isoweekday', datetime)
315+
weekday = _make_nan_func('weekday', datetime.weekday.__doc__)
316+
isoweekday = _make_nan_func('isoweekday', datetime.isoweekday.__doc__)
317+
month_name = _make_nan_func('month_name', # noqa:E128
318+
"""
319+
Return the month name of the Timestamp with specified locale.
320+
321+
Parameters
322+
----------
323+
time_locale : string, default None (English locale)
324+
locale determining the language in which to return the month name
325+
326+
Returns
327+
-------
328+
month_name : string
329+
""")
330+
day_name = _make_nan_func('day_name', # noqa:E128
331+
"""
332+
Return the day name of the Timestamp with specified locale.
333+
334+
Parameters
335+
----------
336+
time_locale : string, default None (English locale)
337+
locale determining the language in which to return the day name
323338
339+
Returns
340+
-------
341+
day_name : string
342+
""")
324343
# _nat_methods
325-
date = _make_nat_func('date', datetime)
344+
date = _make_nat_func('date', datetime.date.__doc__)
326345

327346
utctimetuple = _make_error_func('utctimetuple', datetime)
328347
timetz = _make_error_func('timetz', datetime)

pandas/_libs/tslibs/timestamps.pyx

+48
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ from util cimport (is_datetime64_object, is_timedelta64_object,
2121
INT64_MAX)
2222

2323
cimport ccalendar
24+
from ccalendar import get_locale_names, MONTHS_FULL, DAYS_FULL
2425
from conversion import tz_localize_to_utc, date_normalize
2526
from conversion cimport (tz_convert_single, _TSObject,
2627
convert_to_tsobject, convert_datetime_to_tsobject)
@@ -701,8 +702,55 @@ class Timestamp(_Timestamp):
701702
def dayofweek(self):
702703
return self.weekday()
703704

705+
def day_name(self, time_locale=None):
706+
"""
707+
Return the day name of the Timestamp with specified locale.
708+
709+
Parameters
710+
----------
711+
time_locale : string, default None (English locale)
712+
locale determining the language in which to return the day name
713+
714+
Returns
715+
-------
716+
day_name : string
717+
"""
718+
if time_locale is None:
719+
names = DAYS_FULL
720+
else:
721+
names = get_locale_names('f_weekday', time_locale)
722+
days = dict(enumerate(names))
723+
return days[self.weekday()].capitalize()
724+
725+
def month_name(self, time_locale=None):
726+
"""
727+
Return the month name of the Timestamp with specified locale.
728+
729+
Parameters
730+
----------
731+
time_locale : string, default None (English locale)
732+
locale determining the language in which to return the month name
733+
734+
Returns
735+
-------
736+
month_name : string
737+
"""
738+
if time_locale is None:
739+
names = MONTHS_FULL
740+
else:
741+
names = get_locale_names('f_month', time_locale)
742+
months = dict(enumerate(names))
743+
return months[self.month].capitalize()
744+
704745
@property
705746
def weekday_name(self):
747+
"""
748+
.. deprecated:: 0.23.0
749+
Use ``Timestamp.day_name()`` instead
750+
"""
751+
warnings.warn("`weekday_name` is deprecated and will be removed in a "
752+
"future version. Use `day_name` instead",
753+
DeprecationWarning)
706754
cdef dict wdays = {0: 'Monday', 1: 'Tuesday', 2: 'Wednesday',
707755
3: 'Thursday', 4: 'Friday', 5: 'Saturday',
708756
6: 'Sunday'}

pandas/core/indexes/datetimes.py

+51-1
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ class DatetimeIndex(DatelikeOps, TimelikeOps, DatetimeIndexOpsMixin,
262262
to_pydatetime
263263
to_series
264264
to_frame
265+
month_name
266+
day_name
265267
266268
Notes
267269
-----
@@ -1719,7 +1721,7 @@ def freq(self, value):
17191721
weekday_name = _field_accessor(
17201722
'weekday_name',
17211723
'weekday_name',
1722-
"The name of day in a week (ex: Friday)\n\n.. versionadded:: 0.18.1")
1724+
"The name of day in a week (ex: Friday)\n\n.. deprecated:: 0.23.0")
17231725

17241726
dayofyear = _field_accessor('dayofyear', 'doy',
17251727
"The ordinal day of the year")
@@ -2103,6 +2105,54 @@ def to_julian_date(self):
21032105
self.nanosecond / 3600.0 / 1e+9
21042106
) / 24.0)
21052107

2108+
def month_name(self, time_locale=None):
2109+
"""
2110+
Return the month names of the DateTimeIndex with specified locale.
2111+
2112+
Parameters
2113+
----------
2114+
time_locale : string, default None (English locale)
2115+
locale determining the language in which to return the month name
2116+
2117+
Returns
2118+
-------
2119+
month_names : Index
2120+
Index of month names
2121+
"""
2122+
values = self.asi8
2123+
if self.tz is not None:
2124+
if self.tz is not utc:
2125+
values = self._local_timestamps()
2126+
2127+
result = fields.get_date_name_field(values, 'month_name',
2128+
time_locale=time_locale)
2129+
result = self._maybe_mask_results(result)
2130+
return Index(result, name=self.name)
2131+
2132+
def day_name(self, time_locale=None):
2133+
"""
2134+
Return the day names of the DateTimeIndex with specified locale.
2135+
2136+
Parameters
2137+
----------
2138+
time_locale : string, default None (English locale)
2139+
locale determining the language in which to return the day name
2140+
2141+
Returns
2142+
-------
2143+
month_names : Index
2144+
Index of day names
2145+
"""
2146+
values = self.asi8
2147+
if self.tz is not None:
2148+
if self.tz is not utc:
2149+
values = self._local_timestamps()
2150+
2151+
result = fields.get_date_name_field(values, 'day_name',
2152+
time_locale=time_locale)
2153+
result = self._maybe_mask_results(result)
2154+
return Index(result, name=self.name)
2155+
21062156

21072157
DatetimeIndex._add_comparison_methods()
21082158
DatetimeIndex._add_numeric_methods_disabled()

0 commit comments

Comments
 (0)