提交 00a1b0e2 authored 作者: Thomas Mueller Graf's avatar Thomas Mueller Graf

Formatting

上级 7e8fa541
/* /*
* Copyright 2004-2016 H2 Group. Multiple-Licensed under the MPL 2.0, * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: Daniel Gredler * Initial Developer: Daniel Gredler
*/ */
...@@ -26,8 +26,9 @@ public class ToDateParser { ...@@ -26,8 +26,9 @@ public class ToDateParser {
/** /**
* @param input the input date with the date-time info * @param input the input date with the date-time info
* @param format the format of date-time info * @param format the format of date-time info
* @param functionName one of [TO_DATE, TO_TIMESTAMP] (both share the same code) * @param functionName one of [TO_DATE, TO_TIMESTAMP] (both share the same
* code)
*/ */
private ToDateParser(ConfigParam functionName, String input, String format) { private ToDateParser(ConfigParam functionName, String input, String format) {
// reset calendar - default oracle behaviour // reset calendar - default oracle behaviour
...@@ -72,7 +73,8 @@ public class ToDateParser { ...@@ -72,7 +73,8 @@ public class ToDateParser {
private Timestamp getResultingTimestamp() { private Timestamp getResultingTimestamp() {
Calendar cal = (Calendar) getResultCalendar().clone(); Calendar cal = (Calendar) getResultCalendar().clone();
int nanosToSet = nanos == null ? cal.get(Calendar.MILLISECOND) * 1000000 : nanos.intValue(); int nanosToSet = nanos == null ?
cal.get(Calendar.MILLISECOND) * 1000000 : nanos.intValue();
cal.set(Calendar.MILLISECOND, 0); cal.set(Calendar.MILLISECOND, 0);
Timestamp ts = new Timestamp(cal.getTimeInMillis()); Timestamp ts = new Timestamp(cal.getTimeInMillis());
ts.setNanos(nanosToSet); ts.setNanos(nanosToSet);
...@@ -114,7 +116,8 @@ public class ToDateParser { ...@@ -114,7 +116,8 @@ public class ToDateParser {
private static ToDateParser parse(ToDateParser p) { private static ToDateParser parse(ToDateParser p) {
while (p.hasToParseData()) { while (p.hasToParseData()) {
List<ToDateTokenizer.FormatTokenEnum> tokenList = ToDateTokenizer.FormatTokenEnum.getTokensInQuestion(p.getFormatStr()); List<ToDateTokenizer.FormatTokenEnum> tokenList =
ToDateTokenizer.FormatTokenEnum.getTokensInQuestion(p.getFormatStr());
if (tokenList.isEmpty()) { if (tokenList.isEmpty()) {
p.removeFirstChar(); p.removeFirstChar();
continue; continue;
...@@ -154,7 +157,8 @@ public class ToDateParser { ...@@ -154,7 +157,8 @@ public class ToDateParser {
int currentFormatPos = orgFormatLen - formatStr.length(); int currentFormatPos = orgFormatLen - formatStr.length();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(format("\n %s('%s', '%s')", functionName, unmodifiedInputStr, unmodifiedFormatStr)); sb.append(format("\n %s('%s', '%s')", functionName,
unmodifiedInputStr, unmodifiedFormatStr));
sb.append(format("\n %s^%s , %s^ <-- Parsing failed at this point", sb.append(format("\n %s^%s , %s^ <-- Parsing failed at this point",
format("%" + (functionName.name().length() + currentInputPos) + "s", ""), format("%" + (functionName.name().length() + currentInputPos) + "s", ""),
restInputLen <= 0 ? "" : format("%" + restInputLen + "s", ""), restInputLen <= 0 ? "" : format("%" + restInputLen + "s", ""),
......
/* /*
* Copyright 2004-2016 H2 Group. Multiple-Licensed under the MPL 2.0, * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: Daniel Gredler * Initial Developer: Daniel Gredler
*/ */
...@@ -26,19 +26,24 @@ import org.h2.api.ErrorCode; ...@@ -26,19 +26,24 @@ import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
/** /**
* Emulates Oracle's TO_DATE function.<br> * Emulates Oracle's TO_DATE function. This class knows all about the
* This class knows all about the TO_DATE-format conventions and how to parse the corresponding data * TO_DATE-format conventions and how to parse the corresponding data.
*/ */
class ToDateTokenizer { class ToDateTokenizer {
static final Pattern PATTERN_NUMBER = Pattern.compile("^([+-]?[0-9]+)"); static final Pattern PATTERN_NUMBER = Pattern.compile("^([+-]?[0-9]+)");
static final Pattern PATTERN_FOUR_DIGITS = Pattern.compile("^([+-]?[0-9]{4})"); static final Pattern PATTERN_FOUR_DIGITS = Pattern.compile("^([+-]?[0-9]{4})");
static final Pattern PATTERN_THREE_DIGITS = Pattern.compile("^([+-]?[0-9]{3})"); static final Pattern PATTERN_THREE_DIGITS = Pattern.compile("^([+-]?[0-9]{3})");
static final Pattern PATTERN_TWO_DIGITS = Pattern.compile("^([+-]?[0-9]{2})"); static final Pattern PATTERN_TWO_DIGITS = Pattern.compile("^([+-]?[0-9]{2})");
static final Pattern PATTERN_TWO_DIGITS_OR_LESS = Pattern.compile("^([+-]?[0-9][0-9]?)"); static final Pattern PATTERN_TWO_DIGITS_OR_LESS =
static final Pattern PATTERN_ONE_DIGIT = Pattern.compile("^([+-]?[0-9])"); Pattern.compile("^([+-]?[0-9][0-9]?)");
static final Pattern PATTERN_FF = Pattern.compile("^(FF[0-9]?)", Pattern.CASE_INSENSITIVE); static final Pattern PATTERN_ONE_DIGIT =
static final Pattern PATTERN_AM_PM = Pattern.compile("^(AM|A\\.M\\.|PM|P\\.M\\.)", Pattern.CASE_INSENSITIVE); Pattern.compile("^([+-]?[0-9])");
static final Pattern PATTERN_BC_AD = Pattern.compile("^(BC|B\\.C\\.|AD|A\\.D\\.)", Pattern.CASE_INSENSITIVE); static final Pattern PATTERN_FF =
Pattern.compile("^(FF[0-9]?)", Pattern.CASE_INSENSITIVE);
static final Pattern PATTERN_AM_PM =
Pattern.compile("^(AM|A\\.M\\.|PM|P\\.M\\.)", Pattern.CASE_INSENSITIVE);
static final Pattern PATTERN_BC_AD =
Pattern.compile("^(BC|B\\.C\\.|AD|A\\.D\\.)", Pattern.CASE_INSENSITIVE);
static final YearParslet PARSLET_YEAR = new YearParslet(); static final YearParslet PARSLET_YEAR = new YearParslet();
static final MonthParslet PARSLET_MONTH = new MonthParslet(); static final MonthParslet PARSLET_MONTH = new MonthParslet();
static final DayParslet PARSLET_DAY = new DayParslet(); static final DayParslet PARSLET_DAY = new DayParslet();
...@@ -46,10 +51,12 @@ class ToDateTokenizer { ...@@ -46,10 +51,12 @@ class ToDateTokenizer {
static final int MILLIS_IN_HOUR = 60 * 60 * 1000; static final int MILLIS_IN_HOUR = 60 * 60 * 1000;
/** /**
* Interface of the classes that can parse a specialized small bit of the TO_DATE format-string * Interface of the classes that can parse a specialized small bit of the
* TO_DATE format-string
*/ */
interface ToDateParslet { interface ToDateParslet {
void parse(ToDateParser params, FormatTokenEnum formatTokenEnum, String formatTokenStr); void parse(ToDateParser params, FormatTokenEnum formatTokenEnum,
String formatTokenStr);
} }
/** /**
...@@ -58,7 +65,7 @@ class ToDateTokenizer { ...@@ -58,7 +65,7 @@ class ToDateTokenizer {
static class YearParslet implements ToDateParslet { static class YearParslet implements ToDateParslet {
@Override @Override
public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum, public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum,
String formatTokenStr) { String formatTokenStr) {
Calendar result = params.getResultCalendar(); Calendar result = params.getResultCalendar();
String inputFragmentStr = null; String inputFragmentStr = null;
int dateNr = 0; int dateNr = 0;
...@@ -66,20 +73,25 @@ class ToDateTokenizer { ...@@ -66,20 +73,25 @@ class ToDateTokenizer {
case SYYYY: case SYYYY:
case YYYY: case YYYY:
case IYYY: case IYYY:
inputFragmentStr = matchStringOrThrow(PATTERN_FOUR_DIGITS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_FOUR_DIGITS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
// Gregorian calendar does not have a year 0. 0 = 0001 BC, -1 = 0002 BC, ... so we adjust // Gregorian calendar does not have a year 0.
// 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1); result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1);
break; break;
case YYY: case YYY:
case IYY: case IYY:
inputFragmentStr = matchStringOrThrow(PATTERN_THREE_DIGITS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_THREE_DIGITS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
// Gregorian calendar does not have a year 0. 0 = 0001 BC, -1 = 0002 BC, ... so we adjust // Gregorian calendar does not have a year 0.
// 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1); result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1);
break; break;
case RRRR: case RRRR:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
dateNr += dateNr < 50 ? 2000 : 1900; dateNr += dateNr < 50 ? 2000 : 1900;
result.set(Calendar.YEAR, dateNr); result.set(Calendar.YEAR, dateNr);
...@@ -87,38 +99,47 @@ class ToDateTokenizer { ...@@ -87,38 +99,47 @@ class ToDateTokenizer {
case RR: case RR:
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
int cc = calendar.get(Calendar.YEAR) / 100; int cc = calendar.get(Calendar.YEAR) / 100;
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr) + cc * 100; dateNr = parseInt(inputFragmentStr) + cc * 100;
result.set(Calendar.YEAR, dateNr); result.set(Calendar.YEAR, dateNr);
break; break;
case EE /*NOT supported yet*/: case EE /*NOT supported yet*/:
throwException(params, format("token '%s' not supported yet.", formatTokenEnum.name())); throwException(params, format(
"token '%s' not supported yet.", formatTokenEnum.name()));
break; break;
case E /*NOT supported yet*/: case E /*NOT supported yet*/:
throwException(params, format("token '%s' not supported yet.", formatTokenEnum.name())); throwException(params, format(
"token '%s' not supported yet.", formatTokenEnum.name()));
break; break;
case YY: case YY:
case IY: case IY:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
// Gregorian calendar does not have a year 0. 0 = 0001 BC, -1 = 0002 BC, ... so we adjust // Gregorian calendar does not have a year 0.
// 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1); result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1);
break; break;
case SCC: case SCC:
case CC: case CC:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr) * 100; dateNr = parseInt(inputFragmentStr) * 100;
result.set(Calendar.YEAR, dateNr); result.set(Calendar.YEAR, dateNr);
break; break;
case Y: case Y:
case I: case I:
inputFragmentStr = matchStringOrThrow(PATTERN_ONE_DIGIT, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_ONE_DIGIT, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
// Gregorian calendar does not have a year 0. 0 = 0001 BC, -1 = 0002 BC, ... so we adjust // Gregorian calendar does not have a year 0.
// 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1); result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1);
break; break;
case BC_AD: case BC_AD:
inputFragmentStr = matchStringOrThrow(PATTERN_BC_AD, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_BC_AD, params, formatTokenEnum);
if (inputFragmentStr.toUpperCase().startsWith("B")) { if (inputFragmentStr.toUpperCase().startsWith("B")) {
result.set(Calendar.ERA, GregorianCalendar.BC); result.set(Calendar.ERA, GregorianCalendar.BC);
} else { } else {
...@@ -126,7 +147,8 @@ class ToDateTokenizer { ...@@ -126,7 +147,8 @@ class ToDateTokenizer {
} }
break; break;
default: default:
throw new IllegalArgumentException(format("%s: Internal Error. Unhandled case: %s", this.getClass() throw new IllegalArgumentException(format(
"%s: Internal Error. Unhandled case: %s", this.getClass()
.getSimpleName(), formatTokenEnum)); .getSimpleName(), formatTokenEnum));
} }
params.remove(inputFragmentStr, formatTokenStr); params.remove(inputFragmentStr, formatTokenStr);
...@@ -137,29 +159,33 @@ class ToDateTokenizer { ...@@ -137,29 +159,33 @@ class ToDateTokenizer {
* Parslet responsible for parsing month parameter * Parslet responsible for parsing month parameter
*/ */
static class MonthParslet implements ToDateParslet { static class MonthParslet implements ToDateParslet {
private static final String[] ROMAN_MONTH = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", private static final String[] ROMAN_MONTH = { "I", "II", "III", "IV",
"XI", "XII" }; "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII" };
@Override @Override
public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum, public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum,
String formatTokenStr) { String formatTokenStr) {
Calendar result = params.getResultCalendar(); Calendar result = params.getResultCalendar();
String s = params.getInputStr(); String s = params.getInputStr();
String inputFragmentStr = null; String inputFragmentStr = null;
int dateNr = 0; int dateNr = 0;
switch (formatTokenEnum) { switch (formatTokenEnum) {
case MONTH: case MONTH:
inputFragmentStr = setByName(result, params, Calendar.MONTH, Calendar.LONG); inputFragmentStr = setByName(result, params,
Calendar.MONTH, Calendar.LONG);
break; break;
case Q /*NOT supported yet*/: case Q /*NOT supported yet*/:
throwException(params, format("token '%s' not supported yet.", formatTokenEnum.name())); throwException(params, format(
"token '%s' not supported yet.", formatTokenEnum.name()));
break; break;
case MON: case MON:
inputFragmentStr = setByName(result, params, Calendar.MONTH, Calendar.SHORT); inputFragmentStr = setByName(result, params,
Calendar.MONTH, Calendar.SHORT);
break; break;
case MM: case MM:
// Note: In Calendar Month go from 0 - 11 // Note: In Calendar Month go from 0 - 11
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
result.set(Calendar.MONTH, dateNr - 1); result.set(Calendar.MONTH, dateNr - 1);
break; break;
...@@ -168,7 +194,8 @@ class ToDateTokenizer { ...@@ -168,7 +194,8 @@ class ToDateTokenizer {
for (String monthName : ROMAN_MONTH) { for (String monthName : ROMAN_MONTH) {
dateNr++; dateNr++;
int len = monthName.length(); int len = monthName.length();
if (s.length() >= len && monthName.equalsIgnoreCase(s.substring(0, len))) { if (s.length() >= len &&
monthName.equalsIgnoreCase(s.substring(0, len))) {
result.set(Calendar.MONTH, dateNr); result.set(Calendar.MONTH, dateNr);
inputFragmentStr = monthName; inputFragmentStr = monthName;
break; break;
...@@ -176,12 +203,14 @@ class ToDateTokenizer { ...@@ -176,12 +203,14 @@ class ToDateTokenizer {
} }
if (inputFragmentStr == null || inputFragmentStr.isEmpty()) { if (inputFragmentStr == null || inputFragmentStr.isEmpty()) {
throwException(params, throwException(params,
format("Issue happened when parsing token '%s'. Expected one of: %s", format("Issue happened when parsing token '%s'. " +
"Expected one of: %s",
formatTokenEnum.name(), Arrays.toString(ROMAN_MONTH))); formatTokenEnum.name(), Arrays.toString(ROMAN_MONTH)));
} }
break; break;
default: default:
throw new IllegalArgumentException(format("%s: Internal Error. Unhandled case: %s", this.getClass() throw new IllegalArgumentException(format(
"%s: Internal Error. Unhandled case: %s", this.getClass()
.getSimpleName(), formatTokenEnum)); .getSimpleName(), formatTokenEnum));
} }
params.remove(inputFragmentStr, formatTokenStr); params.remove(inputFragmentStr, formatTokenStr);
...@@ -194,43 +223,51 @@ class ToDateTokenizer { ...@@ -194,43 +223,51 @@ class ToDateTokenizer {
static class DayParslet implements ToDateParslet { static class DayParslet implements ToDateParslet {
@Override @Override
public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum, public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum,
String formatTokenStr) { String formatTokenStr) {
Calendar result = params.getResultCalendar(); Calendar result = params.getResultCalendar();
String inputFragmentStr = null; String inputFragmentStr = null;
int dateNr = 0; int dateNr = 0;
switch (formatTokenEnum) { switch (formatTokenEnum) {
case DDD: case DDD:
inputFragmentStr = matchStringOrThrow(PATTERN_NUMBER, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_NUMBER, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
result.set(Calendar.DAY_OF_YEAR, dateNr); result.set(Calendar.DAY_OF_YEAR, dateNr);
break; break;
case DD: case DD:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
result.set(Calendar.DAY_OF_MONTH, dateNr); result.set(Calendar.DAY_OF_MONTH, dateNr);
break; break;
case D: case D:
inputFragmentStr = matchStringOrThrow(PATTERN_ONE_DIGIT, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_ONE_DIGIT, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
result.set(Calendar.DAY_OF_MONTH, dateNr); result.set(Calendar.DAY_OF_MONTH, dateNr);
break; break;
case DAY: case DAY:
inputFragmentStr = setByName(result, params, Calendar.DAY_OF_WEEK, Calendar.LONG); inputFragmentStr = setByName(result, params,
Calendar.DAY_OF_WEEK, Calendar.LONG);
break; break;
case DY: case DY:
inputFragmentStr = setByName(result, params, Calendar.DAY_OF_WEEK, Calendar.SHORT); inputFragmentStr = setByName(result, params,
Calendar.DAY_OF_WEEK, Calendar.SHORT);
break; break;
case J: case J:
inputFragmentStr = matchStringOrThrow(PATTERN_NUMBER, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_NUMBER, params, formatTokenEnum);
try { try {
Date date = new SimpleDateFormat("Myydd").parse(inputFragmentStr); Date date = new SimpleDateFormat("Myydd").parse(inputFragmentStr);
result.setTime(date); result.setTime(date);
} catch (ParseException e) { } catch (ParseException e) {
throwException(params, format("Failed to parse Julian date: %s", inputFragmentStr)); throwException(params, format(
"Failed to parse Julian date: %s", inputFragmentStr));
} }
break; break;
default: default:
throw new IllegalArgumentException(format("%s: Internal Error. Unhandled case: %s", this.getClass() throw new IllegalArgumentException(format(
"%s: Internal Error. Unhandled case: %s", this.getClass()
.getSimpleName(), formatTokenEnum)); .getSimpleName(), formatTokenEnum));
} }
params.remove(inputFragmentStr, formatTokenStr); params.remove(inputFragmentStr, formatTokenStr);
...@@ -244,41 +281,47 @@ class ToDateTokenizer { ...@@ -244,41 +281,47 @@ class ToDateTokenizer {
@Override @Override
public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum, public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum,
String formatTokenStr) { String formatTokenStr) {
Calendar result = params.getResultCalendar(); Calendar result = params.getResultCalendar();
String inputFragmentStr = null; String inputFragmentStr = null;
int dateNr = 0; int dateNr = 0;
switch (formatTokenEnum) { switch (formatTokenEnum) {
case HH24: case HH24:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
result.set(Calendar.HOUR_OF_DAY, dateNr); result.set(Calendar.HOUR_OF_DAY, dateNr);
break; break;
case HH12: case HH12:
case HH: case HH:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
result.set(Calendar.HOUR, dateNr); result.set(Calendar.HOUR, dateNr);
break; break;
case MI: case MI:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
result.set(Calendar.MINUTE, dateNr); result.set(Calendar.MINUTE, dateNr);
break; break;
case SS: case SS:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
result.set(Calendar.SECOND, dateNr); result.set(Calendar.SECOND, dateNr);
break; break;
case SSSSS: case SSSSS:
inputFragmentStr = matchStringOrThrow(PATTERN_NUMBER, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_NUMBER, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
result.set(Calendar.HOUR_OF_DAY, 0); result.set(Calendar.HOUR_OF_DAY, 0);
result.set(Calendar.MINUTE, 0); result.set(Calendar.MINUTE, 0);
result.set(Calendar.SECOND, dateNr); result.set(Calendar.SECOND, dateNr);
break; break;
case FF: case FF:
inputFragmentStr = matchStringOrThrow(PATTERN_NUMBER, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_NUMBER, params, formatTokenEnum);
String paddedRightNrStr = format("%-9s", inputFragmentStr).replace(' ', '0'); String paddedRightNrStr = format("%-9s", inputFragmentStr).replace(' ', '0');
paddedRightNrStr = paddedRightNrStr.substring(0, 9); paddedRightNrStr = paddedRightNrStr.substring(0, 9);
Double nineDigits = Double.parseDouble(paddedRightNrStr); Double nineDigits = Double.parseDouble(paddedRightNrStr);
...@@ -287,7 +330,8 @@ class ToDateTokenizer { ...@@ -287,7 +330,8 @@ class ToDateTokenizer {
result.set(Calendar.MILLISECOND, dateNr); result.set(Calendar.MILLISECOND, dateNr);
break; break;
case AM_PM: case AM_PM:
inputFragmentStr = matchStringOrThrow(PATTERN_AM_PM, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_AM_PM, params, formatTokenEnum);
if (inputFragmentStr.toUpperCase().startsWith("A")) { if (inputFragmentStr.toUpperCase().startsWith("A")) {
result.set(Calendar.AM_PM, Calendar.AM); result.set(Calendar.AM_PM, Calendar.AM);
} else { } else {
...@@ -295,7 +339,8 @@ class ToDateTokenizer { ...@@ -295,7 +339,8 @@ class ToDateTokenizer {
} }
break; break;
case TZH: case TZH:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
TimeZone tz = result.getTimeZone(); TimeZone tz = result.getTimeZone();
int offsetMillis = tz.getRawOffset(); int offsetMillis = tz.getRawOffset();
...@@ -305,7 +350,8 @@ class ToDateTokenizer { ...@@ -305,7 +350,8 @@ class ToDateTokenizer {
result.setTimeZone(tz); result.setTimeZone(tz);
break; break;
case TZM: case TZM:
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum); inputFragmentStr = matchStringOrThrow(
PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr); dateNr = parseInt(inputFragmentStr);
tz = result.getTimeZone(); tz = result.getTimeZone();
offsetMillis = tz.getRawOffset(); offsetMillis = tz.getRawOffset();
...@@ -320,7 +366,8 @@ class ToDateTokenizer { ...@@ -320,7 +366,8 @@ class ToDateTokenizer {
tz = result.getTimeZone(); tz = result.getTimeZone();
for (String tzName : TimeZone.getAvailableIDs()) { for (String tzName : TimeZone.getAvailableIDs()) {
int length = tzName.length(); int length = tzName.length();
if (s.length() >= length && tzName.equalsIgnoreCase(s.substring(0, length))) { if (s.length() >= length &&
tzName.equalsIgnoreCase(s.substring(0, length))) {
tz.setID(tzName); tz.setID(tzName);
result.setTimeZone(tz); result.setTimeZone(tz);
inputFragmentStr = tzName; inputFragmentStr = tzName;
...@@ -329,11 +376,14 @@ class ToDateTokenizer { ...@@ -329,11 +376,14 @@ class ToDateTokenizer {
} }
break; break;
case TZD: case TZD:
// Must correspond with TZR region. Example: PST (for US/Pacific standard time) // Must correspond with TZR region. Example: PST (for US/Pacific
throwException(params, format("token '%s' not supported yet.", formatTokenEnum.name())); // standard time)
throwException(params, format("token '%s' not supported yet.",
formatTokenEnum.name()));
break; break;
default: default:
throw new IllegalArgumentException(format("%s: Internal Error. Unhandled case: %s", this.getClass() throw new IllegalArgumentException(format(
"%s: Internal Error. Unhandled case: %s", this.getClass()
.getSimpleName(), formatTokenEnum)); .getSimpleName(), formatTokenEnum));
} }
params.remove(inputFragmentStr, formatTokenStr); params.remove(inputFragmentStr, formatTokenStr);
...@@ -362,7 +412,8 @@ class ToDateTokenizer { ...@@ -362,7 +412,8 @@ class ToDateTokenizer {
static String setByName(Calendar c, ToDateParser params, int field, int style) { static String setByName(Calendar c, ToDateParser params, int field, int style) {
String inputFragmentStr = null; String inputFragmentStr = null;
String s = params.getInputStr(); String s = params.getInputStr();
Map<String, Integer> timeStringMap = c.getDisplayNames(field, style, Locale.getDefault()); Map<String, Integer> timeStringMap = c.getDisplayNames(
field, style, Locale.getDefault());
for (String dayName : timeStringMap.keySet()) { for (String dayName : timeStringMap.keySet()) {
int len = dayName.length(); int len = dayName.length();
if (dayName.equalsIgnoreCase(s.substring(0, len))) { if (dayName.equalsIgnoreCase(s.substring(0, len))) {
...@@ -372,7 +423,8 @@ class ToDateTokenizer { ...@@ -372,7 +423,8 @@ class ToDateTokenizer {
} }
} }
if (inputFragmentStr == null || inputFragmentStr.isEmpty()) { if (inputFragmentStr == null || inputFragmentStr.isEmpty()) {
throwException(params, format("Tried to parse one of '%s' but failed (may be an internal error?)", throwException(params, format(
"Tried to parse one of '%s' but failed (may be an internal error?)",
timeStringMap.keySet())); timeStringMap.keySet()));
} }
return inputFragmentStr; return inputFragmentStr;
...@@ -442,13 +494,18 @@ class ToDateTokenizer { ...@@ -442,13 +494,18 @@ class ToDateTokenizer {
TZM(PARSLET_TIME), TZM(PARSLET_TIME),
// Time zone region ID // Time zone region ID
TZR(PARSLET_TIME), TZR(PARSLET_TIME),
// Daylight savings information. Example: PST (for US/Pacific standard time); // Daylight savings information. Example:
// PST (for US/Pacific standard time);
TZD(PARSLET_TIME), TZD(PARSLET_TIME),
// Meridian indicator // Meridian indicator
AM_PM(PARSLET_TIME, PATTERN_AM_PM), AM_PM(PARSLET_TIME, PATTERN_AM_PM),
// NOT supported yet - Full era name (Japanese Imperial, ROC Official, and Thai Buddha calendars). // NOT supported yet -
// Full era name (Japanese Imperial, ROC Official,
// and Thai Buddha calendars).
EE(PARSLET_YEAR), EE(PARSLET_YEAR),
// NOT supported yet - Abbreviated era name (Japanese Imperial, ROC Official, and Thai Buddha calendars). // NOT supported yet -
// Abbreviated era name (Japanese Imperial,
// ROC Official, and Thai Buddha calendars).
E(PARSLET_YEAR), E(PARSLET_YEAR),
Y(PARSLET_YEAR), Y(PARSLET_YEAR),
I(PARSLET_YEAR), I(PARSLET_YEAR),
...@@ -456,12 +513,15 @@ class ToDateTokenizer { ...@@ -456,12 +513,15 @@ class ToDateTokenizer {
Q(PARSLET_MONTH), Q(PARSLET_MONTH),
// Day of week (1-7). // Day of week (1-7).
D(PARSLET_DAY), D(PARSLET_DAY),
// NOT supported yet - Julian day; the number of days since Jan 1, 4712 BC. // NOT supported yet -
// Julian day; the number of days since Jan 1, 4712 BC.
J(PARSLET_DAY); J(PARSLET_DAY);
private static final List<FormatTokenEnum> EMPTY_LIST = new ArrayList<FormatTokenEnum>(0); private static final List<FormatTokenEnum> EMPTY_LIST =
new ArrayList<FormatTokenEnum>(0);
private static final Map<Character, List<FormatTokenEnum>> CACHE = new HashMap<Character, List<FormatTokenEnum>>(FormatTokenEnum.values().length); private static final Map<Character, List<FormatTokenEnum>> CACHE =
new HashMap<Character, List<FormatTokenEnum>>(FormatTokenEnum.values().length);
private final ToDateParslet toDateParslet; private final ToDateParslet toDateParslet;
private final Pattern patternToUse; private final Pattern patternToUse;
...@@ -476,8 +536,9 @@ class ToDateTokenizer { ...@@ -476,8 +536,9 @@ class ToDateTokenizer {
} }
/** /**
* OPTIMISATION: Only return a list of {@link FormatTokenEnum} that share the same 1st char * Optimization: Only return a list of {@link FormatTokenEnum} that
* using the 1st char of the 'to parse' formatStr. Or return empty list if no match. * share the same 1st char using the 1st char of the 'to parse'
* formatStr. Or return empty list if no match.
*/ */
static List<FormatTokenEnum> getTokensInQuestion(String formatStr) { static List<FormatTokenEnum> getTokensInQuestion(String formatStr) {
List<FormatTokenEnum> result = EMPTY_LIST; List<FormatTokenEnum> result = EMPTY_LIST;
...@@ -523,7 +584,7 @@ class ToDateTokenizer { ...@@ -523,7 +584,7 @@ class ToDateTokenizer {
} }
/** /**
* Parse the format-string with passed token of {@link FormatTokenEnum}}.<br> * Parse the format-string with passed token of {@link FormatTokenEnum}.
* If token matches return true, otherwise false. * If token matches return true, otherwise false.
*/ */
boolean parseFormatStrWithToken(ToDateParser params) { boolean parseFormatStrWithToken(ToDateParser params) {
......
...@@ -5,17 +5,14 @@ ...@@ -5,17 +5,14 @@
*/ */
package org.h2.samples; package org.h2.samples;
import org.h2.tools.DeleteDbFiles;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Statement; import java.sql.Statement;
import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Locale;
import java.util.TimeZone; import org.h2.tools.DeleteDbFiles;
/** /**
* A very simple class that shows how to load the driver, create a database, * A very simple class that shows how to load the driver, create a database,
...@@ -37,11 +34,20 @@ public class ToDate { ...@@ -37,11 +34,20 @@ public class ToDate {
Connection conn = DriverManager.getConnection("jdbc:h2:~/test"); Connection conn = DriverManager.getConnection("jdbc:h2:~/test");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("create table ToDateTest(id int primary key, start_date datetime, end_date datetime)"); stat.execute("create table ToDateTest(id int primary key, " +
stat.execute("insert into ToDateTest values(1, TO_DATE('2015-11-13', 'yyyy-MM-DD'), TO_DATE('2015-12-15', 'YYYY-MM-DD'))"); "start_date datetime, end_date datetime)");
stat.execute("insert into ToDateTest values(2, TO_DATE('2015-12-12 00:00:00', 'yyyy-MM-DD HH24:MI:ss'), TO_DATE('2015-12-16 15:00:00', 'YYYY-MM-DD HH24:MI:ss'))"); stat.execute("insert into ToDateTest values(1, " +
stat.execute("insert into ToDateTest values(3, TO_DATE('2015-12-12 08:00 A.M.', 'yyyy-MM-DD HH:MI AM'), TO_DATE('2015-12-17 08:00 P.M.', 'YYYY-MM-DD HH:MI AM'))"); "TO_DATE('2015-11-13', 'yyyy-MM-DD'), " +
stat.execute("insert into ToDateTest values(4, TO_DATE(substr('2015-12-12 08:00 A.M.', 1, 10), 'yyyy-MM-DD'), TO_DATE('2015-12-17 08:00 P.M.', 'YYYY-MM-DD HH:MI AM'))"); "TO_DATE('2015-12-15', 'YYYY-MM-DD'))");
stat.execute("insert into ToDateTest values(2, " +
"TO_DATE('2015-12-12 00:00:00', 'yyyy-MM-DD HH24:MI:ss'), " +
"TO_DATE('2015-12-16 15:00:00', 'YYYY-MM-DD HH24:MI:ss'))");
stat.execute("insert into ToDateTest values(3, " +
"TO_DATE('2015-12-12 08:00 A.M.', 'yyyy-MM-DD HH:MI AM'), " +
"TO_DATE('2015-12-17 08:00 P.M.', 'YYYY-MM-DD HH:MI AM'))");
stat.execute("insert into ToDateTest values(4, " +
"TO_DATE(substr('2015-12-12 08:00 A.M.', 1, 10), 'yyyy-MM-DD'), " +
"TO_DATE('2015-12-17 08:00 P.M.', 'YYYY-MM-DD HH:MI AM'))");
ResultSet rs = stat.executeQuery("select * from ToDateTest"); ResultSet rs = stat.executeQuery("select * from ToDateTest");
while (rs.next()) { while (rs.next()) {
......
...@@ -1337,7 +1337,8 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -1337,7 +1337,8 @@ public class TestFunctions extends TestBase implements AggregateFunction {
assertEquals(date, ToDateParser.toDate("979", "YYY")); assertEquals(date, ToDateParser.toDate("979", "YYY"));
assertEquals(date, ToDateParser.toDate("979", "IYY")); assertEquals(date, ToDateParser.toDate("979", "IYY"));
// Gregorian calendar does not have a year 0. 0 = 0001 BC, -1 = 0002 BC, ... so we adjust // Gregorian calendar does not have a year 0.
// 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
date = new SimpleDateFormat("yyy").parse("-99"); date = new SimpleDateFormat("yyy").parse("-99");
setMonth(date, month); setMonth(date, month);
assertEquals(date, ToDateParser.toDate("0100 BC", "YYYY BC")); assertEquals(date, ToDateParser.toDate("0100 BC", "YYYY BC"));
...@@ -1346,7 +1347,8 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -1346,7 +1347,8 @@ public class TestFunctions extends TestBase implements AggregateFunction {
assertEquals(date, ToDateParser.toDate("-0100", "SYYYY")); assertEquals(date, ToDateParser.toDate("-0100", "SYYYY"));
assertEquals(date, ToDateParser.toDate("-0100", "YYYY")); assertEquals(date, ToDateParser.toDate("-0100", "YYYY"));
// Gregorian calendar does not have a year 0. 0 = 0001 BC, -1 = 0002 BC, ... so we adjust // Gregorian calendar does not have a year 0.
// 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
date = new SimpleDateFormat("y").parse("0"); date = new SimpleDateFormat("y").parse("0");
setMonth(date, month); setMonth(date, month);
assertEquals(date, ToDateParser.toDate("01 BC", "YY BC")); assertEquals(date, ToDateParser.toDate("01 BC", "YY BC"));
......
...@@ -308,9 +308,9 @@ public class BuildBase { ...@@ -308,9 +308,9 @@ public class BuildBase {
protected int execScript(String script, StringList args) { protected int execScript(String script, StringList args) {
if (isWindows()) { if (isWindows()) {
// Under windows, we use the "cmd" command interpreter since it will // Under windows, we use the "cmd" command interpreter since it will
// search the path for us without us having to hard-code an extension // search the path for us without us having to hard-code an
// for the script we want. (Sometimes we don't know if the extension // extension for the script we want. (Sometimes we don't know if the
// will be .bat or .cmd) // extension will be .bat or .cmd)
StringList newArgs = new StringList(); StringList newArgs = new StringList();
newArgs.add("/C"); newArgs.add("/C");
newArgs.add(script); newArgs.add(script);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论