diff --git common/src/java/org/apache/hadoop/hive/common/format/datetime/HiveSqlDateTimeFormatter.java common/src/java/org/apache/hadoop/hive/common/format/datetime/HiveSqlDateTimeFormatter.java index 168a6f2113..f6a52e9557 100644 --- common/src/java/org/apache/hadoop/hive/common/format/datetime/HiveSqlDateTimeFormatter.java +++ common/src/java/org/apache/hadoop/hive/common/format/datetime/HiveSqlDateTimeFormatter.java @@ -243,9 +243,14 @@ * * A.2. Character temporals * Temporal elements, but spelled out. - * - For datetime to string conversion, the pattern's case must match one of the listed formats - * (e.g. mOnTh is not accepted) to avoid ambiguity. Output is right padded with trailing spaces - * unless the pattern is marked with the fill mode modifier (FM). + * - Output is right padded with trailing spaces unless the pattern is marked with the fill mode + * modifier (FM). Capitalization happens as follows: + * - If the first letter of the pattern is lowercase then the output is lowercase: + * 'mONTH' -> 'may' + * - If the first two letters of the pattern are uppercase then the output is uppercase: + * 'MOnth' -> 'MAY' + * - If the first letter of the pattern is uppercase and the second is lowercase then the output + * is capitalized: 'Month' -> 'May'. * - For string to datetime conversion, the case of the pattern does not matter. * * MONTH|Month|month @@ -868,14 +873,6 @@ private void verifyForFormat() { throw new IllegalArgumentException(token.string.toUpperCase() + " not a valid format for " + "timestamp or date."); } - if (token.type == TokenType.CHARACTER_TEMPORAL) { - String s = token.string; - if (!(s.equals(s.toUpperCase()) || s.equals(capitalize(s)) || s.equals(s.toLowerCase()))) { - throw new IllegalArgumentException( - "Ambiguous capitalization of token " + s + ". Accepted " + "forms are " + s - .toUpperCase() + ", " + capitalize(s) + ", or " + s.toLowerCase() + "."); - } - } } } @@ -965,11 +962,17 @@ private String formatCharacterTemporal(int value, Token token) { output = StringUtils.rightPad(output, token.length); //pad to size } - // set case - if (Character.isUpperCase(token.string.charAt(1))) { - output = output.toUpperCase(); - } else if (Character.isLowerCase(token.string.charAt(0))) { + // Set case. + // If the first letter is lowercase then the output is lowercase: 'mONTH' -> 'may' + // If the first two letters are uppercase then the output is uppercase: 'MOnth' -> 'MAY' + // If the first letter is uppercase and the second is lowercase then the output is capitalized: + // 'Month' -> 'May'. + if (Character.isLowerCase(token.string.charAt(0))) { output = output.toLowerCase(); + } else if (Character.isUpperCase(token.string.charAt(1))) { + output = output.toUpperCase(); + } else { + output = capitalize(output); } return output; } diff --git common/src/test/org/apache/hadoop/hive/common/format/datetime/TestHiveSqlDateTimeFormatter.java common/src/test/org/apache/hadoop/hive/common/format/datetime/TestHiveSqlDateTimeFormatter.java index ea60a31088..3abf28b0b8 100644 --- common/src/test/org/apache/hadoop/hive/common/format/datetime/TestHiveSqlDateTimeFormatter.java +++ common/src/test/org/apache/hadoop/hive/common/format/datetime/TestHiveSqlDateTimeFormatter.java @@ -103,12 +103,6 @@ public void testSetPatternWithBadPatterns() { verifyBadPattern("tzm", false); verifyBadPattern("tzh", false); - //ambiguous case for formatting - verifyBadPattern("MOnth", false); - verifyBadPattern("DaY", false); - verifyBadPattern("dAy", false); - verifyBadPattern("dY", false); - //illegal for parsing verifyBadPattern("yyyy-mm-dd q", true); verifyBadPattern("yyyy-mm-dd d", true); @@ -130,15 +124,24 @@ public void testFormatTimestamp() { checkFormatTs("YYYY-MM-DD HH12PM", "2017-05-05 00:00:00", "2017-05-05 12AM"); checkFormatTs("YYYY-MONTH-DD", "2019-01-01 00:00:00", "2019-JANUARY -01"); //fill to length 9 + checkFormatTs("YYYY-MOnth-DD", "2019-01-01 00:00:00", "2019-JANUARY -01"); checkFormatTs("YYYY-Month-DD", "2019-01-01 00:00:00", "2019-January -01"); + checkFormatTs("YYYY-MoNTH-DD", "2019-01-01 00:00:00", "2019-January -01"); checkFormatTs("YYYY-month-DD", "2019-01-01 00:00:00", "2019-january -01"); + checkFormatTs("YYYY-mONTH-DD", "2019-01-01 00:00:00", "2019-january -01"); checkFormatTs("YYYY-MON-DD", "2019-01-01 00:00:00", "2019-JAN-01"); + checkFormatTs("YYYY-MOn-DD", "2019-01-01 00:00:00", "2019-JAN-01"); checkFormatTs("YYYY-Mon-DD", "2019-01-01 00:00:00", "2019-Jan-01"); + checkFormatTs("YYYY-MoN-DD", "2019-01-01 00:00:00", "2019-Jan-01"); checkFormatTs("YYYY-mon-DD", "2019-01-01 00:00:00", "2019-jan-01"); + checkFormatTs("YYYY-mON-DD", "2019-01-01 00:00:00", "2019-jan-01"); checkFormatTs("D: DAY", "2019-01-01 00:00:00", "3: TUESDAY "); //fill to length 9 + checkFormatTs("D: DAy", "2019-01-01 00:00:00", "3: TUESDAY "); checkFormatTs("D: Day", "2019-01-02 00:00:00", "4: Wednesday"); + checkFormatTs("D: DaY", "2019-01-02 00:00:00", "4: Wednesday"); checkFormatTs("D: day", "2019-01-03 00:00:00", "5: thursday "); + checkFormatTs("D: dAY", "2019-01-03 00:00:00", "5: thursday "); checkFormatTs("D: DY", "2019-01-04 00:00:00", "6: FRI"); checkFormatTs("D: Dy", "2019-01-05 00:00:00", "7: Sat"); checkFormatTs("D: dy", "2019-01-06 00:00:00", "1: sun");