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
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ package org.apache.spark.sql.catalyst.util
import java.time._
import java.time.chrono.IsoChronology
import java.time.format.{DateTimeFormatter, DateTimeFormatterBuilder, DateTimeParseException, ResolverStyle}
import java.time.temporal.{ChronoField, TemporalAccessor, TemporalQueries}
import java.time.temporal.{ChronoField, TemporalAccessor, TemporalQueries, WeekFields}
import java.util.Locale

import com.google.common.cache.CacheBuilder
Expand Down Expand Up @@ -99,12 +99,35 @@ private object DateTimeFormatterHelper {
new DateTimeFormatterBuilder().parseCaseInsensitive()
}

def toFormatter(builder: DateTimeFormatterBuilder, locale: Locale): DateTimeFormatter = {
builder
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
def setDefaulting(builder: DateTimeFormatterBuilder,
locale: Locale,
isWeekBased: Boolean = false): DateTimeFormatterBuilder = {

val defaults = Seq(
(ChronoField.ERA, 1),
(ChronoField.MINUTE_OF_HOUR, 0),
(ChronoField.SECOND_OF_MINUTE, 0)
)

val weekDayBased = if (isWeekBased) {
Seq((ChronoField.DAY_OF_WEEK,
WeekFields.of(locale).getFirstDayOfWeek.getValue))
} else {
Seq(
(ChronoField.MONTH_OF_YEAR, 1),
(ChronoField.DAY_OF_MONTH, 1))
Copy link
Contributor

Choose a reason for hiding this comment

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

I found these default values are very fragile to be set here, so I remove them all in #28576 . Can you check if it fixes your problem?

}

(defaults ++ weekDayBased).foldLeft(builder) {
case (builder, (chrono, value)) =>
builder.parseDefaulting(chrono, value)
}
}

def toFormatter(builder: DateTimeFormatterBuilder,
locale: Locale,
isWeekBased: Boolean = false): DateTimeFormatter = {
setDefaulting(builder, locale, isWeekBased)
.toFormatter(locale)
.withChronology(IsoChronology.INSTANCE)
.withResolverStyle(ResolverStyle.STRICT)
Expand Down Expand Up @@ -139,6 +162,13 @@ private object DateTimeFormatterHelper {
builder
}

def isWeekBasedPattern(pattern: String): Boolean = {
// Default values for parser needs to be changed when the pattern
// is week/year based.
// DAY_OF_MONTH and MONTH_OF_YEAR will be added by the week of the year.
Seq("YY", "w").exists(pattern.contains)
}

def buildFormatter(
pattern: String,
locale: Locale,
Expand All @@ -148,7 +178,7 @@ private object DateTimeFormatterHelper {
} else {
createBuilder().appendPattern(pattern)
}
toFormatter(builder, locale)
toFormatter(builder, locale, isWeekBasedPattern(pattern))
}

lazy val fractionFormatter: DateTimeFormatter = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,10 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
val sdf3 = new SimpleDateFormat(fmt3, Locale.US)
sdf3.setTimeZone(TimeZone.getTimeZone(UTC))

// Week of Year relies on Locale to define the first day of the week.
val fmt4 = "YYYY-ww"
val sdf4 = new SimpleDateFormat(fmt4, Locale.US)

withDefaultTimeZone(UTC) {
for (zid <- outstandingZoneIds) {
val timeZoneId = Option(zid.getId)
Expand Down Expand Up @@ -894,6 +898,11 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
Literal("2015-07-24"),
Literal("not a valid format"), timeZoneId), null)

sdf4.setTimeZone(tz)
checkEvaluation(
ToUnixTimestamp(Literal("2020-06"), Literal(fmt4), timeZoneId),
sdf4.parse("2020-06").getTime/1000)

// SPARK-28072 The codegen path for non-literal input should also work
checkEvaluation(
expression = ToUnixTimestamp(
Expand Down