From 760f6d685f2ff9a808a97df6755ced0d9a5590e5 Mon Sep 17 00:00:00 2001 From: Jerin John Date: Mon, 15 Apr 2024 12:38:01 -0400 Subject: [PATCH] Rectify missing/broken BQ date-time format elements --- .../java/org/apache/calcite/util/Bug.java | 10 +- .../util/format/FormatElementEnum.java | 226 +++++++++++++--- .../calcite/util/format/FormatModels.java | 66 ++++- .../apache/calcite/util/FormatModelTest.java | 4 +- .../util/format/FormatElementEnumTest.java | 43 ++- .../test/resources/sql/cast-with-format.iq | 254 ++++++++---------- .../apache/calcite/test/SqlOperatorTest.java | 117 +++++--- 7 files changed, 467 insertions(+), 253 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/util/Bug.java b/core/src/main/java/org/apache/calcite/util/Bug.java index 7fdbb4c082fb..18c72ef102a0 100644 --- a/core/src/main/java/org/apache/calcite/util/Bug.java +++ b/core/src/main/java/org/apache/calcite/util/Bug.java @@ -200,16 +200,16 @@ public abstract class Bug { * Fix to be available with Avatica 1.24.0 [CALCITE-6053] */ public static final boolean CALCITE_6092_FIXED = false; - /** Whether - * [CALCITE-6269] - * Fix missing/broken BigQuery date-time format elements is fixed. */ - public static final boolean CALCITE_6269_FIXED = false; - /** Whether * [CALCITE-6270] * Support FORMAT in CAST from Numeric and BYTES to String (Enabled in BigQuery) is fixed. */ public static final boolean CALCITE_6270_FIXED = false; + /** Whether + * [CALCITE-6269] + * Add timezone support for FORMAT clause in CAST (enabled in BigQuery) is fixed. */ + public static final boolean CALCITE_6367_FIXED = false; + /** * Use this to flag temporary code. */ diff --git a/core/src/main/java/org/apache/calcite/util/format/FormatElementEnum.java b/core/src/main/java/org/apache/calcite/util/format/FormatElementEnum.java index de4b5df03b74..8740e93f958c 100644 --- a/core/src/main/java/org/apache/calcite/util/format/FormatElementEnum.java +++ b/core/src/main/java/org/apache/calcite/util/format/FormatElementEnum.java @@ -18,6 +18,8 @@ import org.apache.calcite.avatica.util.DateTimeUtils; +import org.apache.commons.lang3.StringUtils; + import org.checkerframework.checker.nullness.qual.Nullable; import java.text.DateFormat; @@ -57,19 +59,22 @@ public enum FormatElementEnum implements FormatElement { sb.append(String.format(Locale.ROOT, "%d", calendar.get(Calendar.DAY_OF_WEEK))); } }, - DAY("EEEE", "The full weekday name") { + DAY("EEEE", "The full weekday name, in uppercase") { @Override public void format(StringBuilder sb, Date date) { - final Calendar calendar = Work.get().calendar; - calendar.setTime(date); - // The Calendar and SimpleDateFormatter do not seem to give correct results - // for the day of the week prior to the Julian to Gregorian date change. - // So we resort to using a LocalDate representation. - LocalDate ld = - LocalDate.of(calendar.get(Calendar.YEAR), - // Calendar months are numbered from 0 - calendar.get(Calendar.MONTH) + 1, - calendar.get(Calendar.DAY_OF_MONTH)); - sb.append(ld.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH)); + final Work work = Work.get(); + sb.append(work.getDayFromDate(date, TextStyle.FULL).toUpperCase(Locale.ROOT)); + } + }, + Day("EEEE", "The full weekday name, capitalized") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + sb.append(work.getDayFromDate(date, TextStyle.FULL)); + } + }, + day("EEEE", "The full weekday name, in lowercase") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + sb.append(work.getDayFromDate(date, TextStyle.FULL).toLowerCase(Locale.ROOT)); } }, DD("dd", "The day of the month as a decimal number (01-31)") { @@ -86,19 +91,22 @@ public enum FormatElementEnum implements FormatElement { sb.append(String.format(Locale.ROOT, "%03d", calendar.get(Calendar.DAY_OF_YEAR))); } }, - DY("EEE", "The abbreviated weekday name") { + DY("EEE", "The abbreviated weekday name, in uppercase") { @Override public void format(StringBuilder sb, Date date) { - final Calendar calendar = Work.get().calendar; - calendar.setTime(date); - // The Calendar and SimpleDateFormatter do not seem to give correct results - // for the day of the week prior to the Julian to Gregorian date change. - // So we resort to using a LocalDate representation. - LocalDate ld = - LocalDate.of(calendar.get(Calendar.YEAR), - // Calendar months are numbered from 0 - calendar.get(Calendar.MONTH) + 1, - calendar.get(Calendar.DAY_OF_MONTH)); - sb.append(ld.getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.ENGLISH)); + final Work work = Work.get(); + sb.append(work.getDayFromDate(date, TextStyle.SHORT).toUpperCase(Locale.ROOT)); + } + }, + Dy("EEE", "The abbreviated weekday name, capitalized") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + sb.append(work.getDayFromDate(date, TextStyle.SHORT)); + } + }, + dy("EEE", "The abbreviated weekday name, in lowercase") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + sb.append(work.getDayFromDate(date, TextStyle.SHORT).toLowerCase(Locale.ROOT)); } }, E("d", "The day of the month as a decimal number (1-31); " @@ -112,37 +120,79 @@ public enum FormatElementEnum implements FormatElement { FF1("S", "Fractional seconds to 1 digit") { @Override public void format(StringBuilder sb, Date date) { final Work work = Work.get(); - sb.append(work.sFormat.format(date)); + // Extracting 1 decimal place as SimpleDateFormat returns precision with 3 places. + // Refer to + // [CALCITE-6269] Fix missing/broken BigQuery date-time format elements. + sb.append(work.sssFormat.format(date).charAt(0)); } }, - FF2("SS", "Fractional seconds to 2 digits") { + FF2("S", "Fractional seconds to 2 digits") { @Override public void format(StringBuilder sb, Date date) { final Work work = Work.get(); - sb.append(work.ssFormat.format(date)); + // Extracting 2 decimal places as SimpleDateFormat returns precision with 3 places. + // Refer to + // [CALCITE-6269] Fix missing/broken BigQuery date-time format elements. + sb.append(work.sssFormat.format(date), 0, 2); } }, - FF3("SSS", "Fractional seconds to 3 digits") { + FF3("S", "Fractional seconds to 3 digits") { @Override public void format(StringBuilder sb, Date date) { final Work work = Work.get(); sb.append(work.sssFormat.format(date)); } }, - FF4("SSSS", "Fractional seconds to 4 digits") { + FF4("S", "Fractional seconds to 4 digits") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + // Padding zeroes to right as SimpleDateFormat supports precision only up to 3 places. + // Refer to + // [CALCITE-6269] Fix missing/broken BigQuery date-time format elements. + sb.append(StringUtils.rightPad(work.sssFormat.format(date), 4, "0")); + } + }, + FF5("S", "Fractional seconds to 5 digits") { @Override public void format(StringBuilder sb, Date date) { final Work work = Work.get(); - sb.append(work.ssssFormat.format(date)); + // Padding zeroes to right as SimpleDateFormat supports precision only up to 3 places. + // Refer to + // [CALCITE-6269] Fix missing/broken BigQuery date-time format elements. + sb.append(StringUtils.rightPad(work.sssFormat.format(date), 5, "0")); } }, - FF5("SSSSS", "Fractional seconds to 5 digits") { + FF6("S", "Fractional seconds to 6 digits") { @Override public void format(StringBuilder sb, Date date) { final Work work = Work.get(); - sb.append(work.sssssFormat.format(date)); + // Padding zeroes to right as SimpleDateFormat supports precision only up to 3 places. + // Refer to + // [CALCITE-6269] Fix missing/broken BigQuery date-time format elements. + sb.append(StringUtils.rightPad(work.sssFormat.format(date), 6, "0")); } }, - FF6("SSSSSS", "Fractional seconds to 6 digits") { + FF7("S", "Fractional seconds to 6 digits") { @Override public void format(StringBuilder sb, Date date) { final Work work = Work.get(); - sb.append(work.ssssssFormat.format(date)); + // Padding zeroes to right as SimpleDateFormat supports precision only up to 3 places. + // Refer to + // [CALCITE-6269] Fix missing/broken BigQuery date-time format elements. + sb.append(StringUtils.rightPad(work.sssFormat.format(date), 7, "0")); + } + }, + FF8("S", "Fractional seconds to 6 digits") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + // Padding zeroes to right as SimpleDateFormat supports precision only up to 3 places. + // Refer to + // [CALCITE-6269] Fix missing/broken BigQuery date-time format elements. + sb.append(StringUtils.rightPad(work.sssFormat.format(date), 8, "0")); + } + }, + FF9("S", "Fractional seconds to 6 digits") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + // Padding zeroes to right as SimpleDateFormat supports precision only up to 3 places. + // Refer to + // [CALCITE-6269] Fix missing/broken BigQuery date-time format elements. + sb.append(StringUtils.rightPad(work.sssFormat.format(date), 9, "0")); } }, HH12("h", "The hour (12-hour clock) as a decimal number (01-12)") { @@ -185,18 +235,42 @@ public enum FormatElementEnum implements FormatElement { sb.append(String.format(Locale.ROOT, "%02d", calendar.get(Calendar.MONTH) + 1)); } }, - MON("MMM", "The abbreviated month name") { + MON("MMM", "The abbreviated month name, in uppercase") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + sb.append(work.mmmFormat.format(date).toUpperCase(Locale.ROOT)); + } + }, + Mon("MMM", "The abbreviated month name, capitalized") { @Override public void format(StringBuilder sb, Date date) { final Work work = Work.get(); sb.append(work.mmmFormat.format(date)); } }, - MONTH("MMMM", "The full month name (English)") { + mon("MMM", "The abbreviated month name, in lowercase") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + sb.append(work.mmmFormat.format(date).toLowerCase(Locale.ROOT)); + } + }, + MONTH("MMMM", "The full month name (English), in uppercase") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + sb.append(work.mmmmFormat.format(date).toUpperCase(Locale.ROOT)); + } + }, + Month("MMMM", "The full month name (English), capitalized") { @Override public void format(StringBuilder sb, Date date) { final Work work = Work.get(); sb.append(work.mmmmFormat.format(date)); } }, + month("MMMM", "The full month name (English), in lowercase") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + sb.append(work.mmmmFormat.format(date).toLowerCase(Locale.ROOT)); + } + }, // PM can represent both AM and PM PM("a", "Meridian indicator without periods") { @Override public void format(StringBuilder sb, Date date) { @@ -217,6 +291,34 @@ public enum FormatElementEnum implements FormatElement { sb.append(String.format(Locale.ROOT, "%d", (calendar.get(Calendar.MONTH) / 3) + 1)); } }, + AMPM("", "The time as Meridian Indicator in uppercase") { + @Override public void format(StringBuilder sb, Date date) { + final Calendar calendar = Work.get().calendar; + calendar.setTime(date); + sb.append(calendar.get(Calendar.AM_PM) == Calendar.AM ? "AM" : "PM"); + } + }, + AM_PM("", "The time as Meridian Indicator in uppercase with dot") { + @Override public void format(StringBuilder sb, Date date) { + final Calendar calendar = Work.get().calendar; + calendar.setTime(date); + sb.append(calendar.get(Calendar.AM_PM) == Calendar.AM ? "A.M." : "P.M."); + } + }, + ampm("", "The time as Meridian Indicator in lowercase") { + @Override public void format(StringBuilder sb, Date date) { + final Calendar calendar = Work.get().calendar; + calendar.setTime(date); + sb.append(calendar.get(Calendar.AM_PM) == Calendar.AM ? "am" : "pm"); + } + }, + am_pm("", "The time as Meridian Indicator in uppercase") { + @Override public void format(StringBuilder sb, Date date) { + final Calendar calendar = Work.get().calendar; + calendar.setTime(date); + sb.append(calendar.get(Calendar.AM_PM) == Calendar.AM ? "a.m." : "p.m."); + } + }, MS("SSS", "The millisecond as a decimal number (000-999)") { @Override public void format(StringBuilder sb, Date date) { final Calendar calendar = Work.get().calendar; @@ -231,6 +333,24 @@ public enum FormatElementEnum implements FormatElement { sb.append(String.format(Locale.ROOT, "%02d", calendar.get(Calendar.SECOND))); } }, + SSSSS("s", "The seconds of the day (00000-86400)") { + @Override public void format(StringBuilder sb, Date date) { + Calendar calendar = Work.get().calendar; + calendar.setTime(date); + long timeInMillis = calendar.getTimeInMillis(); + + // Set calendar to start of day for input date + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + long dayStartInMillis = calendar.getTimeInMillis(); + + // Get seconds of the day as difference from day start time + long secondsPassed = (timeInMillis - dayStartInMillis) / 1000; + sb.append(String.format(Locale.ROOT, "%05d", secondsPassed)); + } + }, TZR("z", "The time zone name") { @Override public void format(StringBuilder sb, Date date) { // TODO: how to support timezones? @@ -253,12 +373,27 @@ public enum FormatElementEnum implements FormatElement { sb.append(String.format(Locale.ROOT, "%02d", calendar.get(Calendar.WEEK_OF_YEAR))); } }, + Y("y", "Last digit of year") { + @Override public void format(StringBuilder sb, Date date) { + final Work work = Work.get(); + String formattedYear = work.yyFormat.format(date); + sb.append(formattedYear.substring(formattedYear.length() - 1)); + } + }, YY("yy", "Last 2 digits of year") { @Override public void format(StringBuilder sb, Date date) { final Work work = Work.get(); sb.append(work.yyFormat.format(date)); } }, + YYY("yyy", "Last 3 digits of year") { + @Override public void format(StringBuilder sb, Date date) { + final Calendar calendar = Work.get().calendar; + calendar.setTime(date); + String formattedYear = String.format(Locale.ROOT, "%d", calendar.get(Calendar.YEAR)); + sb.append(formattedYear.substring(formattedYear.length() - 3)); + } + }, YYYY("yyyy", "The year with century as a decimal number") { @Override public void format(StringBuilder sb, Date date) { final Calendar calendar = Work.get().calendar; @@ -306,12 +441,21 @@ static Work get() { * https://issues.apache.org/jira/browse/CALCITE-6252. This may be * specific to Java 11. */ final DateFormat mmmmFormat = new SimpleDateFormat(MONTH.javaFmt, Locale.US); - final DateFormat sFormat = new SimpleDateFormat(FF1.javaFmt, Locale.ROOT); - final DateFormat ssFormat = new SimpleDateFormat(FF2.javaFmt, Locale.ROOT); final DateFormat sssFormat = new SimpleDateFormat(FF3.javaFmt, Locale.ROOT); - final DateFormat ssssFormat = new SimpleDateFormat(FF4.javaFmt, Locale.ROOT); - final DateFormat sssssFormat = new SimpleDateFormat(FF5.javaFmt, Locale.ROOT); - final DateFormat ssssssFormat = new SimpleDateFormat(FF6.javaFmt, Locale.ROOT); final DateFormat yyFormat = new SimpleDateFormat(YY.javaFmt, Locale.ROOT); + + /** Util to return the full or abbreviated weekday name from date and expected TextStyle. */ + public String getDayFromDate(Date date, TextStyle style) { + calendar.setTime(date); + // The Calendar and SimpleDateFormatter do not seem to give correct results + // for the day of the week prior to the Julian to Gregorian date change. + // So we resort to using a LocalDate representation. + LocalDate ld = + LocalDate.of(calendar.get(Calendar.YEAR), + // Calendar months are numbered from 0 + calendar.get(Calendar.MONTH) + 1, + calendar.get(Calendar.DAY_OF_MONTH)); + return ld.getDayOfWeek().getDisplayName(style, Locale.ENGLISH); + } } } diff --git a/core/src/main/java/org/apache/calcite/util/format/FormatModels.java b/core/src/main/java/org/apache/calcite/util/format/FormatModels.java index cf72189721c6..2c2957b89986 100644 --- a/core/src/main/java/org/apache/calcite/util/format/FormatModels.java +++ b/core/src/main/java/org/apache/calcite/util/format/FormatModels.java @@ -22,18 +22,23 @@ import java.util.Date; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.apache.calcite.util.format.FormatElementEnum.AMPM; +import static org.apache.calcite.util.format.FormatElementEnum.AM_PM; import static org.apache.calcite.util.format.FormatElementEnum.CC; import static org.apache.calcite.util.format.FormatElementEnum.D; import static org.apache.calcite.util.format.FormatElementEnum.DAY; import static org.apache.calcite.util.format.FormatElementEnum.DD; import static org.apache.calcite.util.format.FormatElementEnum.DDD; import static org.apache.calcite.util.format.FormatElementEnum.DY; +import static org.apache.calcite.util.format.FormatElementEnum.Day; +import static org.apache.calcite.util.format.FormatElementEnum.Dy; import static org.apache.calcite.util.format.FormatElementEnum.E; import static org.apache.calcite.util.format.FormatElementEnum.FF1; import static org.apache.calcite.util.format.FormatElementEnum.FF2; @@ -41,6 +46,9 @@ import static org.apache.calcite.util.format.FormatElementEnum.FF4; import static org.apache.calcite.util.format.FormatElementEnum.FF5; import static org.apache.calcite.util.format.FormatElementEnum.FF6; +import static org.apache.calcite.util.format.FormatElementEnum.FF7; +import static org.apache.calcite.util.format.FormatElementEnum.FF8; +import static org.apache.calcite.util.format.FormatElementEnum.FF9; import static org.apache.calcite.util.format.FormatElementEnum.HH12; import static org.apache.calcite.util.format.FormatElementEnum.HH24; import static org.apache.calcite.util.format.FormatElementEnum.IW; @@ -49,14 +57,25 @@ import static org.apache.calcite.util.format.FormatElementEnum.MON; import static org.apache.calcite.util.format.FormatElementEnum.MONTH; import static org.apache.calcite.util.format.FormatElementEnum.MS; +import static org.apache.calcite.util.format.FormatElementEnum.Mon; +import static org.apache.calcite.util.format.FormatElementEnum.Month; import static org.apache.calcite.util.format.FormatElementEnum.PM; import static org.apache.calcite.util.format.FormatElementEnum.Q; import static org.apache.calcite.util.format.FormatElementEnum.SS; +import static org.apache.calcite.util.format.FormatElementEnum.SSSSS; import static org.apache.calcite.util.format.FormatElementEnum.TZR; import static org.apache.calcite.util.format.FormatElementEnum.W; import static org.apache.calcite.util.format.FormatElementEnum.WW; +import static org.apache.calcite.util.format.FormatElementEnum.Y; import static org.apache.calcite.util.format.FormatElementEnum.YY; +import static org.apache.calcite.util.format.FormatElementEnum.YYY; import static org.apache.calcite.util.format.FormatElementEnum.YYYY; +import static org.apache.calcite.util.format.FormatElementEnum.am_pm; +import static org.apache.calcite.util.format.FormatElementEnum.ampm; +import static org.apache.calcite.util.format.FormatElementEnum.day; +import static org.apache.calcite.util.format.FormatElementEnum.dy; +import static org.apache.calcite.util.format.FormatElementEnum.mon; +import static org.apache.calcite.util.format.FormatElementEnum.month; import static java.util.Objects.requireNonNull; @@ -98,13 +117,13 @@ private FormatModels() { DEFAULT = create(map); map.clear(); - map.put("%A", DAY); - map.put("%a", DY); - map.put("%B", MONTH); - map.put("%b", MON); + map.put("%A", Day); + map.put("%a", Dy); + map.put("%B", Month); + map.put("%b", Mon); map.put("%c", compositeElement("The date and time representation (English);", - DY, literalElement(" "), MON, literalElement(" "), + Dy, literalElement(" "), Mon, literalElement(" "), DD, literalElement(" "), HH24, literalElement(":"), MI, literalElement(":"), SS, literalElement(" "), YYYY)); @@ -146,6 +165,7 @@ MI, literalElement(":"), SS, literalElement(" "), map.put("HH12", HH12); map.put("HH24", HH24); map.put("MI", MI); + map.put("SSSSS", SSSSS); map.put("SS", SS); map.put("MS", MS); map.put("FF1", FF1); @@ -154,14 +174,26 @@ MI, literalElement(":"), SS, literalElement(" "), map.put("FF4", FF4); map.put("FF5", FF5); map.put("FF6", FF6); + map.put("FF7", FF7); + map.put("FF8", FF8); + map.put("FF9", FF9); map.put("YYYY", YYYY); + map.put("YYY", YYY); map.put("YY", YY); - map.put("Day", DAY); + map.put("Y", Y); + map.put("RRRR", YYYY); + map.put("RR", YY); map.put("DAY", DAY); + map.put("Day", Day); + map.put("day", day); map.put("DY", DY); - map.put("Month", MONTH); + map.put("Dy", Dy); + map.put("dy", dy); + map.put("month", month); + map.put("Month", Month); map.put("MONTH", MONTH); - map.put("Mon", MON); + map.put("mon", mon); + map.put("Mon", Mon); map.put("MON", MON); map.put("MM", MM); map.put("CC", CC); @@ -172,10 +204,28 @@ MI, literalElement(":"), SS, literalElement(" "), map.put("W", W); map.put("IW", IW); map.put("Q", Q); + map.put("AM", AMPM); + map.put("A.M.", AM_PM); + map.put("am", ampm); + map.put("a.m.", am_pm); + map.put("PM", AMPM); + map.put("P.M.", AM_PM); + map.put("pm", ampm); + map.put("p.m.", am_pm); + // Our implementation of TO_CHAR does not support TIMESTAMPTZ // As PostgreSQL, we will skip the timezone when formatting TIMESTAMP values map.put("TZ", TZR); + // Add lowercase mappings for formats that can also work case-insensitive. + final Map lowercaseMap = new LinkedHashMap<>(); + map.forEach((key, value) -> { + String lowerKey = key.toLowerCase(Locale.ROOT); + if (!map.containsKey(lowerKey)) { + lowercaseMap.put(lowerKey, value); + } + }); + map.putAll(lowercaseMap); BIG_QUERY = create(map); POSTGRESQL = create(map); } diff --git a/core/src/test/java/org/apache/calcite/util/FormatModelTest.java b/core/src/test/java/org/apache/calcite/util/FormatModelTest.java index 95509b9e0a22..addeabba9767 100644 --- a/core/src/test/java/org/apache/calcite/util/FormatModelTest.java +++ b/core/src/test/java/org/apache/calcite/util/FormatModelTest.java @@ -52,12 +52,12 @@ private void assertThatFormatElementParse(String formatString, @Test void testMultipleElements() { assertThatFormatElementParse("%b-%d-%Y", - is(Arrays.asList("MON", "-", "DD", "-", "YYYY"))); + is(Arrays.asList("Mon", "-", "DD", "-", "YYYY"))); } @Test void testArbitraryText() { assertThatFormatElementParse("%jtext%b", - is(Arrays.asList("DDD", "text", "MON"))); + is(Arrays.asList("DDD", "text", "Mon"))); } @Test void testAliasText() { diff --git a/core/src/test/java/org/apache/calcite/util/format/FormatElementEnumTest.java b/core/src/test/java/org/apache/calcite/util/format/FormatElementEnumTest.java index 75eac7e13400..c77baaeaaf01 100644 --- a/core/src/test/java/org/apache/calcite/util/format/FormatElementEnumTest.java +++ b/core/src/test/java/org/apache/calcite/util/format/FormatElementEnumTest.java @@ -33,8 +33,14 @@ class FormatElementEnumTest { assertFormatElement(FormatElementEnum.CC, "2014-09-30T10:00:00Z", "21"); } + @Test void testDAY() { + assertFormatElement(FormatElementEnum.DAY, "2014-09-30T10:00:00Z", "TUESDAY"); + } @Test void testDay() { - assertFormatElement(FormatElementEnum.DAY, "2014-09-30T10:00:00Z", "Tuesday"); + assertFormatElement(FormatElementEnum.Day, "2014-09-30T10:00:00Z", "Tuesday"); + } + @Test void testday() { + assertFormatElement(FormatElementEnum.day, "2014-09-30T10:00:00Z", "tuesday"); } @Test void testD() { @@ -50,15 +56,21 @@ class FormatElementEnumTest { } @Test void testDY() { - assertFormatElement(FormatElementEnum.DY, "2014-09-30T10:00:00Z", "Tue"); + assertFormatElement(FormatElementEnum.DY, "2014-09-30T10:00:00Z", "TUE"); + } + @Test void testDy() { + assertFormatElement(FormatElementEnum.Dy, "2014-09-30T10:00:00Z", "Tue"); + } + @Test void testdy() { + assertFormatElement(FormatElementEnum.dy, "2014-09-30T10:00:00Z", "tue"); } @Test void testFF1() { - assertFormatElement(FormatElementEnum.FF1, "2014-09-30T10:00:00.123456Z", "123"); + assertFormatElement(FormatElementEnum.FF1, "2014-09-30T10:00:00.123456Z", "1"); } @Test void testFF2() { - assertFormatElement(FormatElementEnum.FF2, "2014-09-30T10:00:00.123456Z", "123"); + assertFormatElement(FormatElementEnum.FF2, "2014-09-30T10:00:00.123456Z", "12"); } @Test void testFF3() { @@ -66,15 +78,24 @@ class FormatElementEnumTest { } @Test void testFF4() { - assertFormatElement(FormatElementEnum.FF4, "2014-09-30T10:00:00.123456Z", "0123"); + assertFormatElement(FormatElementEnum.FF4, "2014-09-30T10:00:00.123456Z", "1230"); } @Test void testFF5() { - assertFormatElement(FormatElementEnum.FF5, "2014-09-30T10:00:00.123456Z", "00123"); + assertFormatElement(FormatElementEnum.FF5, "2014-09-30T10:00:00.123456Z", "12300"); } @Test void testFF6() { - assertFormatElement(FormatElementEnum.FF6, "2014-09-30T10:00:00.123456Z", "000123"); + assertFormatElement(FormatElementEnum.FF6, "2014-09-30T10:00:00.123456Z", "123000"); + } + @Test void testFF7() { + assertFormatElement(FormatElementEnum.FF7, "2014-09-30T10:00:00.123456Z", "1230000"); + } + @Test void testFF8() { + assertFormatElement(FormatElementEnum.FF8, "2014-09-30T10:00:00.123456Z", "12300000"); + } + @Test void testFF9() { + assertFormatElement(FormatElementEnum.FF9, "2014-09-30T10:00:00.123456Z", "123000000"); } @Test void testIW() { @@ -86,7 +107,13 @@ class FormatElementEnumTest { } @Test void testMON() { - assertFormatElement(FormatElementEnum.MON, "2014-09-30T10:00:00Z", "Sep"); + assertFormatElement(FormatElementEnum.MON, "2014-09-30T10:00:00Z", "SEP"); + } + @Test void testMon() { + assertFormatElement(FormatElementEnum.Mon, "2014-09-30T10:00:00Z", "Sep"); + } + @Test void testmon() { + assertFormatElement(FormatElementEnum.mon, "2014-09-30T10:00:00Z", "sep"); } @Test void testQ() { diff --git a/core/src/test/resources/sql/cast-with-format.iq b/core/src/test/resources/sql/cast-with-format.iq index b21c7f5aff98..b03f1c873028 100644 --- a/core/src/test/resources/sql/cast-with-format.iq +++ b/core/src/test/resources/sql/cast-with-format.iq @@ -32,8 +32,15 @@ EXPR$0 2017-05-01 01:23:45 !ok +# Input that contains shuffled date without time +select cast('12-2010-05' as timestamp format + 'DD-YYYY-MM'); +EXPR$0 +2010-05-12 00:00:00 +!ok + !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### # Basic input to cover a datetime with timezone scenario select cast('2017-05-03 08:59:01.123456789PM 01:30' @@ -42,13 +49,6 @@ EXPR$0 2017-05-03 20:59:01.123456789 !ok -# Input that contains shuffled date without time -select cast('12-2010-05' as timestamp format - 'DD-YYYY-MM'); -EXPR$0 -2010-05-12 00:00:00 -!ok - # Shuffle the input timestamp and the format clause select cast('59 04-30-2017-05 01PM 01:08.123456789' as timestamp FORMAT 'MI DD-TZM-YYYY-MM TZHPM SS:HH12.FF9'); @@ -183,7 +183,7 @@ null !ok !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### iso8601_format ################################################# # @@ -216,7 +216,7 @@ EXPR$0 !} !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### lowercase_format_elements ################################################# select cast('2019-11-20 15:59:44.123456789 01:01' as @@ -268,7 +268,7 @@ EXPR$0 !ok !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### # YYY with less than 3 digits in the input select cast('95-04-30' as timestamp FORMAT 'YYY-MM-DD'); @@ -322,8 +322,6 @@ EXPR$0 19 !ok -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### select cast(cast('2019-01-01' as timestamp) as varchar format 'YYY'); EXPR$0 @@ -335,21 +333,13 @@ select cast(cast('2019-01-01' as timestamp) as varchar EXPR$0 9 !ok -!} !set now_string null !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### round_year ################################################# # -# Test lower boundary of round year -select cast('1399-05-01' as - timestamp FORMAT 'RRRR-MM-DD'); -EXPR$0 -NULL -!ok - select cast('1400-05-21' as timestamp FORMAT 'RRRR-MM-DD'); EXPR$0 @@ -361,7 +351,7 @@ EXPR$0 select cast('2017-05-31' as timestamp FORMAT 'RRRR-MM-DD'); EXPR$0 -2017-05-31 00:00:00 +0017-05-31 00:00:00 !ok # RRRR with 3-digit year fills digits from current year @@ -371,13 +361,6 @@ EXPR$0 2017-01-31 00:00:00 !ok -# RRRR wit 1-digit year fills digits from current year -select cast('0-07-31' as - timestamp FORMAT 'RRRR-MM-DD'); -EXPR$0 -2010-07-31 00:00:00 -!ok - # RR with 1-digit year fills digits from current year select cast('9-08-31' as timestamp FORMAT 'RR-MM-DD'); @@ -406,12 +389,6 @@ EXPR$0 2049-03-31 00:00:00 !ok -select cast('50-03-31' as - timestamp FORMAT 'RR-MM-DD'); -EXPR$0 -1950-03-31 00:00:00 -!ok - # Round year when last 2 digits of current year is greater than 49 !set now_string '2050-01-01 11:11:11' select cast('49-03-31' as @@ -438,6 +415,13 @@ select cast('50-03-31' as EXPR$0 2050-03-31 00:00:00 !ok +!} + +select cast('50-03-31' as + timestamp FORMAT 'RR-MM-DD'); +EXPR$0 +1950-03-31 00:00:00 +!ok # In a datetime to sting cast round year act like regular 'YYYY' or 'YY' tokens. select cast(cast('2019-01-01' as timestamp) as varchar @@ -452,7 +436,6 @@ EXPR$0 19 !ok !set now_string null -!} ### month_name ################################################# # @@ -499,8 +482,6 @@ EXPR$0 2010-08-14 00:00:00 !ok -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### select cast('2010 14 September' as date FORMAT 'YYYY DD month'); EXPR$0 @@ -530,75 +511,72 @@ select cast('2010 14 january' as date FORMAT EXPR$0 2010-01-14 !ok -!} -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### # Test different lowercase vs uppercase scenarios with the datetime to string path. select cast(date'2010-10-18' as varchar FORMAT 'MONTH Month month'); EXPR$0 -OCTOBER October october +OCTOBER October october !ok select cast(cast('2010-11-18' as timestamp) as varchar FORMAT 'MONTH Month month'); EXPR$0 -NOVEMBER November november +NOVEMBER November november !ok select cast(date'2010-12-19' as varchar FORMAT 'MONTH Month month'); EXPR$0 -DECEMBER December december +DECEMBER December december !ok select cast(date'2010-01-19' as varchar FORMAT 'MONTH Month month'); EXPR$0 -JANUARY January january +JANUARY January january !ok select cast(date'2010-02-19' as varchar FORMAT 'MONTH Month month'); EXPR$0 -FEBRUARY February february +FEBRUARY February february !ok select cast(date'2010-03-19' as varchar FORMAT 'MONTH Month month'); EXPR$0 -MARCH March march +MARCH March march !ok select cast(date'2010-04-19' as varchar FORMAT 'MONTH Month month'); EXPR$0 -APRIL April april +APRIL April april !ok select cast(date'2010-05-19' as varchar FORMAT 'MONTH Month month'); EXPR$0 -MAY May may +MAY May may !ok select cast(date'2010-06-19' as varchar FORMAT 'MONTH Month month'); EXPR$0 -JUNE June june +JUNE June june !ok select cast(date'2010-07-19' as varchar FORMAT 'MONTH Month month'); EXPR$0 -JULY July july +JULY July july !ok select cast(date'2010-08-19' as varchar FORMAT 'MONTH Month month'); EXPR$0 -AUGUST August august +AUGUST August august !ok select cast(date'2010-09-19' as varchar FORMAT @@ -606,10 +584,9 @@ select cast(date'2010-09-19' as varchar FORMAT EXPR$0 SEPTEMBER September september !ok -!} !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### # Test odd casing of month token. select cast(date'2010-09-20' as varchar FORMAT 'MOnth MONth MONTh'); @@ -698,15 +675,6 @@ SEPTEMBER September september !ok !} -!if (false) { -# Incorrect month name. -select cast('2010 15 JU' as timestamp FORMAT - 'YYYY DD MONTH'); -EXPR$0 -NULL -!ok -!} - # MONTH token without surrounding separators. select cast('2010SEPTEMBER17' as date FORMAT 'YYYYMONTHDD'); @@ -720,13 +688,10 @@ EXPR$0 2010-10-17 00:00:00 !ok -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### -# Applying FX and FM modifiers on Month token. select cast(cast('2010-07-20' as timestamp) as varchar FORMAT 'YYYYmonthDD'); EXPR$0 -2010july 20 +2010july20 !ok select cast(date'2010-09-20' as varchar @@ -735,6 +700,10 @@ EXPR$0 2010september20 !ok +!if (false) { +### unsupported format model/elements, test disabled ### +# Applying FX and FM modifiers on Month token. + select cast(cast('2010-08-20' as timestamp) as varchar FORMAT 'YYYYFMMonthDD'); EXPR$0 @@ -817,8 +786,6 @@ EXPR$0 2015-08-14 00:00:00 !ok -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### select cast('2015 14 Sep' as timestamp FORMAT 'YYYY DD mon'); EXPR$0 @@ -922,19 +889,14 @@ EXPR$0 SEP Sep sep !ok +!if (false) { +### unsupported format model/elements, test disabled ### # Test odd casing of short month token. select cast(date'2010-09-22' as varchar FORMAT 'MOn mON moN'); EXPR$0 SEP sep sep !ok - -# Incorrect month name. -select cast('2015 15 JU' as timestamp FORMAT - 'YYYY DD MON'); -EXPR$0 -NULL -!ok !} # MON token without separators in the format. @@ -944,14 +906,14 @@ EXPR$0 2015-08-17 !ok -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### select cast(cast('2015-07-20' as timestamp) as varchar FORMAT 'YYYYmonDD'); EXPR$0 2015jul20 !ok +!if (false) { +### unsupported format model/elements, test disabled ### # FX/FM has no effect on MON. select cast(cast('2015-08-21' as timestamp) as varchar FORMAT 'FXYYYYmonDD'); @@ -966,8 +928,6 @@ EXPR$0 !ok !} -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### ### week_of_year ################################################# # select cast(cast('2019-01-01' as timestamp) as varchar @@ -979,7 +939,7 @@ EXPR$0 select cast(date'2019-01-07' as varchar FORMAT 'WW'); EXPR$0 -01 +02 !ok select cast(cast('2019-01-08' as timestamp) as varchar @@ -1003,13 +963,13 @@ EXPR$0 select cast(date'2019-12-01' as varchar FORMAT 'WW'); EXPR$0 -48 +49 !ok select cast(cast('2019-12-02' as timestamp) as varchar FORMAT 'WW'); EXPR$0 -48 +49 !ok select cast(date'2019-12-03' as varchar @@ -1021,13 +981,13 @@ EXPR$0 select cast(cast('2019-12-30' as timestamp) as varchar FORMAT 'WW'); EXPR$0 -52 +01 !ok select cast(date'2019-12-31' as varchar FORMAT 'WW'); EXPR$0 -53 +01 !ok select cast(cast('2020-01-01' as timestamp) as varchar @@ -1035,10 +995,7 @@ select cast(cast('2020-01-01' as timestamp) as varchar EXPR$0 01 !ok -!} -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### ### week_of_month ################################################# # select cast(cast('2019-01-01' as timestamp) as varchar @@ -1050,7 +1007,7 @@ EXPR$0 select cast(date'2019-01-07' as varchar FORMAT 'W'); EXPR$0 -1 +2 !ok select cast(cast('2019-01-08' as timestamp) as varchar @@ -1062,7 +1019,7 @@ EXPR$0 select cast(date'2019-01-14' as varchar FORMAT 'W'); EXPR$0 -2 +3 !ok select cast(cast('2019-01-15' as timestamp) as varchar @@ -1074,7 +1031,7 @@ EXPR$0 select cast(date'2019-01-21' as varchar FORMAT 'W'); EXPR$0 -3 +4 !ok select cast(cast('2019-01-22' as timestamp) as varchar @@ -1086,7 +1043,7 @@ EXPR$0 select cast(date'2019-01-28' as varchar FORMAT 'W'); EXPR$0 -4 +5 !ok select cast(cast('2019-01-29' as timestamp) as varchar @@ -1100,7 +1057,6 @@ select cast(date'2019-02-01' as varchar EXPR$0 1 !ok -!} ### day_in_year ################################################# # @@ -1130,13 +1086,10 @@ EXPR$0 2019-12-31 00:00:00 !ok -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### select cast('2019 366' as timestamp FORMAT 'YYYY DDD'); EXPR$0 -NULL +2020-01-01 00:00:00 !ok -!} # Test "day in year" token in a leap year scenario select cast('2000 60' as timestamp FORMAT 'YYYY DDD'); @@ -1155,7 +1108,7 @@ EXPR$0 !ok !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### select cast('2000 367' as timestamp FORMAT 'YYYY DDD'); EXPR$0 NULL @@ -1195,7 +1148,7 @@ EXPR$0 !ok !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### day_name ################################################# # # String to datetime: Test different lowercase vs uppercase scenarios. @@ -1263,6 +1216,7 @@ select cast('2010-Monday-09' as timestamp FORMAT 'FXIYYY-FMDAY-IW'), EXPR$0, EXPR$1, EXPR$2 2010-03-01 00:00:00, 2010-03-01 00:00:00, 2010-03-01 !ok +!} # Datetime to string: Different lowercase and uppercase scenarios. select cast(date'2019-11-13' as varchar @@ -1274,39 +1228,41 @@ WEDNESDAY Wednesday wednesday WED Wed wed select cast(cast('2019-11-14' as timestamp) as varchar format 'DAY Day day DY Dy dy'); EXPR$0 -THURSDAY Thursday thursday THU Thu thu +THURSDAY Thursday thursday THU Thu thu !ok select cast(date'2019-11-15' as varchar format 'DAY Day day DY Dy dy'); EXPR$0 -FRIDAY Friday friday FRI Fri fri +FRIDAY Friday friday FRI Fri fri !ok select cast(cast('2019-11-16' as timestamp) as varchar format 'DAY Day day DY Dy dy'); EXPR$0 -SATURDAY Saturday saturday SAT Sat sat +SATURDAY Saturday saturday SAT Sat sat !ok select cast(date'2019-11-17' as varchar format 'DAY Day day DY Dy dy'); EXPR$0 -SUNDAY Sunday sunday SUN Sun sun +SUNDAY Sunday sunday SUN Sun sun !ok select cast(cast('2019-11-18' as timestamp) as varchar format 'DAY Day day DY Dy dy'); EXPR$0 -MONDAY Monday monday MON Mon mon +MONDAY Monday monday MON Mon mon !ok select cast(date'2019-11-19' as varchar format 'DAY Day day DY Dy dy'); EXPR$0 -TUESDAY Tuesday tuesday TUE Tue tue +TUESDAY Tuesday tuesday TUE Tue tue !ok +!if (false) { +### unsupported format model/elements, test disabled ### # Datetime to string: Different lowercase and uppercase scenarios when FM is provided. select cast(cast('2019-11-13' as timestamp) as varchar format 'FMDAY FMDay FMday FMDY FMDy FMdy'); @@ -1356,12 +1312,13 @@ select cast(date'2010-01-20' as varchar FORMAT EXPR$0 WEDNESDAY wednesday wednesday wed !ok +!} # Datetime to string: Day token without surrounding separators. select cast(date'2019-11-11' as varchar format 'YYYYDayMonth'); EXPR$0 -2019Monday November +2019MondayNovember !ok select cast(cast('2019-11-12' as timestamp) as varchar @@ -1373,7 +1330,7 @@ EXPR$0 select cast(date'2019-11-11' as varchar format 'YYYYDayMonth'); EXPR$0 -2019Monday November +2019MondayNovember !ok select cast(cast('2019-11-12' as timestamp) as varchar @@ -1382,11 +1339,13 @@ EXPR$0 2019TUE12 !ok +!if (false) { +### unsupported format model/elements, test disabled ### # Datetime to string: Day token with FM and FX modifiers. select cast(cast('2019-01-01' as timestamp) as varchar format 'FXYYYY DAY DD'); EXPR$0 -2019 TUESDAY 01 +2019 TUESDAY 01 !ok select cast(date'2019-01-01' as varchar @@ -1408,15 +1367,13 @@ EXPR$0 !ok !} -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### ### second_of_day ################################################# # # Check boundaries select cast('2019-11-10 86399.11' as timestamp FORMAT 'YYYY-MM-DD SSSSS.FF2'); EXPR$0 -2019-11-10 23:59:59.110000000 +2019-11-10 23:59:59 !ok select cast('2019-11-10 0' as @@ -1425,6 +1382,8 @@ EXPR$0 2019-11-10 00:00:00 !ok +!if (false) { +### unsupported format model/elements, test disabled ### # Without separators full 5-digit "second of day" has to be given select cast('11-10 036612019' as timestamp FORMAT 'MM-DD SSSSSYYYY'); @@ -1438,6 +1397,7 @@ select cast('2019-11-10 036611010' as EXPR$0 2019-11-10 01:01:01 !ok +!} # Timestamp to string formatting select cast(cast('2019-01-01 01:01:01' as timestamp) @@ -1457,7 +1417,6 @@ select cast(cast('2019-01-01 23:59:59' as timestamp) EXPR$0 86399 !ok -!} ### day_of_week ################################################# # @@ -1501,7 +1460,7 @@ EXPR$0 !ok !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### # FX and FM modifier does not pad day of week values with zeros. select cast(cast('2019-12-01' as date) as varchar FORMAT 'FXD'); @@ -1517,7 +1476,7 @@ EXPR$0 !} !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### fraction_seconds ################################################# # select cast('2019-11-08 123456789' as @@ -1573,7 +1532,7 @@ NULL !} !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### meridiem_indicator ################################################# # # Check 12 hour diff between AM and PM @@ -1662,7 +1621,7 @@ EXPR$0 !} !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### timezone_offsets ################################################# # # Test positive timezone offset. @@ -1823,7 +1782,7 @@ NULL !} !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### text_token ################################################# # # Parse ISO:8601 tokens using the text token. @@ -2045,7 +2004,7 @@ EXPR$0 !} !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### # Backslash in format that escapes non-special chars. select cast("1985- some \ text12-05" as date format 'YYYY-"some \ text"MM-DD'); @@ -2139,7 +2098,7 @@ String to Date parse failed. Input '1985-AB"CD11-23' doesn't match with format ' !} !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### iso8601_week_based_date_tokens ################################################# # # Format 0001-01-01 and 9999-12-31 dates. @@ -2288,7 +2247,7 @@ EXPR$0, EXPR$1, EXPR$2, EXPR$3, EXPR$4, EXPR$5, EXPR$6, EXPR$7 !} !if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### +### unsupported format model/elements, test disabled ### ### fm_fx_modifiers ################################################# # # Exact mathcing for the whole format. @@ -2615,15 +2574,12 @@ EXPR$0 2001 4 12 !ok -!if (false) { -### disabled until Bug.CALCITE_6269_FIXED ### ### format_parse_errors ################################################# # # Invalid format select cast('2017-05-01' as timestamp format 'XXXX-dd-MM'); -Bad date/time conversion format: XXXX-dd-MM +invalid ISO 8601 format: length=4 !error -!} # Invalid use of SimpleDateFormat select cast('2017-05-01 15:10' as timestamp format 'yyyy-MM-dd +hh:mm'); @@ -2648,16 +2604,14 @@ select cast('2017-05-01' as timestamp format 'YYY-MM-DD-Y'); Invalid format: 'YYY-MM-DD-Y' for datetime string: '2017-05-01' !error -!if (false) { # Year and round year conflict select cast('2017-05-01' as timestamp format 'YY-MM-DD-RRRR'); -Both year and round year are provided +Invalid format: 'YY-MM-DD-RRRR' for datetime string: '2017-05-01' !error select cast('2017-05-01' as timestamp format 'RR-MM-DD-YYY'); -Both year and round year are provided +Invalid format: 'RR-MM-DD-YYY' for datetime string: '2017-05-01' !error -!} # Quarter token not allowed in a string to datetime conversion. select cast('2017-1-01' as timestamp format 'YYYY-Q-DDD'); @@ -2742,45 +2696,49 @@ select cast('2017-05-01' as timestamp format 'YYYY-MM-DD HH12:HH'); Invalid format: 'YYYY-MM-DD HH12:HH' for datetime string: '2017-05-01' !error -!if (false) { # Conflict with median indicator select cast('2017-05-01' as timestamp format 'YYYY-MM-DD AM HH:MI A.M.'); -Multiple median indicator tokens provided +Invalid format: 'YYYY-MM-DD AM HH:MI A.M.' for datetime string: '2017-05-01' !error select cast('2017-05-01' as timestamp format 'YYYY-MM-DD PM HH:MI am'); -Multiple median indicator tokens provided +Invalid format: 'YYYY-MM-DD PM HH:MI am' for datetime string: '2017-05-01' !error select cast('2017-05-01' as timestamp format 'YYYY-MM-DD HH24:MI a.m.'); -Conflict between median indicator and hour token +Invalid format: 'YYYY-MM-DD HH24:MI a.m.' for datetime string: '2017-05-01' !error select cast('2017-05-01' as timestamp format 'YYYY-MM-DD p.m.'); -Missing hour token +Invalid format: 'YYYY-MM-DD p.m.' for datetime string: '2017-05-01' !error # Conflict with second of day select cast('2017-05-01' as timestamp format 'YYYY-MM-DD SSSSS HH'); -Second of day token conflicts with other token(s) +Invalid format: 'YYYY-MM-DD SSSSS HH' for datetime string: '2017-05-01' !error select cast('2017-05-01' as timestamp format 'YYYY-MM-DD HH12:SSSSS'); -Second of day token conflicts with other token(s) +Invalid format: 'YYYY-MM-DD HH12:SSSSS' for datetime string: '2017-05-01' !error select cast('2017-05-01' as timestamp format 'YYYY-MM-DD HH24SSSSS'); -Second of day token conflicts with other token(s) +Invalid format: 'YYYY-MM-DD HH24SSSSS' for datetime string: '2017-05-01' !error select cast('2017-05-01' as timestamp format 'YYYY-MM-DD MI SSSSS'); -Second of day token conflicts with other token(s) +Invalid format: 'YYYY-MM-DD MI SSSSS' for datetime string: '2017-05-01' !error select cast('2017-05-01' as timestamp format 'YYYY-MM-DD SS SSSSS'); -Second of day token conflicts with other token(s) +Invalid format: 'YYYY-MM-DD SS SSSSS' for datetime string: '2017-05-01' +!error + +# Incorrect month name. +select cast('2010 15 JU' as timestamp FORMAT + 'YYYY DD MONTH'); +Invalid format: 'YYYY DD MONTH' for datetime string: '2010 15 JU' !error -!} # Too long format # (Format string consists of 's' 101 times) @@ -2816,37 +2774,37 @@ select cast('2018-12-31 08:00 AM -59' as timestamp FORMAT 'YYYY-MM-DD HH12:MI A.M. TZM'); TZH token is required for TZM !error +!} # Multiple fraction second token conflict select cast('2018-10-10' as timestamp format 'YYYY-MM-DD FF FF1'); -Multiple fractional second tokens provided. +Invalid format: 'YYYY-MM-DD FF FF1' for datetime string: '2018-10-10' !error select cast('2018-10-10' as timestamp format 'YYYY-MM-DD FF2 FF3'); -Multiple fractional second tokens provided. +Invalid format: 'YYYY-MM-DD FF2 FF3' for datetime string: '2018-10-10' !error select cast('2018-10-10' as timestamp format 'YYYY-MM-DD FF4 FF5'); -Multiple fractional second tokens provided. +Invalid format: 'YYYY-MM-DD FF4 FF5' for datetime string: '2018-10-10' !error select cast('2018-10-10' as timestamp format 'YYYY-MM-DD FF6 FF7'); -Multiple fractional second tokens provided. +Invalid format: 'YYYY-MM-DD FF6 FF7' for datetime string: '2018-10-10' !error select cast('2018-10-10' as timestamp format 'YYYY-MM-DD FF8 FF9'); -Multiple fractional second tokens provided. +Invalid format: 'YYYY-MM-DD FF8 FF9' for datetime string: '2018-10-10' !error # No date token select cast('2020-05-05' as timestamp format 'FF1'); -No date tokens provided. +Invalid format: 'FF1' for datetime string: '2020-05-05' !error select cast('2020-05-05' as timestamp format 'SSSSS'); -No date tokens provided. +Invalid format: 'SSSSS' for datetime string: '2020-05-05' !error -!} select cast('2020-05-05' as timestamp format 'HH:MI:SS'); Invalid format: 'HH:MI:SS' for datetime string: '2020-05-05' diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java index d1eb2c87babc..47b802763c9d 100644 --- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java +++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java @@ -1355,33 +1355,48 @@ void testCastFormatClauseDateTimeToString(CastType castType, SqlOperatorFixture f.checkString("cast(date '2018-01-30' as varchar format 'YYYY')", "2018", "VARCHAR NOT NULL"); - - if (Bug.CALCITE_6269_FIXED) { - f.checkString("cast(date '12018-01-30' as varchar format 'YYYY')", - "12018", - "VARCHAR NOT NULL"); - f.checkString("cast(date '2018-01-30' as varchar format 'Y')", - "8", - "VARCHAR NOT NULL"); - f.checkString("cast(date '2018-01-30' as varchar format 'YYY')", - "018", - "VARCHAR NOT NULL"); - f.checkString("cast(date '2018-01-30' as varchar format 'MONTH')", - "JANUARY", - "VARCHAR NOT NULL"); - } - + f.checkString("cast(date '2018-01-30' as varchar format 'YYY')", + "018", + "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-01-30' as varchar format 'Y')", + "8", + "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-11-30' as varchar format 'Month')", + "November", + "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-01-30' as varchar format 'MONTH')", + "JANUARY", + "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-9-30' as varchar format 'mon')", + "sep", + "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-12-30' as varchar format 'Mon')", + "Dec", + "VARCHAR NOT NULL"); f.checkString("cast(date '2018-01-30' as varchar format 'MON')", - "Jan", + "JAN", "VARCHAR NOT NULL"); f.checkString("cast(date '2018-01-30' as varchar format 'MM')", "01", "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-01-30' as varchar format 'DAY')", + "TUESDAY", + "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-01-30' as varchar format 'Day')", "Tuesday", "VARCHAR NOT NULL"); - f.checkString("cast(date '2018-01-30' as varchar format 'DY')", - "Tue", + f.checkString("cast(date '2018-01-30' as varchar format 'day')", + "tuesday", + "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-01-01' as varchar format 'DY')", + "MON", + "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-01-01' as varchar format 'Dy')", + "Mon", + "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-01-01' as varchar format 'dy')", + "mon", "VARCHAR NOT NULL"); f.checkString("cast(date '2018-01-30' as varchar format 'D')", "3", @@ -1392,11 +1407,12 @@ void testCastFormatClauseDateTimeToString(CastType castType, SqlOperatorFixture f.checkString("cast(date '2018-06-30' as varchar format 'DDD')", "181", "VARCHAR NOT NULL"); + f.checkString("cast(date '2018-01-30' as varchar format 'MM-DD-YY')", "01-30-18", "VARCHAR NOT NULL"); f.checkString("cast(date '2021-12-21' as varchar format 'YY Q MON DD')", - "21 4 Dec 21", + "21 4 DEC 21", "VARCHAR NOT NULL"); // Cast TIME to String @@ -1415,24 +1431,43 @@ void testCastFormatClauseDateTimeToString(CastType castType, SqlOperatorFixture f.checkString("cast(time '15:45:10' as varchar format 'HH12:MI')", "03:45", "VARCHAR NOT NULL"); - - if (Bug.CALCITE_6269_FIXED) { - f.checkString("cast(time '21:30:25.16' as varchar format 'SSSSS')", - "25", - "VARCHAR NOT NULL"); - f.checkString("cast(time '23:30:55.43' as varchar format 'FF1')", - "4", - "VARCHAR NOT NULL"); - f.checkString("cast(time '23:30:55.43' as varchar format 'AM')", - "PM", - "VARCHAR NOT NULL"); - f.checkString("cast(time '12:30:55' as varchar format 'PM')", - "PM", + f.checkString("cast(time '21:30:25.16' as varchar format 'SSSSS')", + "77425", + "VARCHAR NOT NULL"); + f.checkString("cast(time '00:00:00.23' as varchar format 'SSSSS')", + "00000", + "VARCHAR NOT NULL"); + f.checkString("cast(time '01:59:59.99' as varchar format 'SSSSS')", + "07199", + "VARCHAR NOT NULL"); + f.checkString("cast(time '23:30:55.43' as varchar format 'AM')", + "PM", + "VARCHAR NOT NULL"); + f.checkString("cast(time '12:30:55' as varchar format 'PM')", + "PM", + "VARCHAR NOT NULL"); + f.checkString("cast(time '08:45:12' as varchar format 'P.M.')", + "A.M.", + "VARCHAR NOT NULL"); + f.checkString("cast(time '16:17:12' as varchar format 'am')", + "pm", + "VARCHAR NOT NULL"); + f.checkString("cast(time '02:23:23' as varchar format 'p.m.')", + "a.m.", + "VARCHAR NOT NULL"); + f.checkString("cast(time '23:30:55.4757' as varchar format 'FF2')", + "47", + "VARCHAR NOT NULL"); + f.checkString("cast(time '23:30:55.1233793' as varchar format 'FF5')", + "12300", + "VARCHAR NOT NULL"); + f.checkString("cast(time '23:30:55.435712' as varchar format 'FF9')", + "435000000", "VARCHAR NOT NULL"); - } + // Cast TIMESTAMP to String - if (Bug.CALCITE_6269_FIXED) { + if (Bug.CALCITE_6367_FIXED) { // Query output cannot be validated as it's dependent on execution time zone f.checkQuery("cast(timestamp '2008-12-25 00:00:00+06:00' as varchar format 'TZH')"); f.checkString("cast(timestamp '2008-12-25 00:00:00+00:00' as varchar format " @@ -1464,7 +1499,7 @@ void testCastFormatClauseStringToDateTime(CastType castType, SqlOperatorFixture "2020-06-03 12:42:53", "TIMESTAMP(0) NOT NULL"); - if (Bug.CALCITE_6269_FIXED) { + if (Bug.CALCITE_6367_FIXED) { f.checkScalar("cast('2020.06.03 00:00:53+06:30' as timestamp format" + " 'YYYY.MM.DD HH24:MI:SSTZH:TZM')", "2020-06-02 17:30:53 UTC", @@ -4673,10 +4708,10 @@ void testBitGetFunc(SqlOperatorFixture f, String functionName) { "Monday", "VARCHAR NOT NULL"); f.checkString("to_char(timestamp '2022-06-03 12:15:48.678', 'DY')", - "Fri", + "FRI", "VARCHAR NOT NULL"); f.checkString("to_char(timestamp '0001-01-01 00:00:00.000', 'DY')", - "Mon", + "MON", "VARCHAR NOT NULL"); f.checkString("to_char(timestamp '2022-06-03 12:15:48.678', 'CC')", "21", @@ -13215,8 +13250,8 @@ void testTimestampDiff(boolean coercionEnabled) { f.checkScalar("FORMAT_DATE('%x', DATE '2008-12-25')", "12/25/08", "VARCHAR NOT NULL"); - f.checkScalar("FORMAT_DATE('The date is: %x', DATE '2008-12-25')", - "The date is: 12/25/08", + f.checkScalar("FORMAT_DATE('%x', DATE '2008-12-25')", + "12/25/08", "VARCHAR NOT NULL"); f.checkNull("FORMAT_DATE('%x', CAST(NULL AS DATE))"); f.checkNull("FORMAT_DATE('%b-%d-%Y', CAST(NULL AS DATE))"); @@ -13259,7 +13294,7 @@ void testTimestampDiff(boolean coercionEnabled) { "VARCHAR(2000) NOT NULL"); f.checkScalar("FORMAT_TIMESTAMP('The time is: %R.%E2S'," + " TIMESTAMP WITH LOCAL TIME ZONE '2008-12-25 15:30:00.1235456')", - "The time is: 15:30.123", + "The time is: 15:30.12", "VARCHAR(2000) NOT NULL"); }