Skip to content

Commit 3326aad

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 Rebase and adjust tests Move some imports to top of file, remove dup implimention of day_name in fields Fix failing test allow path to accept weekday_name as well
1 parent 9bdc5c8 commit 3326aad

File tree

9 files changed

+263
-48
lines changed

9 files changed

+263
-48
lines changed

doc/source/api.rst

+6
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,8 @@ These can be accessed like ``Series.dt.<property>``.
581581
Series.dt.round
582582
Series.dt.floor
583583
Series.dt.ceil
584+
Series.dt.month_name
585+
Series.dt.day_name
584586

585587
**Timedelta Properties**
586588

@@ -1759,6 +1761,8 @@ Time-specific operations
17591761
DatetimeIndex.round
17601762
DatetimeIndex.floor
17611763
DatetimeIndex.ceil
1764+
DatetimeIndex.month_name
1765+
DatetimeIndex.day_name
17621766

17631767
Conversion
17641768
~~~~~~~~~~
@@ -1954,6 +1958,7 @@ Methods
19541958
Timestamp.combine
19551959
Timestamp.ctime
19561960
Timestamp.date
1961+
Timestamp.day_name
19571962
Timestamp.dst
19581963
Timestamp.floor
19591964
Timestamp.freq
@@ -1963,6 +1968,7 @@ Methods
19631968
Timestamp.isocalendar
19641969
Timestamp.isoformat
19651970
Timestamp.isoweekday
1971+
Timestamp.month_name
19661972
Timestamp.normalize
19671973
Timestamp.now
19681974
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

@@ -642,6 +643,7 @@ Deprecations
642643
- The ``broadcast`` parameter of ``.apply()`` is deprecated in favor of ``result_type='broadcast'`` (:issue:`18577`)
643644
- The ``reduce`` parameter of ``.apply()`` is deprecated in favor of ``result_type='reduce'`` (:issue:`18577`)
644645
- The ``order`` parameter of :func:`factorize` is deprecated and will be removed in a future release (:issue:`19727`)
646+
- :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`)
645647

646648
.. _whatsnew_0230.prior_deprecations:
647649

pandas/_libs/tslibs/ccalendar.pyx

+33
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ cimport numpy as cnp
1212
from numpy cimport int64_t, int32_t
1313
cnp.import_array()
1414

15+
import locale
16+
from strptime import LocaleTime
1517

1618
# ----------------------------------------------------------------------
1719
# Constants
@@ -35,11 +37,18 @@ cdef int32_t* _month_offset = [
3537
# Canonical location for other modules to find name constants
3638
MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL',
3739
'AUG', 'SEP', 'OCT', 'NOV', 'DEC']
40+
# The first blank line is consistent with calendar.month_name in the calendar
41+
# standard library
42+
MONTHS_FULL = ['', 'January', 'February', 'March', 'April', 'May', 'June',
43+
'July', 'August', 'September', 'October', 'November',
44+
'December']
3845
MONTH_NUMBERS = {name: num for num, name in enumerate(MONTHS)}
3946
MONTH_ALIASES = {(num + 1): name for num, name in enumerate(MONTHS)}
4047
MONTH_TO_CAL_NUM = {name: num + 1 for num, name in enumerate(MONTHS)}
4148

4249
DAYS = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN']
50+
DAYS_FULL = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
51+
'Saturday', 'Sunday']
4352
int_to_weekday = {num: name for num, name in enumerate(DAYS)}
4453
weekday_to_int = {int_to_weekday[key]: key for key in int_to_weekday}
4554

@@ -199,3 +208,27 @@ cpdef int32_t get_day_of_year(int year, int month, int day) nogil:
199208

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

pandas/_libs/tslibs/fields.pyx

+24-9
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,23 +97,37 @@ 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

107-
if field == 'weekday_name':
103+
if field == 'day_name' or field == 'weekday_name':
104+
if time_locale is None:
105+
_dayname = np.array(DAYS_FULL, dtype=np.object_)
106+
else:
107+
_dayname = np.array(get_locale_names('f_weekday', time_locale),
108+
dtype=np.object_)
108109
for i in range(count):
109110
if dtindex[i] == NPY_NAT:
110111
out[i] = np.nan
111112
continue
112113

113114
dt64_to_dtstruct(dtindex[i], &dts)
114115
dow = dayofweek(dts.year, dts.month, dts.day)
115-
out[i] = _dayname[dow]
116+
out[i] = _dayname[dow].capitalize()
117+
return out
118+
elif field == 'month_name':
119+
if time_locale is None:
120+
_monthname = np.array(MONTHS_FULL, dtype=np.object_)
121+
else:
122+
_monthname = np.array(get_locale_names('f_month', time_locale),
123+
dtype=np.object_)
124+
for i in range(count):
125+
if dtindex[i] == NPY_NAT:
126+
out[i] = np.nan
127+
continue
128+
129+
dt64_to_dtstruct(dtindex[i], &dts)
130+
out[i] = _monthname[dts.month].capitalize()
116131
return out
117132

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

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
-----
@@ -1715,7 +1717,7 @@ def freq(self, value):
17151717
weekday_name = _field_accessor(
17161718
'weekday_name',
17171719
'weekday_name',
1718-
"The name of day in a week (ex: Friday)\n\n.. versionadded:: 0.18.1")
1720+
"The name of day in a week (ex: Friday)\n\n.. deprecated:: 0.23.0")
17191721

17201722
dayofyear = _field_accessor('dayofyear', 'doy',
17211723
"The ordinal day of the year")
@@ -2099,6 +2101,54 @@ def to_julian_date(self):
20992101
self.nanosecond / 3600.0 / 1e+9
21002102
) / 24.0)
21012103

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

21032153
DatetimeIndex._add_comparison_methods()
21042154
DatetimeIndex._add_numeric_methods_disabled()

0 commit comments

Comments
 (0)