Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions R/pkg/R/functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,20 @@ setMethod("dayofyear",
column(jc)
})

#' @details
#' \code{monthname}: Extracts the three-letter abbreviated month name from a
#' given date/timestamp/string.
#'
#' @rdname column_datetime_functions
#' @aliases monthname monthname,Column-method
#' @note monthname since 4.0.0
setMethod("monthname",
signature(x = "Column"),
function(x) {
jc <- callJStatic("org.apache.spark.sql.functions", "monthname", x@jc)
column(jc)
})

#' @details
#' \code{decode}: Computes the first argument into a string from a binary using the provided
#' character set.
Expand Down
4 changes: 4 additions & 0 deletions R/pkg/R/generics.R
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,10 @@ setGeneric("dayofweek", function(x) { standardGeneric("dayofweek") })
#' @name NULL
setGeneric("dayofyear", function(x) { standardGeneric("dayofyear") })

#' @rdname column_datetime_functions
#' @name NULL
setGeneric("monthname", function(x) { standardGeneric("monthname") })

#' @rdname column_string_functions
#' @name NULL
setGeneric("decode", function(x, charset) { standardGeneric("decode") })
Expand Down
1 change: 1 addition & 0 deletions R/pkg/tests/fulltests/test_sparkSQL.R
Original file line number Diff line number Diff line change
Expand Up @@ -2062,6 +2062,7 @@ test_that("date functions on a DataFrame", {
expect_equal(collect(select(df, weekofyear(df$b)))[, 1], c(50, 50, 51))
expect_equal(collect(select(df, year(df$b)))[, 1], c(2012, 2013, 2014))
expect_equal(collect(select(df, month(df$b)))[, 1], c(12, 12, 12))
expect_equal(collect(select(df, monthname(df$b)))[, 1], c("Dec", "Dec", "Dec"))
expect_equal(collect(select(df, last_day(df$b)))[, 1],
c(as.Date("2012-12-31"), as.Date("2013-12-31"), as.Date("2014-12-31")))
expect_equal(collect(select(df, next_day(df$b, "MONDAY")))[, 1],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5944,6 +5944,15 @@ object functions {
def to_unix_timestamp(timeExp: Column): Column =
Column.fn("to_unix_timestamp", timeExp)

/**
* Extracts the three-letter abbreviated month name from a given date/timestamp/string.
*
* @group datetime_funcs
* @since 4.0.0
*/
def monthname(timeExp: Column): Column =
Column.fn("monthname", timeExp)

//////////////////////////////////////////////////////////////////////////////////////////////
// Collection functions
//////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2121,6 +2121,10 @@ class PlanGenerationTestSuite
fn.months_between(fn.current_date(), fn.col("d"), roundOff = true)
}

temporalFunctionTest("monthname") {
fn.monthname(fn.col("d"))
}

temporalFunctionTest("next_day") {
fn.next_day(fn.col("d"), "Mon")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Project [monthname(d#0) AS monthname(d)#0]
+- LocalRelation <empty>, [d#0, t#0, s#0, x#0L, wt#0]
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"common": {
"planId": "1"
},
"project": {
"input": {
"common": {
"planId": "0"
},
"localRelation": {
"schema": "struct\u003cd:date,t:timestamp,s:string,x:bigint,wt:struct\u003cstart:timestamp,end:timestamp\u003e\u003e"
}
},
"expressions": [{
"unresolvedFunction": {
"functionName": "monthname",
"arguments": [{
"unresolvedAttribute": {
"unparsedIdentifier": "d"
}
}]
}
}]
}
}
Binary file not shown.
1 change: 1 addition & 0 deletions python/docs/source/reference/pyspark.sql/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ Date and Timestamp Functions
make_ym_interval
minute
month
monthname
months_between
next_day
now
Expand Down
7 changes: 7 additions & 0 deletions python/pyspark/sql/connect/functions/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2975,6 +2975,13 @@ def weekday(col: "ColumnOrName") -> Column:
weekday.__doc__ = pysparkfuncs.weekday.__doc__


def monthname(col: "ColumnOrName") -> Column:
return _invoke_function_over_columns("monthname", col)


monthname.__doc__ = pysparkfuncs.monthname.__doc__


def extract(field: "ColumnOrName", source: "ColumnOrName") -> Column:
return _invoke_function_over_columns("extract", field, source)

Expand Down
30 changes: 30 additions & 0 deletions python/pyspark/sql/functions/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7273,6 +7273,36 @@ def weekday(col: "ColumnOrName") -> Column:
return _invoke_function_over_columns("weekday", col)


@_try_remote_functions
def monthname(col: "ColumnOrName") -> Column:
"""
Returns the three-letter abbreviated month name from the given date.

.. versionadded:: 4.0.0

Parameters
----------
col : :class:`~pyspark.sql.Column` or str
target date/timestamp column to work on.

Returns
-------
:class:`~pyspark.sql.Column`
the three-letter abbreviation of month name for date/timestamp (Jan, Feb, Mar...)

Examples
--------
>>> df = spark.createDataFrame([('2015-04-08',)], ['dt'])
>>> df.select(monthname('dt').alias('month')).show()
+-----+
|month|
+-----+
| Apr|
+-----+
"""
return _invoke_function_over_columns("monthname", col)


@_try_remote_functions
def extract(field: "ColumnOrName", source: "ColumnOrName") -> Column:
"""
Expand Down
6 changes: 6 additions & 0 deletions python/pyspark/sql/tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,12 @@ def test_dayofweek(self):
row = df.select(F.dayofweek(df.date)).first()
self.assertEqual(row[0], 2)

def test_monthname(self):
dt = datetime.datetime(2017, 11, 6)
df = self.spark.createDataFrame([Row(date=dt)])
row = df.select(F.monthname(df.date)).first()
self.assertEqual(row[0], "Nov")

# Test added for SPARK-37738; change Python API to accept both col & int as input
def test_date_add_function(self):
dt = datetime.date(2021, 12, 27)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ object FunctionRegistry {
expression[WindowTime]("window_time"),
expression[MakeDate]("make_date"),
expression[MakeTimestamp]("make_timestamp"),
expression[MonthName]("monthname"),
// We keep the 2 expression builders below to have different function docs.
expressionBuilder("make_timestamp_ntz", MakeTimestampNTZExpressionBuilder, setAlias = true),
expressionBuilder("make_timestamp_ltz", MakeTimestampLTZExpressionBuilder, setAlias = true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,23 @@ case class WeekOfYear(child: Expression) extends GetDateField {
copy(child = newChild)
}

@ExpressionDescription(
usage = "_FUNC_(date) - Returns the three-letter abbreviated month name from the given date.",
examples = """
Examples:
> SELECT _FUNC_('2008-02-20');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the function doc, shall we avoid implicit cast and use DATE'2008-02-20'?

Feb
""",
group = "datetime_funcs",
since = "4.0.0")
case class MonthName(child: Expression) extends GetDateField {
override val func = DateTimeUtils.getMonthName
override val funcName = "getMonthName"
override def dataType: DataType = StringType
override protected def withNewChildInternal(newChild: Expression): MonthName =
copy(child = newChild)
}

// scalastyle:off line.size.limit
@ExpressionDescription(
usage = "_FUNC_(timestamp, fmt) - Converts `timestamp` to a value of string in the format specified by the date format `fmt`.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.apache.spark.sql.catalyst.util

import java.time._
import java.time.format.TextStyle
import java.time.temporal.{ChronoField, ChronoUnit, IsoFields, Temporal}
import java.util.Locale
import java.util.concurrent.TimeUnit._
Expand Down Expand Up @@ -196,6 +197,17 @@ object DateTimeUtils extends SparkDateTimeUtils {
localDateToDays(daysToLocalDate(days).plusMonths(months))
}

/**
* Returns the three-letter abbreviated month name for the given number of days since 1970-01-01.
*/
def getMonthName(days: Int): UTF8String = {
val monthName = Month
.of(getMonth(days))
.getDisplayName(TextStyle.SHORT, DateFormatter.defaultLocale)

UTF8String.fromString(monthName)
}

/**
* Adds months to a timestamp at the given time zone. It converts the input timestamp to a local
* timestamp at the given time zone, adds months, and converts the resulted local timestamp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,17 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
checkConsistencyBetweenInterpretedAndCodegen(WeekOfYear, DateType)
}

test("MonthName") {
checkEvaluation(MonthName(Literal.create(null, DateType)), null)
checkEvaluation(MonthName(Literal(d)), "Apr")
checkEvaluation(MonthName(Cast(Literal(date), DateType, UTC_OPT)), "Apr")
checkEvaluation(MonthName(Cast(Literal(ts), DateType, UTC_OPT)), "Nov")
checkEvaluation(MonthName(Cast(Literal("2011-05-06"), DateType, UTC_OPT)), "May")
checkEvaluation(MonthName(Literal(new Date(toMillis("2017-01-27 13:10:15")))), "Jan")
checkEvaluation(MonthName(Literal(new Date(toMillis("1582-12-15 13:10:15")))), "Dec")
checkConsistencyBetweenInterpretedAndCodegen(MonthName, DateType)
}

test("DateFormat") {
Seq("legacy", "corrected").foreach { legacyParserPolicy =>
withSQLConf(SQLConf.LEGACY_TIME_PARSER_POLICY.key -> legacyParserPolicy) {
Expand Down
9 changes: 9 additions & 0 deletions sql/core/src/main/scala/org/apache/spark/sql/functions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5738,6 +5738,15 @@ object functions {
def to_unix_timestamp(timeExp: Column): Column =
Column.fn("to_unix_timestamp", timeExp)

/**
* Extracts the three-letter abbreviated month name from a given date/timestamp/string.
*
* @group datetime_funcs
* @since 4.0.0
*/
def monthname(timeExp: Column): Column =
Column.fn("monthname", timeExp)

//////////////////////////////////////////////////////////////////////////////////////////////
// Collection functions
//////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@
| org.apache.spark.sql.catalyst.expressions.Minute | minute | SELECT minute('2009-07-30 12:58:59') | struct<minute(2009-07-30 12:58:59):int> |
| org.apache.spark.sql.catalyst.expressions.MonotonicallyIncreasingID | monotonically_increasing_id | SELECT monotonically_increasing_id() | struct<monotonically_increasing_id():bigint> |
| org.apache.spark.sql.catalyst.expressions.Month | month | SELECT month('2016-07-30') | struct<month(2016-07-30):int> |
| org.apache.spark.sql.catalyst.expressions.MonthName | monthname | SELECT monthname('2008-02-20') | struct<monthname(2008-02-20):string> |
| org.apache.spark.sql.catalyst.expressions.MonthsBetween | months_between | SELECT months_between('1997-02-28 10:30:00', '1996-10-30') | struct<months_between(1997-02-28 10:30:00, 1996-10-30, true):double> |
| org.apache.spark.sql.catalyst.expressions.Multiply | * | SELECT 2 * 3 | struct<(2 * 3):int> |
| org.apache.spark.sql.catalyst.expressions.Murmur3Hash | hash | SELECT hash('Spark', array(123), 2) | struct<hash(Spark, array(123), 2):int> |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,18 @@ class DateFunctionsSuite extends QueryTest with SharedSparkSession {
Row(2, 2, 0))
}

test("monthname") {
val df = Seq((d, sdfDate.format(d), ts)).toDF("a", "b", "c")

checkAnswer(
df.select(monthname($"a"), monthname($"b"), monthname($"c")),
Row("Apr", "Apr", "Apr"))

checkAnswer(
df.selectExpr("monthname(a)", "monthname(b)", "monthname(c)"),
Row("Apr", "Apr", "Apr"))
}

test("extract") {
val df = Seq((d, sdf.format(d), ts)).toDF("a", "b", "c")

Expand Down