Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
3d253279
提交
3d253279
authored
12月 31, 2015
作者:
Noel Grandin
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #222 from plus33/master
Added TO_DATE and TO_TIMESTAMP functionality
上级
0e6a9baa
769aa54b
隐藏空白字符变更
内嵌
并排
正在显示
17 个修改的文件
包含
907 行增加
和
16 行删除
+907
-16
ErrorCode.java
h2/src/main/org/h2/api/ErrorCode.java
+7
-1
Function.java
h2/src/main/org/h2/expression/Function.java
+15
-11
_messages_cs.prop
h2/src/main/org/h2/res/_messages_cs.prop
+1
-0
_messages_de.prop
h2/src/main/org/h2/res/_messages_de.prop
+1
-0
_messages_en.prop
h2/src/main/org/h2/res/_messages_en.prop
+1
-0
_messages_es.prop
h2/src/main/org/h2/res/_messages_es.prop
+1
-0
_messages_ja.prop
h2/src/main/org/h2/res/_messages_ja.prop
+1
-0
_messages_pl.prop
h2/src/main/org/h2/res/_messages_pl.prop
+1
-0
_messages_pt_br.prop
h2/src/main/org/h2/res/_messages_pt_br.prop
+1
-0
_messages_ru.prop
h2/src/main/org/h2/res/_messages_ru.prop
+1
-0
_messages_zh_cn.prop
h2/src/main/org/h2/res/_messages_zh_cn.prop
+1
-0
DateTimeUtils.java
h2/src/main/org/h2/util/DateTimeUtils.java
+1
-4
ToDate.java
h2/src/main/org/h2/util/ToDate.java
+19
-0
ToDateParser.java
h2/src/main/org/h2/util/ToDateParser.java
+179
-0
ToDateTokenizer.java
h2/src/main/org/h2/util/ToDateTokenizer.java
+494
-0
ToDate.java
h2/src/test/org/h2/samples/ToDate.java
+60
-0
TestFunctions.java
h2/src/test/org/h2/test/db/TestFunctions.java
+123
-0
没有找到文件。
h2/src/main/org/h2/api/ErrorCode.java
浏览文件 @
3d253279
...
@@ -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 90
056, 90
110, 90122, 90143
// next are 90110, 90122, 90143
private
ErrorCode
()
{
private
ErrorCode
()
{
// utility class
// utility class
...
...
h2/src/main/org/h2/expression/Function.java
浏览文件 @
3d253279
...
@@ -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
;
...
...
h2/src/main/org/h2/res/_messages_cs.prop
浏览文件 @
3d253279
...
@@ -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}
...
...
h2/src/main/org/h2/res/_messages_de.prop
浏览文件 @
3d253279
...
@@ -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}
...
...
h2/src/main/org/h2/res/_messages_en.prop
浏览文件 @
3d253279
...
@@ -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}
...
...
h2/src/main/org/h2/res/_messages_es.prop
浏览文件 @
3d253279
...
@@ -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}
...
...
h2/src/main/org/h2/res/_messages_ja.prop
浏览文件 @
3d253279
...
@@ -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} �������
...
...
h2/src/main/org/h2/res/_messages_pl.prop
浏览文件 @
3d253279
...
@@ -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}
...
...
h2/src/main/org/h2/res/_messages_pt_br.prop
浏览文件 @
3d253279
...
@@ -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.
...
...
h2/src/main/org/h2/res/_messages_ru.prop
浏览文件 @
3d253279
...
@@ -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}
...
...
h2/src/main/org/h2/res/_messages_zh_cn.prop
浏览文件 @
3d253279
...
@@ -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}
...
...
h2/src/main/org/h2/util/DateTimeUtils.java
浏览文件 @
3d253279
...
@@ -11,10 +11,7 @@ import java.sql.Date;
...
@@ -11,10 +11,7 @@ import java.sql.Date;
import
java.sql.Time
;
import
java.sql.Time
;
import
java.sql.Timestamp
;
import
java.sql.Timestamp
;
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
;
...
...
h2/src/main/org/h2/util/ToDate.java
0 → 100644
浏览文件 @
3d253279
package
org
.
h2
.
util
;
import
java.sql.Timestamp
;
import
java.util.Date
;
/**
* Emulates Oracle's TO_DATE function.<br>
* Main class
*/
public
class
ToDate
{
public
static
Timestamp
TO_DATE
(
final
String
input
,
final
String
format
)
{
ToDateParser
parser
=
ToDateParser
.
toDate
(
input
,
format
);
return
parser
.
getResultingTimestamp
();
}
public
static
Timestamp
TO_TIMESTAMP
(
final
String
input
,
final
String
format
)
{
ToDateParser
parser
=
ToDateParser
.
toTimestamp
(
input
,
format
);
return
parser
.
getResultingTimestamp
();
}
}
\ No newline at end of file
h2/src/main/org/h2/util/ToDateParser.java
0 → 100644
浏览文件 @
3d253279
package
org
.
h2
.
util
;
import
static
java
.
lang
.
String
.
format
;
import
java.sql.Timestamp
;
import
java.util.Calendar
;
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
ToDateParser
{
private
final
String
unmodifiedInputStr
;
private
final
String
unmodifiedFormatStr
;
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)
*/
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
));
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
}
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
;
}
String
getFunctionName
()
{
return
functionName
.
name
();
}
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
);
}
}
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
();
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
h2/src/main/org/h2/util/ToDateTokenizer.java
0 → 100644
浏览文件 @
3d253279
package
org
.
h2
.
util
;
import
static
java
.
lang
.
String
.
format
;
import
java.text.ParseException
;
import
java.text.SimpleDateFormat
;
import
java.util.*
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
org.h2.api.ErrorCode
;
import
org.h2.message.DbException
;
/**
* Emulates Oracle's TO_DATE function.<br>
* This class knows all about the TO_DATE-format conventions and how to parse the corresponding data
*/
class
ToDateTokenizer
{
private
static
final
Pattern
PATTERN_NUMBER
=
Pattern
.
compile
(
"^([+-]?[0-9]+)"
);
private
static
final
Pattern
PATTERN_FOUR_DIGITS
=
Pattern
.
compile
(
"^([+-]?[0-9]{4})"
);
private
static
final
Pattern
PATTERN_THREE_DIGITS
=
Pattern
.
compile
(
"^([+-]?[0-9]{3})"
);
private
static
final
Pattern
PATTERN_TWO_DIGITS
=
Pattern
.
compile
(
"^([+-]?[0-9]{2})"
);
private
static
final
Pattern
PATTERN_TWO_DIGITS_OR_LESS
=
Pattern
.
compile
(
"^([+-]?[0-9][0-9]?)"
);
private
static
final
Pattern
PATTERN_ONE_DIGIT
=
Pattern
.
compile
(
"^([+-]?[0-9])"
);
private
static
final
Pattern
PATTERN_FF
=
Pattern
.
compile
(
"^(FF[0-9]?)"
,
Pattern
.
CASE_INSENSITIVE
);
private
static
final
Pattern
PATTERN_AM_PM
=
Pattern
.
compile
(
"^(AM|A\\.M\\.|PM|P\\.M\\.)"
,
Pattern
.
CASE_INSENSITIVE
);
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
DayParslet
PARSLET_DAY
=
new
DayParslet
();
private
static
final
TimeParslet
PARSLET_TIME
=
new
TimeParslet
();
static
enum
FormatTokenEnum
{
YYYY
(
PARSLET_YEAR
)
// 4-digit year
,
SYYYY
(
PARSLET_YEAR
)
// 4-digit year with sign (- = B.C.)
,
IYYY
(
PARSLET_YEAR
)
// 4-digit year based on the ISO standard (?)
,
YYY
(
PARSLET_YEAR
)
//
,
IYY
(
PARSLET_YEAR
)
//
,
YY
(
PARSLET_YEAR
)
//
,
IY
(
PARSLET_YEAR
)
//
,
SCC
(
PARSLET_YEAR
)
// Two-digit century with with sign (- = B.C.)
,
CC
(
PARSLET_YEAR
)
// Two-digit century.
,
RRRR
(
PARSLET_YEAR
)
// 2-digit -> 4-digit year 0-49 -> 20xx , 50-99 -> 19xx
,
RR
(
PARSLET_YEAR
)
// last 2-digit of the year using "current" century value.
,
BC_AD
(
PARSLET_YEAR
,
PATTERN_BC_AD
)
// Meridian indicator
,
MONTH
(
PARSLET_MONTH
)
// Full Name of month
,
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).
,
DDD
(
PARSLET_DAY
)
// Day of year (1-366).
,
DAY
(
PARSLET_DAY
)
// Name of day.
,
DD
(
PARSLET_DAY
)
// Day of month (1-31).
,
DY
(
PARSLET_DAY
)
// Abbreviated name of day.
,
HH24
(
PARSLET_TIME
)
//
,
HH12
(
PARSLET_TIME
)
//
,
HH
(
PARSLET_TIME
)
// Hour of day (1-12).
,
MI
(
PARSLET_TIME
)
// Min
,
SSSSS
(
PARSLET_TIME
)
// Seconds past midnight (0-86399)
,
SS
(
PARSLET_TIME
)
//
,
FF
(
PARSLET_TIME
,
PATTERN_FF
)
// Fractional seconds
,
TZH
(
PARSLET_TIME
)
// Time zone hour.
,
TZM
(
PARSLET_TIME
)
// Time zone minute.
,
TZR
(
PARSLET_TIME
)
// Time zone region ID
,
TZD
(
PARSLET_TIME
)
// Daylight savings information. Example: PST (for US/Pacific standard time);
,
AM_PM
(
PARSLET_TIME
,
PATTERN_AM_PM
)
// Meridian indicator
,
EE
(
PARSLET_YEAR
)
// NOT supported yet - Full era name (Japanese Imperial, ROC Official, and Thai Buddha calendars).
,
E
(
PARSLET_YEAR
)
// NOT supported yet - Abbreviated era name (Japanese Imperial, ROC Official, and Thai Buddha calendars).
,
Y
(
PARSLET_YEAR
)
//
,
I
(
PARSLET_YEAR
)
//
,
Q
(
PARSLET_MONTH
)
// Quarter of year (1, 2, 3, 4; JAN-MAR = 1).
,
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.
private
final
static
Map
<
Character
,
List
<
FormatTokenEnum
>>
cache
=
new
HashMap
<
Character
,
List
<
FormatTokenEnum
>>(
FormatTokenEnum
.
values
().
length
);
private
final
ToDateParslet
toDateParslet
;
private
final
Pattern
patternToUse
;
FormatTokenEnum
(
final
ToDateParslet
toDateParslet
,
final
Pattern
patternToUse
)
{
this
.
toDateParslet
=
toDateParslet
;
this
.
patternToUse
=
patternToUse
;
}
FormatTokenEnum
(
final
ToDateParslet
toDateParslet
)
{
this
.
toDateParslet
=
toDateParslet
;
patternToUse
=
Pattern
.
compile
(
format
(
"^(%s)"
,
name
()),
Pattern
.
CASE_INSENSITIVE
);
}
private
static
List
<
FormatTokenEnum
>
EMPTY_LIST
=
new
ArrayList
<
FormatTokenEnum
>(
0
);
/**
* 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
(
String
formatStr
)
{
List
<
FormatTokenEnum
>
result
=
EMPTY_LIST
;
if
(
cache
.
size
()
<=
0
)
{
initCache
();
}
if
(
formatStr
!=
null
&&
formatStr
.
length
()
>
0
)
{
Character
key
=
Character
.
toUpperCase
(
formatStr
.
charAt
(
0
));
result
=
cache
.
get
(
key
);
}
if
(
result
==
null
)
{
result
=
EMPTY_LIST
;
}
return
result
;
}
private
static
synchronized
void
initCache
()
{
if
(
cache
.
size
()
<=
0
)
{
for
(
FormatTokenEnum
token
:
FormatTokenEnum
.
values
())
{
List
<
Character
>
tokenKeys
=
new
ArrayList
<
Character
>();
if
(
token
.
name
().
contains
(
"_"
))
{
String
[]
tokens
=
token
.
name
().
split
(
"_"
);
for
(
String
tokenLets
:
tokens
)
{
tokenKeys
.
add
(
tokenLets
.
toUpperCase
().
charAt
(
0
));
}
}
else
{
tokenKeys
.
add
(
token
.
name
().
toUpperCase
().
charAt
(
0
));
}
for
(
Character
tokenKey
:
tokenKeys
)
{
List
<
FormatTokenEnum
>
l
=
cache
.
get
(
tokenKey
);
if
(
l
==
null
)
{
l
=
new
ArrayList
<
FormatTokenEnum
>(
1
);
cache
.
put
(
tokenKey
,
l
);
}
l
.
add
(
token
);
}
}
}
}
/**
* Parse the format-string with passed token of {@link FormatTokenEnum}}.<br>
* If token matches return true, otherwise false.
*/
boolean
parseFormatStrWithToken
(
final
ToDateParser
params
)
{
Matcher
matcher
=
patternToUse
.
matcher
(
params
.
getFormatStr
());
boolean
foundToken
=
matcher
.
find
();
if
(
foundToken
)
{
String
formatTokenStr
=
matcher
.
group
(
1
);
toDateParslet
.
parse
(
params
,
this
,
formatTokenStr
);
}
return
foundToken
;
}
}
/**
* Interface of the classes that can parse a specialized small bit of the TO_DATE format-string
*/
interface
ToDateParslet
{
void
parse
(
ToDateParser
params
,
FormatTokenEnum
formatTokenEnum
,
String
formatTokenStr
);
}
/**
* Parslet responsible for parsing year parameter
*/
private
static
final
class
YearParslet
implements
ToDateParslet
{
@Override
public
void
parse
(
final
ToDateParser
params
,
final
FormatTokenEnum
formatTokenEnum
,
final
String
formatTokenStr
)
{
final
Calendar
result
=
params
.
getResultCalendar
();
String
inputFragmentStr
=
null
;
int
dateNr
=
0
;
switch
(
formatTokenEnum
)
{
case
SYYYY:
case
YYYY:
case
IYYY:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_FOUR_DIGITS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
// 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
);
break
;
case
YYY:
case
IYY:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_THREE_DIGITS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
// 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
);
break
;
case
RRRR:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
dateNr
+=
dateNr
<
50
?
2000
:
1900
;
result
.
set
(
Calendar
.
YEAR
,
dateNr
);
break
;
case
RR:
Calendar
calendar
=
Calendar
.
getInstance
();
int
cc
=
(
calendar
.
get
(
Calendar
.
YEAR
)
/
100
);
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
)
+
cc
*
100
;
result
.
set
(
Calendar
.
YEAR
,
dateNr
);
break
;
case
EE
/*NOT supported yet*/
:
throwException
(
params
,
format
(
"token '%s' not supported yet."
,
formatTokenEnum
.
name
()));
break
;
case
E
/*NOT supported yet*/
:
throwException
(
params
,
format
(
"token '%s' not supported yet."
,
formatTokenEnum
.
name
()));
break
;
case
YY:
case
IY:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
// 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
);
break
;
case
SCC:
case
CC:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
)
*
100
;
result
.
set
(
Calendar
.
YEAR
,
dateNr
);
break
;
case
Y:
case
I:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_ONE_DIGIT
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
// 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
);
break
;
case
BC_AD:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_BC_AD
,
params
,
formatTokenEnum
);
if
(
inputFragmentStr
.
toUpperCase
().
startsWith
(
"B"
))
{
result
.
set
(
Calendar
.
ERA
,
GregorianCalendar
.
BC
);
}
else
{
result
.
set
(
Calendar
.
ERA
,
GregorianCalendar
.
AD
);
}
break
;
default
:
throw
new
IllegalArgumentException
(
format
(
"%s: Internal Error. Unhandled case: %s"
,
this
.
getClass
()
.
getSimpleName
(),
formatTokenEnum
));
}
params
.
remove
(
inputFragmentStr
,
formatTokenStr
);
}
}
/**
* Parslet responsible for parsing month parameter
*/
private
static
final
class
MonthParslet
implements
ToDateParslet
{
private
static
final
String
[]
ROMAN_Month
=
{
"I"
,
"II"
,
"III"
,
"IV"
,
"V"
,
"VI"
,
"VII"
,
"VIII"
,
"IX"
,
"X"
,
"XI"
,
"XII"
};
@Override
public
void
parse
(
final
ToDateParser
params
,
final
FormatTokenEnum
formatTokenEnum
,
final
String
formatTokenStr
)
{
final
Calendar
result
=
params
.
getResultCalendar
();
final
String
s
=
params
.
getInputStr
();
String
inputFragmentStr
=
null
;
int
dateNr
=
0
;
switch
(
formatTokenEnum
)
{
case
MONTH:
inputFragmentStr
=
setByName
(
result
,
params
,
Calendar
.
MONTH
,
Calendar
.
LONG
);
break
;
case
Q
/*NOT supported yet*/
:
throwException
(
params
,
format
(
"token '%s' not supported jet."
,
formatTokenEnum
.
name
()));
break
;
case
MON:
inputFragmentStr
=
setByName
(
result
,
params
,
Calendar
.
MONTH
,
Calendar
.
SHORT
);
break
;
case
MM:
// Note: In Calendar Month go from 0 - 11
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS_OR_LESS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
result
.
set
(
Calendar
.
MONTH
,
dateNr
-
1
);
break
;
case
RM:
dateNr
=
0
;
for
(
String
monthName
:
ROMAN_Month
)
{
dateNr
++;
int
leng
=
monthName
.
length
();
if
(
s
.
length
()
>=
leng
&&
monthName
.
equalsIgnoreCase
(
s
.
substring
(
0
,
leng
)))
{
result
.
set
(
Calendar
.
MONTH
,
dateNr
);
inputFragmentStr
=
monthName
;
break
;
}
}
if
(
inputFragmentStr
==
null
||
inputFragmentStr
.
isEmpty
())
{
throwException
(
params
,
format
(
"Issue happened when parsing token '%s'. Expected one of: %s"
,
formatTokenEnum
.
name
(),
Arrays
.
toString
(
ROMAN_Month
)));
}
break
;
default
:
throw
new
IllegalArgumentException
(
format
(
"%s: Internal Error. Unhandled case: %s"
,
this
.
getClass
()
.
getSimpleName
(),
formatTokenEnum
));
}
params
.
remove
(
inputFragmentStr
,
formatTokenStr
);
}
}
/**
* Parslet responsible for parsing day parameter
*/
private
static
final
class
DayParslet
implements
ToDateParslet
{
@Override
public
void
parse
(
final
ToDateParser
params
,
final
FormatTokenEnum
formatTokenEnum
,
final
String
formatTokenStr
)
{
final
Calendar
result
=
params
.
getResultCalendar
();
String
inputFragmentStr
=
null
;
int
dateNr
=
0
;
switch
(
formatTokenEnum
)
{
case
DDD:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_NUMBER
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
result
.
set
(
Calendar
.
DAY_OF_YEAR
,
dateNr
);
break
;
case
DD:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS_OR_LESS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
result
.
set
(
Calendar
.
DAY_OF_MONTH
,
dateNr
);
break
;
case
D:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_ONE_DIGIT
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
result
.
set
(
Calendar
.
DAY_OF_MONTH
,
dateNr
);
break
;
case
DAY:
inputFragmentStr
=
setByName
(
result
,
params
,
Calendar
.
DAY_OF_WEEK
,
Calendar
.
LONG
);
break
;
case
DY:
inputFragmentStr
=
setByName
(
result
,
params
,
Calendar
.
DAY_OF_WEEK
,
Calendar
.
SHORT
);
break
;
case
J:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_NUMBER
,
params
,
formatTokenEnum
);
try
{
Date
date
=
new
SimpleDateFormat
(
"Myydd"
).
parse
(
inputFragmentStr
);
result
.
setTime
(
date
);
}
catch
(
ParseException
e
)
{
throwException
(
params
,
format
(
"Failed to parse Julian date: %s"
,
inputFragmentStr
));
}
break
;
default
:
throw
new
IllegalArgumentException
(
format
(
"%s: Internal Error. Unhandled case: %s"
,
this
.
getClass
()
.
getSimpleName
(),
formatTokenEnum
));
}
params
.
remove
(
inputFragmentStr
,
formatTokenStr
);
}
}
private
static
int
MILLIS_in_hour
=
60
*
60
*
1000
;
/**
* Parslet responsible for parsing time parameter
*/
private
static
final
class
TimeParslet
implements
ToDateParslet
{
@Override
public
void
parse
(
final
ToDateParser
params
,
final
FormatTokenEnum
formatTokenEnum
,
final
String
formatTokenStr
)
{
final
Calendar
result
=
params
.
getResultCalendar
();
String
inputFragmentStr
=
null
;
int
dateNr
=
0
;
switch
(
formatTokenEnum
)
{
case
HH24:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS_OR_LESS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
result
.
set
(
Calendar
.
HOUR_OF_DAY
,
dateNr
);
break
;
case
HH12:
case
HH:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS_OR_LESS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
result
.
set
(
Calendar
.
HOUR
,
dateNr
);
break
;
case
MI:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS_OR_LESS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
result
.
set
(
Calendar
.
MINUTE
,
dateNr
);
break
;
case
SS:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS_OR_LESS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
result
.
set
(
Calendar
.
SECOND
,
dateNr
);
break
;
case
SSSSS:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_NUMBER
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
result
.
set
(
Calendar
.
HOUR_OF_DAY
,
0
);
result
.
set
(
Calendar
.
MINUTE
,
0
);
result
.
set
(
Calendar
.
SECOND
,
dateNr
);
break
;
case
FF:
//
inputFragmentStr
=
matchStringOrDie
(
PATTERN_NUMBER
,
params
,
formatTokenEnum
);
String
paddedRightNrStr
=
format
(
"%-9s"
,
inputFragmentStr
).
replace
(
' '
,
'0'
);
paddedRightNrStr
=
paddedRightNrStr
.
substring
(
0
,
9
);
Double
nineDigits
=
Double
.
parseDouble
(
paddedRightNrStr
);
params
.
setNanos
(
nineDigits
.
intValue
());
dateNr
=
(
int
)
Math
.
round
(
nineDigits
/
1000000.0
);
result
.
set
(
Calendar
.
MILLISECOND
,
dateNr
);
break
;
case
AM_PM:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_AM_PM
,
params
,
formatTokenEnum
);
if
(
inputFragmentStr
.
toUpperCase
().
startsWith
(
"A"
))
{
result
.
set
(
Calendar
.
AM_PM
,
Calendar
.
AM
);
}
else
{
result
.
set
(
Calendar
.
AM_PM
,
Calendar
.
PM
);
}
break
;
case
TZH:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS_OR_LESS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
TimeZone
tz
=
result
.
getTimeZone
();
int
offsetMillis
=
tz
.
getRawOffset
();
offsetMillis
=
(
offsetMillis
/
MILLIS_in_hour
)
*
MILLIS_in_hour
;
// purge min and sec
tz
.
setRawOffset
(
offsetMillis
+
dateNr
);
result
.
setTimeZone
(
tz
);
break
;
case
TZM:
inputFragmentStr
=
matchStringOrDie
(
PATTERN_TWO_DIGITS_OR_LESS
,
params
,
formatTokenEnum
);
dateNr
=
parseInt
(
inputFragmentStr
);
tz
=
result
.
getTimeZone
();
offsetMillis
=
tz
.
getRawOffset
();
offsetMillis
=
offsetMillis
%
MILLIS_in_hour
;
// purge hour
tz
.
setRawOffset
(
dateNr
*
MILLIS_in_hour
+
offsetMillis
);
result
.
setTimeZone
(
tz
);
break
;
case
TZR:
// Example: US/Pacific
final
String
s
=
params
.
getInputStr
();
tz
=
result
.
getTimeZone
();
for
(
String
tzName
:
TimeZone
.
getAvailableIDs
())
{
int
length
=
tzName
.
length
();
if
(
s
.
length
()
>=
length
&&
tzName
.
equalsIgnoreCase
(
s
.
substring
(
0
,
length
)))
{
tz
.
setID
(
tzName
);
result
.
setTimeZone
(
tz
);
inputFragmentStr
=
tzName
;
break
;
}
}
break
;
case
TZD:
// Must correspond with TZR region. Example: PST (for US/Pacific standard time)
throwException
(
params
,
format
(
"token '%s' not supported yet."
,
formatTokenEnum
.
name
()));
break
;
default
:
throw
new
IllegalArgumentException
(
format
(
"%s: Internal Error. Unhandled case: %s"
,
this
.
getClass
()
.
getSimpleName
(),
formatTokenEnum
));
}
params
.
remove
(
inputFragmentStr
,
formatTokenStr
);
}
}
private
static
int
parseInt
(
final
String
s
)
{
int
result
=
0
;
if
(
s
.
length
()
>
0
&&
s
.
charAt
(
0
)
==
'+'
)
{
result
=
Integer
.
parseInt
(
s
.
substring
(
1
));
}
else
{
result
=
Integer
.
parseInt
(
s
);
}
return
result
;
}
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
())
{
throwException
(
params
,
format
(
"Issue happend when parsing token '%s'"
,
aEnum
.
name
()));
}
return
matcher
.
group
(
1
);
}
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
());
for
(
String
dayName
:
timeStringMap
.
keySet
())
{
int
leng
=
dayName
.
length
();
if
(
dayName
.
equalsIgnoreCase
(
s
.
substring
(
0
,
leng
)))
{
c
.
set
(
field
,
timeStringMap
.
get
(
dayName
));
inputFragmentStr
=
dayName
;
break
;
}
}
if
(
inputFragmentStr
==
null
||
inputFragmentStr
.
isEmpty
())
{
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
ToDateParser
params
,
final
String
errorStr
)
{
throw
DbException
.
get
(
ErrorCode
.
INVALID_TO_DATE_FORMAT
,
params
.
getFunctionName
(),
format
(
" %s. Details: %s"
,
errorStr
,
params
));
}
}
\ No newline at end of file
h2/src/test/org/h2/samples/ToDate.java
0 → 100644
浏览文件 @
3d253279
/*
* 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
);
}
}
h2/src/test/org/h2/test/db/TestFunctions.java
浏览文件 @
3d253279
...
@@ -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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论