提交 769aa54b authored 作者: Patrick Brielmayer's avatar Patrick Brielmayer

Improvements

Fixed misspelling
Renamed ToDateParams to ToDateParser
Code cleanup
上级 d1514e17
......@@ -10,7 +10,6 @@ package org.h2.util;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
......@@ -581,59 +580,6 @@ public class DateTimeUtils {
}
}
@SuppressWarnings("serial")
private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {
{
put("^\\d{8}[^\\d]*", "yyyyMMdd");
put("^\\d{1,2}\\.\\d{1,2}\\.\\d{4}[^\\d]*", "dd.MM.yyyy");
put("^\\d{4}\\.\\d{1,2}\\.\\d{1,2}[^\\d]*", "yyyy.MM.dd");
put("^\\d{4}-\\d{1,2}-\\d{1,2}[^\\d]*", "yyyy-MM-dd");
put("^\\d{1,2}-\\d{1,2}-\\d{4}[^\\d]*", "dd-MM-yyyy");
put("^\\d{1,2}/\\d{1,2}/\\d{4}[^\\d]*", "dd/MM/yyyy");
put("^\\d{4}/\\d{1,2}/\\d{1,2}[^\\d]*", "yyyy/MM/dd");
put("^\\d{1,2}/\\d{1,2}/\\d{4}[^\\d]*", "dd/MM/yyyy");
put("^\\d{1,2}-[^\\d]{3}-\\d{4}[^\\d]*", "dd-MMM-yyyy");
put("^\\d{4}-[^\\d]{3}-\\d{1,2}[^\\d]*", "yyyy-MMM-dd");
put("^.{2}\\s.{3}\\s\\d{1,2}\\s\\d{1,2}\\:\\d{1,2}\\:\\d{1,2}\\s.{3}\\s\\d{4}$",
"EEE MMM dd hh:mm:ss z yyyy");
}
};
/**
* Determine SimpleDateFormat pattern matching with the given date string. Returns null if format is unknown. You can
* simply extend DateUtil with more formats if needed.
*
* @param dateString
* The date string to determine the SimpleDateFormat pattern for.
* @return The matching SimpleDateFormat pattern, or null if format is unknown.
*/
private static String determineDateFormat(final String dateString) {
for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
if (dateString.toLowerCase().matches(regexp)) {
return DATE_FORMAT_REGEXPS.get(regexp);
}
}
return null; // Unknown format.
}
/**
* Parse date-string in "Best Effort" (BE) manner.
* Uses a predefined list of date patterns to parse a string into a date.
*/
public static java.util.Date parseDateBestEffort(final String dateStr) {
String dateFormat = determineDateFormat(dateStr);
if (dateFormat == null) {
// The source does not contain a date that can be parsed.
throw DbException.get(ErrorCode.PARSE_ERROR_1, "Invalid date. " + dateStr);
}
SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);
try {
return formatter.parse(dateStr);
} catch (ParseException e) {
throw DbException.get(ErrorCode.PARSE_ERROR_1, e, "Invalid date. " + dateStr);
}
}
/**
* Parses a date using a format string.
*
......
package org.h2.util;
import java.sql.Timestamp;
import java.util.List;
import org.h2.util.ToDateTokenizer.FormatTokenEnum;
import java.util.Date;
/**
* Emulates Oracle's TO_DATE function.<br>
* Main class
*/
public class ToDate {
public enum ToDateFunctionName {
TO_DATE("DD MON YYYY"), TO_TIMESTAMP("DD MON YYYY HH:MI:SS");
private final String defaultFormatStr;
ToDateFunctionName(final String defaultFormatStr) {
this.defaultFormatStr = defaultFormatStr;
}
String getDefaultFormatStr() {
return defaultFormatStr;
}
};
public static Timestamp TO_DATE(final String input, final String format) {
ToDateParams parsed = parse(new ToDateParams(ToDateFunctionName.TO_DATE, input, format));
return parsed.getResultingTimestamp();
ToDateParser parser = ToDateParser.toDate(input, format);
return parser.getResultingTimestamp();
}
public static Timestamp TO_TIMESTAMP(final String input, final String format) {
ToDateParams parsed = parse(new ToDateParams(ToDateFunctionName.TO_TIMESTAMP, input, format));
return parsed.getResultingTimestamp();
ToDateParser parser = ToDateParser.toTimestamp(input, format);
return parser.getResultingTimestamp();
}
/**
* Parse the format-string with passed token of {@link FormatTokenEnum}}.<br>
* if token matches return true otherwise false.
*/
private static ToDateParams parse(final ToDateParams p) {
while (p.hasToParseData()) {
List<FormatTokenEnum> tokenList = FormatTokenEnum.getTokensInQuestion(p);
if (tokenList.isEmpty()) {
p.removeFirstChar();
continue;
}
boolean foundAnToken = false;
for (FormatTokenEnum token : tokenList) {
if (token.parseFormatStrWithToken(p)) {
foundAnToken = true;
break;
}
}
if (!foundAnToken) {
p.removeFirstChar();
continue;
}
}
return p;
}
}
\ No newline at end of file
......@@ -2,31 +2,53 @@ package org.h2.util;
import static java.lang.String.format;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Calendar;
import org.h2.util.ToDate.ToDateFunctionName;
import java.util.Date;
import java.util.List;
/**
* Emulates Oracle's TO_DATE function.<br>
* This class holds and handles the input data form the TO_DATE-method
*/
class ToDateParams {
class ToDateParser {
private final String unmodifiedInputStr;
private final String unmodifiedFormatStr;
private final ToDateFunctionName functionName;
private final ConfigParam functionName;
private String inputStr;
private String formatStr;
private final Calendar resultCalendar = (Calendar) Calendar.getInstance().clone();
private Integer nanos = null;
private enum ConfigParam {
TO_DATE("DD MON YYYY"), TO_TIMESTAMP("DD MON YYYY HH:MI:SS");
private final String defaultFormatStr;
ConfigParam (final String defaultFormatStr) {
this.defaultFormatStr = defaultFormatStr;
}
String getDefaultFormatStr() {
return defaultFormatStr;
}
}
static ToDateParser toDate(final String input, final String format) {
ToDateParser result = new ToDateParser(ConfigParam.TO_DATE, input, format);
parse(result);
return result;
}
static ToDateParser toTimestamp(final String input, final String format) {
ToDateParser result = new ToDateParser(ConfigParam.TO_TIMESTAMP, input, format);
parse(result);
return result;
}
/**
* @param input the input date with the date-time info
* @param format the format of date-time info
* @param functionName one of [TO_DATE, TO_TIMESTAMP] (both share the same code)
*/
ToDateParams(final ToDateFunctionName functionName, final String input, final String format) {
ToDateParser(final ConfigParam functionName, final String input, final String format) {
// reset calendar - default oracle behaviour
resultCalendar.set(Calendar.YEAR, 1970);
resultCalendar.set(Calendar.MONTH, Calendar.getInstance().get(Calendar.MONTH));
......@@ -52,10 +74,6 @@ class ToDateParams {
unmodifiedFormatStr = formatStr; // Keep a copy
}
Date getResultingDate() {
return new Date(getResultCalendar().getTimeInMillis());
}
Timestamp getResultingTimestamp() {
Calendar cal = (Calendar) getResultCalendar().clone();
int nanosToSet = nanos == null ? cal.get(Calendar.MILLISECOND) * 1000000 : nanos.intValue();
......@@ -77,8 +95,8 @@ class ToDateParams {
return formatStr;
}
ToDateFunctionName getFunctionName() {
return functionName;
String getFunctionName() {
return functionName.name();
}
void setNanos(final int nanos) {
......@@ -98,6 +116,28 @@ class ToDateParams {
}
}
private static ToDateParser parse(final ToDateParser p) {
while (p.hasToParseData()) {
List<ToDateTokenizer.FormatTokenEnum> tokenList = ToDateTokenizer.FormatTokenEnum.getTokensInQuestion(p.getFormatStr());
if (tokenList.isEmpty()) {
p.removeFirstChar();
continue;
}
boolean foundAnToken = false;
for (ToDateTokenizer.FormatTokenEnum token : tokenList) {
if (token.parseFormatStrWithToken(p)) {
foundAnToken = true;
break;
}
}
if (!foundAnToken) {
p.removeFirstChar();
continue;
}
}
return p;
}
void remove(final String toIgnore) {
if (toIgnore != null) {
int trimLeng = toIgnore.length();
......
......@@ -27,7 +27,6 @@ class ToDateTokenizer {
private static final Pattern PATTERN_BC_AD = Pattern.compile("^(BC|B\\.C\\.|AD|A\\.D\\.)", Pattern.CASE_INSENSITIVE);
private static final YearParslet PARSLET_YEAR = new YearParslet();
private static final MonthParslet PARSLET_MONTH = new MonthParslet();
//private static final WeekParslet PARSLET_Week = new WeekParslet();
private static final DayParslet PARSLET_DAY = new DayParslet();
private static final TimeParslet PARSLET_TIME = new TimeParslet();
......@@ -48,8 +47,6 @@ class ToDateTokenizer {
, MON(PARSLET_MONTH) // Abbreviated name of month.
, MM(PARSLET_MONTH) // Month (01-12; JAN = 01).
, RM(PARSLET_MONTH) // Roman numeral month (I-XII; JAN = I).
//, WW(PARSLET_Week) // Week of year (1-53)
//, IW(PARSLET_Week) // Week of year (1-52 or 1-53) based on the ISO standard.
, DDD(PARSLET_DAY) // Day of year (1-366).
, DAY(PARSLET_DAY) // Name of day.
, DD(PARSLET_DAY) // Day of month (1-31).
......@@ -71,7 +68,6 @@ class ToDateTokenizer {
, Y(PARSLET_YEAR) //
, I(PARSLET_YEAR) //
, Q(PARSLET_MONTH) // Quarter of year (1, 2, 3, 4; JAN-MAR = 1).
//, W(PARSLET_Week) // Week of month (1-5)
, D(PARSLET_DAY) // Day of week (1-7).
, J(PARSLET_DAY); // NOT supported yet - Julian day; the number of days since Jan 1, 4712 BC.
......@@ -96,12 +92,11 @@ class ToDateTokenizer {
* OPTIMISATION: Only return a list of {@link FormatTokenEnum} that 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(final ToDateParams params) {
static List<FormatTokenEnum> getTokensInQuestion(String formatStr) {
List<FormatTokenEnum> result = EMPTY_LIST;
if (cache.size() <= 0) {
initCache();
}
String formatStr = params.getFormatStr();
if (formatStr != null && formatStr.length() > 0) {
Character key = Character.toUpperCase(formatStr.charAt(0));
result = cache.get(key);
......@@ -142,9 +137,9 @@ class ToDateTokenizer {
/**
* Parse the format-string with passed token of {@link FormatTokenEnum}}.<br>
* if token matches return true otherwise false.
* If token matches return true, otherwise false.
*/
boolean parseFormatStrWithToken(final ToDateParams params) {
boolean parseFormatStrWithToken(final ToDateParser params) {
Matcher matcher = patternToUse.matcher(params.getFormatStr());
boolean foundToken = matcher.find();
if (foundToken) {
......@@ -159,15 +154,15 @@ class ToDateTokenizer {
* Interface of the classes that can parse a specialized small bit of the TO_DATE format-string
*/
interface ToDateParslet {
ToDateParams parse(ToDateParams params, FormatTokenEnum formatTokenEnum, String formatTokenStr);
void parse(ToDateParser params, FormatTokenEnum formatTokenEnum, String formatTokenStr);
}
/**
*
* Parslet responsible for parsing year parameter
*/
private static final class YearParslet implements ToDateParslet {
@Override
public ToDateParams parse(final ToDateParams params, final FormatTokenEnum formatTokenEnum,
public void parse(final ToDateParser params, final FormatTokenEnum formatTokenEnum,
final String formatTokenStr) {
final Calendar result = params.getResultCalendar();
String inputFragmentStr = null;
......@@ -241,19 +236,18 @@ class ToDateTokenizer {
.getSimpleName(), formatTokenEnum));
}
params.remove(inputFragmentStr, formatTokenStr);
return params;
}
}
/**
*
* Parslet responsible for parsing month parameter
*/
private static final class MonthParslet implements ToDateParslet {
private static String[] ROMAN_Month = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI",
"XII" };
private static final String[] ROMAN_Month = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
"XI", "XII" };
@Override
public ToDateParams parse(final ToDateParams params, final FormatTokenEnum formatTokenEnum,
public void parse(final ToDateParser params, final FormatTokenEnum formatTokenEnum,
final String formatTokenStr) {
final Calendar result = params.getResultCalendar();
final String s = params.getInputStr();
......@@ -288,7 +282,7 @@ class ToDateTokenizer {
}
if (inputFragmentStr == null || inputFragmentStr.isEmpty()) {
throwException(params,
format("Issue happend 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)));
}
break;
......@@ -297,60 +291,15 @@ class ToDateTokenizer {
.getSimpleName(), formatTokenEnum));
}
params.remove(inputFragmentStr, formatTokenStr);
return params;
}
}
/**
* Week parsing can be used by TO_CHAR but not by TO_DATE
*
private static final class WeekParslet implements ToDateParslet {
@Override
public ToDateParams parse(final ToDateParams params, final FormatTokenEnum formatTokenEnum,
final String formatTokenStr) {
final Calendar result = params.getResultCalendar();
String inputFragmentStr = null;
int dateNr = 0;
switch (formatTokenEnum) {
case WW:
inputFragmentStr = matchStringOrDie(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr);
// The first week of the month, as defined by
// getFirstDayOfWeek() and getMinimalDaysInFirstWeek(),
result.set(Calendar.WEEK_OF_YEAR, dateNr);
break;
case IW:
inputFragmentStr = matchStringOrDie(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr);
// Build set the calendar to ISO8601 (see
// http://en.wikipedia.org/wiki/ISO_8601_week_number)
result.setMinimalDaysInFirstWeek(4);
result.setFirstDayOfWeek(Calendar.MONDAY);
result.set(Calendar.WEEK_OF_YEAR, dateNr);
break;
case W:
inputFragmentStr = matchStringOrDie(PATTERN_ONE_DIGIT, params, formatTokenEnum);
dateNr = parseInt(inputFragmentStr);
// The first week of the month, as defined by
// getFirstDayOfWeek() and getMinimalDaysInFirstWeek(),
result.set(Calendar.WEEK_OF_MONTH, dateNr);
break;
default:
throw new IllegalArgumentException(format("%s: Internal Error. Unhandled case: %s", this.getClass()
.getSimpleName(), formatTokenEnum));
}
params.remove(inputFragmentStr, formatTokenStr);
return params;
}
}
*/
/**
*
* Parslet responsible for parsing day parameter
*/
private static final class DayParslet implements ToDateParslet {
@Override
public ToDateParams parse(final ToDateParams params, final FormatTokenEnum formatTokenEnum,
public void parse(final ToDateParser params, final FormatTokenEnum formatTokenEnum,
final String formatTokenStr) {
final Calendar result = params.getResultCalendar();
String inputFragmentStr = null;
......@@ -391,19 +340,18 @@ class ToDateTokenizer {
.getSimpleName(), formatTokenEnum));
}
params.remove(inputFragmentStr, formatTokenStr);
return params;
}
}
private static int MILLIS_in_hour = 60 * 60 * 1000;
/**
*
* Parslet responsible for parsing time parameter
*/
private static final class TimeParslet implements ToDateParslet {
@Override
public ToDateParams parse(final ToDateParams params, final FormatTokenEnum formatTokenEnum,
public void parse(final ToDateParser params, final FormatTokenEnum formatTokenEnum,
final String formatTokenStr) {
final Calendar result = params.getResultCalendar();
String inputFragmentStr = null;
......@@ -495,11 +443,9 @@ class ToDateTokenizer {
.getSimpleName(), formatTokenEnum));
}
params.remove(inputFragmentStr, formatTokenStr);
return params;
}
}
// ========== PRIVATE ===================
private static int parseInt(final String s) {
int result = 0;
if (s.length() > 0 && s.charAt(0) == '+') {
......@@ -510,7 +456,7 @@ class ToDateTokenizer {
return result;
}
private static String matchStringOrDie(final Pattern p, final ToDateParams params, final Enum<?> aEnum) {
private static String matchStringOrDie(final Pattern p, final ToDateParser params, final Enum<?> aEnum) {
final String s = params.getInputStr();
Matcher matcher = p.matcher(s);
if (!matcher.find()) {
......@@ -519,7 +465,7 @@ class ToDateTokenizer {
return matcher.group(1);
}
private static String setByName(final Calendar c, final ToDateParams params, final int field, final int style) {
private static String setByName(final Calendar c, final ToDateParser params, final int field, final int style) {
String inputFragmentStr = null;
String s = params.getInputStr();
Map<String, Integer> timeStringMap = c.getDisplayNames(field, style, Locale.getDefault());
......@@ -532,16 +478,16 @@ class ToDateTokenizer {
}
}
if (inputFragmentStr == null || inputFragmentStr.isEmpty()) {
throwException(params, format("Tryed 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()));
}
return inputFragmentStr;
}
private static void throwException(final ToDateParams params, final String errorStr) {
private static void throwException(final ToDateParser params, final String errorStr) {
throw DbException.get(
ErrorCode.INVALID_TO_DATE_FORMAT,
params.getFunctionName().name(),
params.getFunctionName(),
format(" %s. Details: %s", errorStr, params));
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论