-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-31939][SQL][test-java11] Fix Parsing day of year when year field pattern is missing #28766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
6afd5f7
2d797e0
a11a049
f78db47
b7da48e
cd55187
20b585d
b3cdccf
6b8dec4
52184fc
04ce7ee
1899e81
78c676e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,11 +21,39 @@ import org.scalatest.Matchers | |
|
|
||
| import org.apache.spark.{SparkFunSuite, SparkUpgradeException} | ||
| import org.apache.spark.sql.catalyst.plans.SQLHelper | ||
| import org.apache.spark.sql.catalyst.util.DateTimeTestUtils.{date, UTC} | ||
|
|
||
| trait DatetimeFormatterSuite extends SparkFunSuite with SQLHelper with Matchers { | ||
| import DateTimeFormatterHelper._ | ||
| def checkFormatterCreation(pattern: String, isParsing: Boolean): Unit | ||
|
|
||
| private def dateFormatter(pattern: String): DateFormatter = { | ||
| DateFormatter(pattern, UTC, isParsing = true) | ||
| } | ||
|
|
||
| private def timestampFormatter(pattern: String): TimestampFormatter = | ||
| TimestampFormatter(pattern, UTC, isParsing = true) | ||
|
|
||
| protected def useDateFormatter: Boolean | ||
|
|
||
| private def assertEqual(pattern: String, datetimeStr: String, expected: Long): Unit = { | ||
| if (useDateFormatter) { | ||
| assert(dateFormatter(pattern).parse(datetimeStr) === | ||
| DateTimeUtils.microsToEpochDays(expected, UTC)) | ||
| } else { | ||
| assert(timestampFormatter(pattern).parse(datetimeStr) === expected) | ||
| } | ||
| } | ||
|
|
||
| private def assertError(pattern: String, datetimeStr: String, expectedMsg: String): Unit = { | ||
| val exception = if (useDateFormatter) { | ||
| intercept[SparkUpgradeException](dateFormatter(pattern).parse(datetimeStr)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this mean the legacy formatter won't fail?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the legacy fomatter only fails when |
||
| } else { | ||
| intercept[SparkUpgradeException](timestampFormatter(pattern).parse(datetimeStr)) | ||
| } | ||
| assert(exception.getCause.getMessage.contains(expectedMsg)) | ||
| } | ||
|
|
||
| test("explicitly forbidden datetime patterns") { | ||
|
|
||
| Seq(true, false).foreach { isParsing => | ||
|
|
@@ -51,4 +79,36 @@ trait DatetimeFormatterSuite extends SparkFunSuite with SQLHelper with Matchers | |
| pattern => intercept[SparkUpgradeException](checkFormatterCreation(pattern, true)) | ||
| } | ||
| } | ||
|
|
||
| test("SPARK-31939: Fix Parsing day of year when year field pattern is missing") { | ||
| // resolved to queryable LocaleDate or fail directly | ||
| assertEqual("yyyy-dd-DD", "2020-29-60", date(2020, 2, 29)) | ||
| assertError("yyyy-dd-DD", "2020-02-60", | ||
| "Field DayOfMonth 29 differs from DayOfMonth 2 derived from 2020-02-29") | ||
|
cloud-fan marked this conversation as resolved.
|
||
| assertEqual("yyyy-MM-DD", "2020-02-60", date(2020, 2, 29)) | ||
| assertError("yyyy-MM-DD", "2020-03-60", | ||
| "Field MonthOfYear 2 differs from MonthOfYear 3 derived from 2020-02-29") | ||
| assertEqual("yyyy-MM-dd-DD", "2020-02-29-60", date(2020, 2, 29)) | ||
| assertError("yyyy-MM-dd-DD", "2020-03-01-60", | ||
| "Field DayOfYear 61 differs from DayOfYear 60 derived from 2020-03-01") | ||
| assertEqual("yyyy-DDD", "2020-366", date(2020, 12, 31)) | ||
| assertError("yyyy-DDD", "2019-366", | ||
| "Invalid date 'DayOfYear 366' as '2019' is not a leap year") | ||
|
|
||
| // unresolved and need to check manually(SPARK-31939 fixed) | ||
| assertEqual("DDD", "365", date(1970, 12, 31)) | ||
| assertError("DDD", "366", | ||
| "Invalid date 'DayOfYear 366' as '1970' is not a leap year") | ||
| assertEqual("MM-DD", "03-60", date(1970, 3)) | ||
| assertError("MM-DD", "02-60", | ||
| "Field MonthOfYear 2 differs from MonthOfYear 3 derived from 1970-03-01") | ||
| assertEqual("MM-dd-DD", "02-28-59", date(1970, 2, 28)) | ||
| assertError("MM-dd-DD", "02-28-60", | ||
| "Field MonthOfYear 2 differs from MonthOfYear 3 derived from 1970-03-01") | ||
| assertError("MM-dd-DD", "02-28-58", | ||
| "Field DayOfMonth 28 differs from DayOfMonth 27 derived from 1970-02-27") | ||
| assertEqual("dd-DD", "28-59", date(1970, 2, 28)) | ||
| assertError("dd-DD", "27-59", | ||
| "Field DayOfMonth 27 differs from DayOfMonth 28 derived from 1970-02-28") | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| --- TESTS FOR DATETIME PARSING FUNCTIONS WITH INVALID VALUES --- | ||
|
|
||
| -- parsing invalid values with pattern 'D' | ||
| select to_timestamp('366', 'D'); | ||
| select to_timestamp('9', 'DD'); | ||
| -- in java 8 this case is invalid, but valid in java 11, disabled for jenkins | ||
| -- select to_timestamp('100', 'DD'); | ||
| select to_timestamp('366', 'DD'); | ||
| select to_timestamp('9', 'DDD'); | ||
| select to_timestamp('99', 'DDD'); | ||
| select to_timestamp('30-365', 'dd-DDD'); | ||
| select to_timestamp('11-365', 'MM-DDD'); | ||
| select to_timestamp('2019-366', 'yyyy-DDD'); | ||
| select to_timestamp('12-30-365', 'MM-dd-DDD'); | ||
| select to_timestamp('2020-01-365', 'yyyy-dd-DDD'); | ||
| select to_timestamp('2020-10-350', 'yyyy-MM-DDD'); | ||
| select to_timestamp('2020-11-31-366', 'yyyy-MM-dd-DDD'); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| --SET spark.sql.legacy.timeParserPolicy=LEGACY | ||
| --IMPORT datetime-parsing.sql |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| --- TESTS FOR DATETIME PARSING FUNCTIONS --- | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there are a lot more tests should be added here later :) |
||
|
|
||
| -- parsing with pattern 'D' | ||
| select to_timestamp('9', 'D'); | ||
| select to_timestamp('300', 'D'); | ||
| select to_timestamp('09', 'DD'); | ||
| select to_timestamp('99', 'DD'); | ||
| select to_timestamp('009', 'DDD'); | ||
| select to_timestamp('365', 'DDD'); | ||
| select to_timestamp('31-365', 'dd-DDD'); | ||
| select to_timestamp('12-365', 'MM-DDD'); | ||
| select to_timestamp('2020-365', 'yyyy-DDD'); | ||
| select to_timestamp('12-31-365', 'MM-dd-DDD'); | ||
| select to_timestamp('2020-30-365', 'yyyy-dd-DDD'); | ||
| select to_timestamp('2020-11-350', 'yyyy-MM-DDD'); | ||
| select to_timestamp('2020-12-31-366', 'yyyy-MM-dd-DDD'); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| -- Automatically generated by SQLQueryTestSuite | ||
| -- Number of queries: 12 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('366', 'D') | ||
| -- !query schema | ||
| struct<to_timestamp(366, D):timestamp> | ||
| -- !query output | ||
| NULL | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('9', 'DD') | ||
| -- !query schema | ||
| struct<> | ||
| -- !query output | ||
| org.apache.spark.SparkUpgradeException | ||
| You may get a different result due to the upgrading of Spark 3.0: Fail to parse '9' in the new parser. You can set spark.sql.legacy.timeParserPolicy to LEGACY to restore the behavior before Spark 3.0, or set to CORRECTED and treat it as an invalid datetime string. | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('366', 'DD') | ||
| -- !query schema | ||
| struct<to_timestamp(366, DD):timestamp> | ||
| -- !query output | ||
| NULL | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('9', 'DDD') | ||
| -- !query schema | ||
| struct<> | ||
| -- !query output | ||
| org.apache.spark.SparkUpgradeException | ||
| You may get a different result due to the upgrading of Spark 3.0: Fail to parse '9' in the new parser. You can set spark.sql.legacy.timeParserPolicy to LEGACY to restore the behavior before Spark 3.0, or set to CORRECTED and treat it as an invalid datetime string. | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('99', 'DDD') | ||
| -- !query schema | ||
| struct<> | ||
| -- !query output | ||
| org.apache.spark.SparkUpgradeException | ||
| You may get a different result due to the upgrading of Spark 3.0: Fail to parse '99' in the new parser. You can set spark.sql.legacy.timeParserPolicy to LEGACY to restore the behavior before Spark 3.0, or set to CORRECTED and treat it as an invalid datetime string. | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('30-365', 'dd-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(30-365, dd-DDD):timestamp> | ||
| -- !query output | ||
| NULL | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('11-365', 'MM-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(11-365, MM-DDD):timestamp> | ||
| -- !query output | ||
| NULL | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('2019-366', 'yyyy-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(2019-366, yyyy-DDD):timestamp> | ||
| -- !query output | ||
| NULL | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('12-30-365', 'MM-dd-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(12-30-365, MM-dd-DDD):timestamp> | ||
| -- !query output | ||
| NULL | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('2020-01-365', 'yyyy-dd-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(2020-01-365, yyyy-dd-DDD):timestamp> | ||
| -- !query output | ||
| NULL | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('2020-10-350', 'yyyy-MM-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(2020-10-350, yyyy-MM-DDD):timestamp> | ||
| -- !query output | ||
| NULL | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('2020-11-31-366', 'yyyy-MM-dd-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(2020-11-31-366, yyyy-MM-dd-DDD):timestamp> | ||
| -- !query output | ||
| NULL |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| -- Automatically generated by SQLQueryTestSuite | ||
| -- Number of queries: 13 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('9', 'D') | ||
| -- !query schema | ||
| struct<to_timestamp(9, D):timestamp> | ||
| -- !query output | ||
| 1970-01-09 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('300', 'D') | ||
| -- !query schema | ||
| struct<to_timestamp(300, D):timestamp> | ||
| -- !query output | ||
| 1970-10-27 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('09', 'DD') | ||
| -- !query schema | ||
| struct<to_timestamp(09, DD):timestamp> | ||
| -- !query output | ||
| 1970-01-09 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('99', 'DD') | ||
| -- !query schema | ||
| struct<to_timestamp(99, DD):timestamp> | ||
| -- !query output | ||
| 1970-04-09 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('009', 'DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(009, DDD):timestamp> | ||
| -- !query output | ||
| 1970-01-09 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('365', 'DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(365, DDD):timestamp> | ||
| -- !query output | ||
| 1970-12-31 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('31-365', 'dd-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(31-365, dd-DDD):timestamp> | ||
| -- !query output | ||
| 1970-12-31 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('12-365', 'MM-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(12-365, MM-DDD):timestamp> | ||
| -- !query output | ||
| 1970-12-31 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('2020-365', 'yyyy-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(2020-365, yyyy-DDD):timestamp> | ||
| -- !query output | ||
| 2020-12-30 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('12-31-365', 'MM-dd-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(12-31-365, MM-dd-DDD):timestamp> | ||
| -- !query output | ||
| 1970-12-31 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('2020-30-365', 'yyyy-dd-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(2020-30-365, yyyy-dd-DDD):timestamp> | ||
| -- !query output | ||
| 2020-12-30 00:00:00 | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('2020-11-350', 'yyyy-MM-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(2020-11-350, yyyy-MM-DDD):timestamp> | ||
| -- !query output | ||
| NULL | ||
|
|
||
|
|
||
| -- !query | ||
| select to_timestamp('2020-12-31-366', 'yyyy-MM-dd-DDD') | ||
| -- !query schema | ||
| struct<to_timestamp(2020-12-31-366, yyyy-MM-dd-DDD):timestamp> | ||
| -- !query output | ||
| 2020-12-31 00:00:00 |
Uh oh!
There was an error while loading. Please reload this page.