提交 0c7d6634 authored 作者: Patrick Brielmayer's avatar Patrick Brielmayer

Added TO_DATE and TO_TIMESTAMP functionality

上级 990e81ce
...@@ -1001,6 +1001,12 @@ public class ErrorCode { ...@@ -1001,6 +1001,12 @@ public class ErrorCode {
*/ */
public static final int UNSUPPORTED_CIPHER = 90055; public static final int UNSUPPORTED_CIPHER = 90055;
/**
* The error with code <code>90056</code> is thrown when trying to format a
* timestamp using TO_DATE and TO_TIMESTAMP with an invalid format.
*/
public static final int INVALID_TO_DATE_FORMAT = 90056;
/** /**
* The error with code <code>90057</code> is thrown when * The error with code <code>90057</code> is thrown when
* trying to drop a constraint that does not exist. * trying to drop a constraint that does not exist.
...@@ -1923,7 +1929,7 @@ public class ErrorCode { ...@@ -1923,7 +1929,7 @@ public class ErrorCode {
public static final int STEP_SIZE_MUST_NOT_BE_ZERO = 90142; public static final int STEP_SIZE_MUST_NOT_BE_ZERO = 90142;
// next are 90056, 90110, 90122, 90143 // next are 90110, 90122, 90143
private ErrorCode() { private ErrorCode() {
// utility class // utility class
......
...@@ -44,16 +44,7 @@ import org.h2.table.Table; ...@@ -44,16 +44,7 @@ import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.tools.CompressTool; import org.h2.tools.CompressTool;
import org.h2.tools.Csv; import org.h2.tools.Csv;
import org.h2.util.AutoCloseInputStream; import org.h2.util.*;
import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.ToChar;
import org.h2.util.Utils;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueArray; import org.h2.value.ValueArray;
...@@ -93,7 +84,8 @@ public class Function extends Expression implements FunctionCall { ...@@ -93,7 +84,8 @@ public class Function extends Expression implements FunctionCall {
STRINGDECODE = 80, STRINGTOUTF8 = 81, UTF8TOSTRING = 82, STRINGDECODE = 80, STRINGTOUTF8 = 81, UTF8TOSTRING = 82,
XMLATTR = 83, XMLNODE = 84, XMLCOMMENT = 85, XMLCDATA = 86, XMLATTR = 83, XMLNODE = 84, XMLCOMMENT = 85, XMLCDATA = 86,
XMLSTARTDOC = 87, XMLTEXT = 88, REGEXP_REPLACE = 89, RPAD = 90, XMLSTARTDOC = 87, XMLTEXT = 88, REGEXP_REPLACE = 89, RPAD = 90,
LPAD = 91, CONCAT_WS = 92, TO_CHAR = 93, TRANSLATE = 94, ORA_HASH = 95; LPAD = 91, CONCAT_WS = 92, TO_CHAR = 93, TRANSLATE = 94, ORA_HASH = 95,
TO_DATE = 96, TO_TIMESTAMP = 97;
public static final int CURDATE = 100, CURTIME = 101, DATE_ADD = 102, public static final int CURDATE = 100, CURTIME = 101, DATE_ADD = 102,
DATE_DIFF = 103, DAY_NAME = 104, DAY_OF_MONTH = 105, DATE_DIFF = 103, DAY_NAME = 104, DAY_OF_MONTH = 105,
...@@ -306,6 +298,8 @@ public class Function extends Expression implements FunctionCall { ...@@ -306,6 +298,8 @@ public class Function extends Expression implements FunctionCall {
0, Value.DATE); 0, Value.DATE);
addFunctionNotDeterministic("CURDATE", CURDATE, addFunctionNotDeterministic("CURDATE", CURDATE,
0, Value.DATE); 0, Value.DATE);
addFunction("TO_DATE", TO_DATE, VAR_ARGS, Value.STRING);
addFunction("TO_TIMESTAMP", TO_TIMESTAMP, VAR_ARGS, Value.STRING);
// alias for MSSQLServer // alias for MSSQLServer
addFunctionNotDeterministic("GETDATE", CURDATE, addFunctionNotDeterministic("GETDATE", CURDATE,
0, Value.DATE); 0, Value.DATE);
...@@ -1429,6 +1423,14 @@ public class Function extends Expression implements FunctionCall { ...@@ -1429,6 +1423,14 @@ public class Function extends Expression implements FunctionCall {
database.getMode().treatEmptyStringsAsNull); database.getMode().treatEmptyStringsAsNull);
} }
break; break;
case TO_DATE:
result = ValueTimestamp.get(ToDate.TO_DATE(v0.getString(),
v1 == null ? null : v1.getString()));
break;
case TO_TIMESTAMP:
result = ValueTimestamp.get(ToDate.TO_TIMESTAMP(v0.getString(),
v1 == null ? null : v1.getString()));
break;
case TRANSLATE: { case TRANSLATE: {
String matching = v1.getString(); String matching = v1.getString();
String replacement = v2.getString(); String replacement = v2.getString();
...@@ -2123,10 +2125,12 @@ public class Function extends Expression implements FunctionCall { ...@@ -2123,10 +2125,12 @@ public class Function extends Expression implements FunctionCall {
case ROUND: case ROUND:
case XMLTEXT: case XMLTEXT:
case TRUNCATE: case TRUNCATE:
case TO_TIMESTAMP:
min = 1; min = 1;
max = 2; max = 2;
break; break;
case TO_CHAR: case TO_CHAR:
case TO_DATE:
min = 1; min = 1;
max = 3; max = 3;
break; break;
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
90053=Skalární vnořený dotaz obsahuje více než jeden řádek 90053=Skalární vnořený dotaz obsahuje více než jeden řádek
90054=Neplatné použití agregátní funkce {0} 90054=Neplatné použití agregátní funkce {0}
90055=Nepodporované šifrování {0} 90055=Nepodporované šifrování {0}
90056=Function {0}: Invalid date format: {1}
90057=Omezení {0} nenalezeno 90057=Omezení {0} nenalezeno
90058=Vkládání nebo vrácení změn není povoleno uvnitř triggeru 90058=Vkládání nebo vrácení změn není povoleno uvnitř triggeru
90059=Dvojsmyslný název sloupce {0} 90059=Dvojsmyslný název sloupce {0}
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
90053=Skalar-Unterabfrage enthält mehr als eine Zeile 90053=Skalar-Unterabfrage enthält mehr als eine Zeile
90054=Ungültige Verwendung der Aggregat Funktion {0} 90054=Ungültige Verwendung der Aggregat Funktion {0}
90055=Chiffre nicht unterstützt: {0} 90055=Chiffre nicht unterstützt: {0}
90056=Funktion {0}: Ungültiges TO_DATE Format: {1}
90057=Bedingung {0} nicht gefunden 90057=Bedingung {0} nicht gefunden
90058=Innerhalb eines Triggers sind Commit und Rollback ist nicht erlaubt 90058=Innerhalb eines Triggers sind Commit und Rollback ist nicht erlaubt
90059=Mehrdeutiger Feldname {0} 90059=Mehrdeutiger Feldname {0}
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
90053=Scalar subquery contains more than one row 90053=Scalar subquery contains more than one row
90054=Invalid use of aggregate function {0} 90054=Invalid use of aggregate function {0}
90055=Unsupported cipher {0} 90055=Unsupported cipher {0}
90056=Function {0}: Invalid date format: {1}
90057=Constraint {0} not found 90057=Constraint {0} not found
90058=Commit or rollback is not allowed within a trigger 90058=Commit or rollback is not allowed within a trigger
90059=Ambiguous column name {0} 90059=Ambiguous column name {0}
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
90053=El Subquery escalar contiene mas de una fila 90053=El Subquery escalar contiene mas de una fila
90054=Uso Invalido de la función de columna agregada {0} 90054=Uso Invalido de la función de columna agregada {0}
90055=Cipher No soportado {0} 90055=Cipher No soportado {0}
90056=Function {0}: Invalid date format: {1}
90057=Constraint {0} no encontrado 90057=Constraint {0} no encontrado
90058=Commit ó rollback no permitido dentro de un trigger 90058=Commit ó rollback no permitido dentro de un trigger
90059=Nombre de columna ambigua {0} 90059=Nombre de columna ambigua {0}
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
90053=数値サブクエリ�複数�行を�ん���� 90053=数値サブクエリ�複数�行を�ん����
90054=集約関数 {0} ��正�使用 90054=集約関数 {0} ��正�使用
90055={0} �未サ�ート�暗��� 90055={0} �未サ�ート�暗���
90056=Function {0}: Invalid date format: {1}
90057=制約 {0} �見��り��ん 90057=制約 {0} �見��り��ん
90058=トリガ内��コミット�ロール�ック�許�れ����ん 90058=トリガ内��コミット�ロール�ック�許�れ����ん
90059=列� {0} ������� 90059=列� {0} �������
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
90053=Skalarna pod-kwerenda zawiera więcej niż jeden wiersz 90053=Skalarna pod-kwerenda zawiera więcej niż jeden wiersz
90054=Nieprawidłowe użycie funkcji agregującej {0} 90054=Nieprawidłowe użycie funkcji agregującej {0}
90055=Nieobsługiwany szyfr {0} 90055=Nieobsługiwany szyfr {0}
90056=Function {0}: Invalid date format: {1}
90057=Ograniczenie {0} nie istnieje 90057=Ograniczenie {0} nie istnieje
90058=Zatwierdzenie lub wycofanie transakcji nie jest dozwolone w wyzwalaczu 90058=Zatwierdzenie lub wycofanie transakcji nie jest dozwolone w wyzwalaczu
90059=Niejednoznaczna nazwa kolumny {0} 90059=Niejednoznaczna nazwa kolumny {0}
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
90053=A Subquery contém mais de uma linha 90053=A Subquery contém mais de uma linha
90054=Uso inválido da função {0} agregada 90054=Uso inválido da função {0} agregada
90055=Cipher {0} não é suportado 90055=Cipher {0} não é suportado
90056=Function {0}: Invalid date format: {1}
90057=Restrição {0} não foi encontrada 90057=Restrição {0} não foi encontrada
90058=#Commit or rollback is not allowed within a trigger 90058=#Commit or rollback is not allowed within a trigger
90059=Nome da coluna {0} é ambíguo. 90059=Nome da coluna {0} é ambíguo.
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
90053=Подзапро� выбирает более одной �троки 90053=Подзапро� выбирает более одной �троки
90054=�екорректное и�пользование агрегирующей функции {0} 90054=�екорректное и�пользование агрегирующей функции {0}
90055=Метод шифровани� {0} не поддерживает�� 90055=Метод шифровани� {0} не поддерживает��
90056=Function {0}: Invalid date format: {1}
90057=Ограничение {0} не найдено 90057=Ограничение {0} не найдено
90058=Commit или rollback внутри триггера не допу�кает�� 90058=Commit или rollback внутри триггера не допу�кает��
90059=�еоднозначное им� �толбца {0} 90059=�еоднозначное им� �толбца {0}
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
90053=标��查询(Scalar subquery)包�多于一行结果 90053=标��查询(Scalar subquery)包�多于一行结果
90054=�法使用��函数 {0} 90054=�法使用��函数 {0}
90055=�支�的加密算法 {0} 90055=�支�的加密算法 {0}
90056=Function {0}: Invalid date format: {1}
90057=约� {0} 找�到 90057=约� {0} 找�到
90058=�交或回滚�能办函触�器 90058=�交或回滚�能办函触�器
90059=�明确的字段� {0} 90059=�明确的字段� {0}
......
...@@ -10,11 +10,9 @@ package org.h2.util; ...@@ -10,11 +10,9 @@ package org.h2.util;
import java.sql.Date; import java.sql.Date;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.*;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
...@@ -583,6 +581,59 @@ public class DateTimeUtils { ...@@ -583,6 +581,59 @@ 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. * 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;
/**
* 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();
}
public static Timestamp TO_TIMESTAMP(final String input, final String format) {
ToDateParams parsed = parse(new ToDateParams(ToDateFunctionName.TO_TIMESTAMP, input, format));
return parsed.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
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;
/**
* Emulates Oracle's TO_DATE function.<br>
* This class holds and handles the input data form the TO_DATE-method
*/
class ToDateParams {
private final String unmodifiedInputStr;
private final String unmodifiedFormatStr;
private final ToDateFunctionName functionName;
private String inputStr;
private String formatStr;
private final Calendar resultCalendar = (Calendar) Calendar.getInstance().clone();
private Integer nanos = null;
/**
* @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) {
// reset calendar - default oracle behaviour
resultCalendar.set(Calendar.YEAR, 1970);
resultCalendar.set(Calendar.MONTH, Calendar.getInstance().get(Calendar.MONTH));
resultCalendar.clear(Calendar.DAY_OF_YEAR);
resultCalendar.clear(Calendar.DAY_OF_WEEK);
resultCalendar.clear(Calendar.DAY_OF_WEEK_IN_MONTH);
resultCalendar.set(Calendar.DAY_OF_MONTH, 1);
resultCalendar.set(Calendar.HOUR, 0);
resultCalendar.set(Calendar.HOUR_OF_DAY, 0);
resultCalendar.set(Calendar.MINUTE, 0);
resultCalendar.set(Calendar.SECOND, 0);
resultCalendar.set(Calendar.MILLISECOND, 0);
resultCalendar.set(Calendar.AM_PM, Calendar.AM);
this.functionName = functionName;
inputStr = input.trim();
unmodifiedInputStr = inputStr; // Keep a copy
if (format == null || format.isEmpty()) {
formatStr = functionName.getDefaultFormatStr(); // default Oracle format.
} else {
formatStr = format.trim();
}
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();
cal.set(Calendar.MILLISECOND, 0);
Timestamp ts = new Timestamp(cal.getTimeInMillis());
ts.setNanos(nanosToSet);
return ts;
}
Calendar getResultCalendar() {
return resultCalendar;
}
String getInputStr() {
return inputStr;
}
String getFormatStr() {
return formatStr;
}
ToDateFunctionName getFunctionName() {
return functionName;
}
void setNanos(final int nanos) {
this.nanos = nanos;
}
boolean hasToParseData() {
return formatStr.length() > 0;
}
void removeFirstChar() {
if (!formatStr.isEmpty()) {
formatStr = formatStr.substring(1);
}
if (!inputStr.isEmpty()) {
inputStr = inputStr.substring(1);
}
}
void remove(final String toIgnore) {
if (toIgnore != null) {
int trimLeng = toIgnore.length();
formatStr = formatStr.substring(trimLeng);
if (inputStr.length() >= trimLeng) {
inputStr = inputStr.substring(trimLeng);
}
}
}
void remove(final String intputFragmentStr, final String formatFragment) {
if (intputFragmentStr != null && inputStr.length() >= intputFragmentStr.length()) {
inputStr = inputStr.substring(intputFragmentStr.length());
}
if (formatFragment != null && formatStr.length() >= formatFragment.length()) {
formatStr = formatStr.substring(formatFragment.length());
}
}
@Override
public String toString() {
int inputStrLeng = inputStr.length();
int orgInputLeng = unmodifiedInputStr.length();
int currentInputPos = orgInputLeng - inputStrLeng;
int restInputLeng = inputStrLeng <= 0 ? inputStrLeng : inputStrLeng - 1;
int orgFormatLeng = unmodifiedFormatStr.length();
int currentFormatPos = orgFormatLeng - formatStr.length();
StringBuilder sb = new StringBuilder();
sb.append(format("\n %s('%s', '%s')", functionName, unmodifiedInputStr, unmodifiedFormatStr));
sb.append(format("\n %s^%s , %s^ <-- Parsing failed at this point", //
format("%" + (functionName.name().length() + currentInputPos) + "s", ""),
restInputLeng <= 0 ? "" : format("%" + restInputLeng + "s", ""),
currentFormatPos <= 0 ? "" : format("%" + currentFormatPos + "s", "")));
return sb.toString();
}
}
\ No newline at end of file
差异被折叠。
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.samples;
import org.h2.tools.DeleteDbFiles;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* A very simple class that shows how to load the driver, create a database,
* create a table, and insert some data.
*/
public class ToDate {
/**
* Called when ran from command line.
*
* @param args ignored
*/
public static void main(String... args) throws Exception {
// delete the database named 'test' in the user home directory
DeleteDbFiles.execute("~", "test", true);
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:~/test");
Statement stat = conn.createStatement();
stat.execute("create table ToDateTest(id int primary key, start_date datetime, end_date datetime)");
stat.execute("insert into ToDateTest values(1, TO_DATE('2015-11-13', 'yyyy-MM-DD'), 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");
while (rs.next()) {
System.out.println("Start date: " + dateToString(rs.getTimestamp("start_date")));
System.out.println("End date: " + dateToString(rs.getTimestamp("end_date")));
System.out.println();
}
stat.close();
conn.close();
}
private static String dateToString(Date date) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
}
}
...@@ -27,6 +27,8 @@ import java.sql.SQLException; ...@@ -27,6 +27,8 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Currency; import java.util.Currency;
...@@ -42,6 +44,7 @@ import org.h2.api.AggregateFunction; ...@@ -42,6 +44,7 @@ import org.h2.api.AggregateFunction;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.jdbc.JdbcSQLException; import org.h2.jdbc.JdbcSQLException;
import org.h2.message.DbException;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.ap.TestAnnotationProcessor; import org.h2.test.ap.TestAnnotationProcessor;
...@@ -49,6 +52,7 @@ import org.h2.tools.SimpleResultSet; ...@@ -49,6 +52,7 @@ import org.h2.tools.SimpleResultSet;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.ToDate;
import org.h2.value.Value; import org.h2.value.Value;
/** /**
...@@ -72,6 +76,8 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -72,6 +76,8 @@ public class TestFunctions extends TestBase implements AggregateFunction {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
deleteDb("functions"); deleteDb("functions");
testToDate();
testToDateException();
testDataType(); testDataType();
testVersion(); testVersion();
testFunctionTable(); testFunctionTable();
...@@ -1275,6 +1281,123 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -1275,6 +1281,123 @@ public class TestFunctions extends TestBase implements AggregateFunction {
String.format("SELECT ORA_HASH('%s', 0, 0) FROM DUAL", testStr)); String.format("SELECT ORA_HASH('%s', 0, 0) FROM DUAL", testStr));
} }
private void testToDateException() {
try {
ToDate.TO_DATE("1979-ThisWillFail-12", "YYYY-MM-DD");
} catch (Exception e) {
assertEquals(DbException.class.getSimpleName(), e.getClass().getSimpleName());
}
}
private void testToDate() throws SQLException, ParseException {
final int curMonth = Calendar.getInstance().get(Calendar.MONTH);
Date date = null;
date = new SimpleDateFormat("yyyy-MM-dd").parse("1979-11-12");
assertEquals(date, ToDate.TO_DATE("1979-11-12", "YYYY-MM-DD"));
assertEquals(date, ToDate.TO_DATE("1979/11/12", "YYYY/MM/DD"));
assertEquals(date, ToDate.TO_DATE("1979,11,12", "YYYY,MM,DD"));
assertEquals(date, ToDate.TO_DATE("1979.11.12", "YYYY.MM.DD"));
assertEquals(date, ToDate.TO_DATE("1979;11;12", "YYYY;MM;DD"));
assertEquals(date, ToDate.TO_DATE("1979:11:12", "YYYY:MM:DD"));
date = new SimpleDateFormat("yyyy").parse("1979");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("1979", "YYYY"));
assertEquals(date, ToDate.TO_DATE("1979 AD", "YYYY AD"));
assertEquals(date, ToDate.TO_DATE("1979 A.D.", "YYYY A.D."));
assertEquals(date, ToDate.TO_DATE("1979 A.D.", "YYYY BC"));
assertEquals(date, ToDate.TO_DATE("1979", "IYYY"));
assertEquals(date, ToDate.TO_DATE("+1979", "SYYYY"));
assertEquals(date, ToDate.TO_DATE("79", "RRRR"));
date = new SimpleDateFormat("yyyy-mm").parse("1970-12");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("12", "MI"));
date = new SimpleDateFormat("yyyy-MM").parse("1970-11");
assertEquals(date, ToDate.TO_DATE("11", "MM"));
assertEquals(date, ToDate.TO_DATE("11", "Mm"));
assertEquals(date, ToDate.TO_DATE("11", "mM"));
assertEquals(date, ToDate.TO_DATE("11", "mm"));
assertEquals(date, ToDate.TO_DATE("XI", "RM"));
date = new SimpleDateFormat("yyyy").parse("9");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("9", "Y"));
assertEquals(date, ToDate.TO_DATE("9", "I"));
date = new SimpleDateFormat("yyyy").parse("79");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("79", "YY"));
assertEquals(date, ToDate.TO_DATE("79", "IY"));
date = new SimpleDateFormat("yyyy").parse("979");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("979", "YYY"));
assertEquals(date, ToDate.TO_DATE("979", "IYY"));
// Gregorian calendar does not have a year 0. 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
date = new SimpleDateFormat("yyy").parse("-99");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("0100 BC", "YYYY BC"));
assertEquals(date, ToDate.TO_DATE("0100 B.C.", "YYYY B.C."));
assertEquals(date, ToDate.TO_DATE("100 BC", "YYY BC"));
assertEquals(date, ToDate.TO_DATE("-0100", "SYYYY"));
assertEquals(date, ToDate.TO_DATE("-0100", "YYYY"));
// Gregorian calendar does not have a year 0. 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
date = new SimpleDateFormat("y").parse("0");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("01 BC", "YY BC"));
assertEquals(date, ToDate.TO_DATE("1 BC", "Y BC"));
date = new SimpleDateFormat("hh:mm:ss").parse("08:12:00");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("08:12 AM", "HH:MI AM"));
assertEquals(date, ToDate.TO_DATE("08:12 A.M.", "HH:MI A.M."));
assertEquals(date, ToDate.TO_DATE("08:12", "HH24:MI"));
date = new SimpleDateFormat("hh:mm:ss").parse("08:12:00");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("08:12", "HH:MI"));
assertEquals(date, ToDate.TO_DATE("08:12", "HH12:MI"));
date = new SimpleDateFormat("hh:mm:ss").parse("08:12:34");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("08:12:34", "HH:MI:SS"));
date = new SimpleDateFormat("ss").parse("34");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("34", "SS"));
date = new SimpleDateFormat("yyyy hh:mm:ss").parse("1970 08:12:34");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("29554", "SSSSS"));
date = new SimpleDateFormat("yyyy hh:mm:ss SSS").parse("1970 08:12:34 550");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("08:12:34 550", "HH:MI:SS FF"));
assertEquals(date, ToDate.TO_DATE("08:12:34 55", "HH:MI:SS FF2"));
date = new SimpleDateFormat("hh:mm:ss").parse("14:04:00");
date.setMonth(curMonth);
assertEquals(date, ToDate.TO_DATE("02:04 P.M.", "HH:MI p.M."));
assertEquals(date, ToDate.TO_DATE("02:04 PM", "HH:MI PM"));
date = new SimpleDateFormat("yyyy-MM-dd").parse("1970-12-12");
assertEquals(date, ToDate.TO_DATE("12", "DD"));
date = new SimpleDateFormat("yyyy-MM-dd").parse("1970-11-12");
assertEquals(date, ToDate.TO_DATE("316", "DDD"));
assertEquals(date, ToDate.TO_DATE("316", "DdD"));
assertEquals(date, ToDate.TO_DATE("316", "dDD"));
assertEquals(date, ToDate.TO_DATE("316", "ddd"));
date = new SimpleDateFormat("yyyy-MM-dd").parse("2013-01-29");
assertEquals(date, ToDate.TO_DATE("113029", "J"));
}
private void testToCharFromDateTime() throws SQLException { private void testToCharFromDateTime() throws SQLException {
deleteDb("functions"); deleteDb("functions");
Connection conn = getConnection("functions"); Connection conn = getConnection("functions");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论