Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
bef4c05e
Unverified
提交
bef4c05e
authored
2月 21, 2018
作者:
Noel Grandin
提交者:
GitHub
2月 21, 2018
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #890 from katzyn/datetime
Implement conversions for TIMESTAMP WITH TIME ZONE
上级
525cc9ac
d0e0e8f9
显示空白字符变更
内嵌
并排
正在显示
8 个修改的文件
包含
184 行增加
和
28 行删除
+184
-28
DateTimeUtils.java
h2/src/main/org/h2/util/DateTimeUtils.java
+64
-2
LocalDateTimeUtils.java
h2/src/main/org/h2/util/LocalDateTimeUtils.java
+20
-5
DataType.java
h2/src/main/org/h2/value/DataType.java
+2
-2
Value.java
h2/src/main/org/h2/value/Value.java
+39
-10
ValueTimestampTimeZone.java
h2/src/main/org/h2/value/ValueTimestampTimeZone.java
+1
-1
TestPreparedStatement.java
h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
+16
-7
TestTimeStampWithTimeZone.java
h2/src/test/org/h2/test/unit/TestTimeStampWithTimeZone.java
+41
-0
dictionary.txt
h2/src/tools/org/h2/build/doc/dictionary.txt
+1
-1
没有找到文件。
h2/src/main/org/h2/util/DateTimeUtils.java
浏览文件 @
bef4c05e
...
...
@@ -510,6 +510,41 @@ public class DateTimeUtils {
return
ValueTimestamp
.
fromDateValueAndNanos
(
dateValue
,
nanos
);
}
/**
* Calculates the time zone offset in minutes for the specified time zone, date
* value, and nanoseconds since midnight.
*
* @param tz
* time zone, or {@code null} for default
* @param dateValue
* date value
* @param timeNanos
* nanoseconds since midnight
* @return time zone offset in milliseconds
*/
public
static
int
getTimeZoneOffsetMillis
(
TimeZone
tz
,
long
dateValue
,
long
timeNanos
)
{
long
msec
=
timeNanos
/
1_000_000
;
long
utc
=
convertDateTimeValueToMillis
(
tz
,
dateValue
,
msec
);
long
local
=
absoluteDayFromDateValue
(
dateValue
)
*
MILLIS_PER_DAY
+
msec
;
return
(
int
)
(
local
-
utc
);
}
/**
* Calculates the milliseconds since epoch for the specified date value,
* nanoseconds since midnight, and time zone offset.
* @param dateValue
* date value
* @param timeNanos
* nanoseconds since midnight
* @param offsetMins
* time zone offset in minutes
* @return milliseconds since epoch in UTC
*/
public
static
long
getMillis
(
long
dateValue
,
long
timeNanos
,
short
offsetMins
)
{
return
absoluteDayFromDateValue
(
dateValue
)
*
MILLIS_PER_DAY
+
timeNanos
/
1_000_000
-
offsetMins
*
60_000
;
}
/**
* Calculate the milliseconds since 1970-01-01 (UTC) for the given date and
* time (in the specified timezone).
...
...
@@ -971,8 +1006,7 @@ public class DateTimeUtils {
* @return the timestamp
*/
public
static
Timestamp
convertTimestampTimeZoneToTimestamp
(
long
dateValue
,
long
timeNanos
,
short
offsetMins
)
{
Timestamp
ts
=
new
Timestamp
(
absoluteDayFromDateValue
(
dateValue
)
*
MILLIS_PER_DAY
+
timeNanos
/
1_000_000
-
offsetMins
*
60_000
);
Timestamp
ts
=
new
Timestamp
(
getMillis
(
dateValue
,
timeNanos
,
offsetMins
));
ts
.
setNanos
((
int
)
(
timeNanos
%
1_000_000_000
));
return
ts
;
}
...
...
@@ -1157,6 +1191,34 @@ public class DateTimeUtils {
dateValueFromAbsoluteDay
(
absoluteDay
),
nanos
);
}
/**
* Converts local date value and nanoseconds to timestamp with time zone.
*
* @param dateValue
* date value
* @param timeNanos
* nanoseconds since midnight
* @return timestamp with time zone
*/
public
static
ValueTimestampTimeZone
timestampTimeZoneFromLocalDateValueAndNanos
(
long
dateValue
,
long
timeNanos
)
{
int
timeZoneOffset
=
DateTimeUtils
.
getTimeZoneOffsetMillis
(
null
,
dateValue
,
timeNanos
);
int
offsetMins
=
timeZoneOffset
/
60_000
;
int
correction
=
timeZoneOffset
%
60_000
;
if
(
correction
!=
0
)
{
timeNanos
-=
correction
;
if
(
timeNanos
<
0
)
{
timeNanos
+=
DateTimeUtils
.
NANOS_PER_DAY
;
dateValue
=
DateTimeUtils
.
dateValueFromAbsoluteDay
(
DateTimeUtils
.
absoluteDayFromDateValue
(
dateValue
)
-
1
);
}
else
if
(
timeNanos
>=
DateTimeUtils
.
NANOS_PER_DAY
)
{
timeNanos
-=
DateTimeUtils
.
NANOS_PER_DAY
;
dateValue
=
DateTimeUtils
.
dateValueFromAbsoluteDay
(
DateTimeUtils
.
absoluteDayFromDateValue
(
dateValue
)
+
1
);
}
}
return
ValueTimestampTimeZone
.
fromDateValueAndNanos
(
dateValue
,
timeNanos
,
(
short
)
offsetMins
);
}
/**
* Calculate the absolute day from an encoded date value.
*
...
...
h2/src/main/org/h2/util/LocalDateTimeUtils.java
浏览文件 @
bef4c05e
...
...
@@ -101,9 +101,13 @@ public class LocalDateTimeUtils {
private
static
final
Method
LOCAL_DATE_AT_START_OF_DAY
;
/**
* {@code java.
sql.Timestamp.from(java.time.Instant
)} or {@code null}.
* {@code java.
time.Instant#getEpochSecond(
)} or {@code null}.
*/
private
static
final
Method
TIMESTAMP_FROM
;
private
static
final
Method
INSTANT_GET_EPOCH_SECOND
;
/**
* {@code java.time.Instant#getNano()} or {@code null}.
*/
private
static
final
Method
INSTANT_GET_NANO
;
/**
* {@code java.sql.Timestamp.toInstant()} or {@code null}.
*/
...
...
@@ -186,7 +190,8 @@ public class LocalDateTimeUtils {
LOCAL_DATE_GET_DAY_OF_MONTH
=
getMethod
(
LOCAL_DATE
,
"getDayOfMonth"
);
LOCAL_DATE_AT_START_OF_DAY
=
getMethod
(
LOCAL_DATE
,
"atStartOfDay"
);
TIMESTAMP_FROM
=
getMethod
(
Timestamp
.
class
,
"from"
,
INSTANT
);
INSTANT_GET_EPOCH_SECOND
=
getMethod
(
INSTANT
,
"getEpochSecond"
);
INSTANT_GET_NANO
=
getMethod
(
INSTANT
,
"getNano"
);
TIMESTAMP_TO_INSTANT
=
getMethod
(
Timestamp
.
class
,
"toInstant"
);
LOCAL_TIME_PARSE
=
getMethod
(
LOCAL_TIME
,
"parse"
,
CharSequence
.
class
);
...
...
@@ -214,7 +219,8 @@ public class LocalDateTimeUtils {
LOCAL_DATE_GET_MONTH_VALUE
=
null
;
LOCAL_DATE_GET_DAY_OF_MONTH
=
null
;
LOCAL_DATE_AT_START_OF_DAY
=
null
;
TIMESTAMP_FROM
=
null
;
INSTANT_GET_EPOCH_SECOND
=
null
;
INSTANT_GET_NANO
=
null
;
TIMESTAMP_TO_INSTANT
=
null
;
LOCAL_TIME_PARSE
=
null
;
LOCAL_DATE_TIME_PLUS_NANOS
=
null
;
...
...
@@ -485,7 +491,16 @@ public class LocalDateTimeUtils {
*/
public
static
Value
instantToValue
(
Object
instant
)
{
try
{
return
ValueTimestamp
.
get
((
Timestamp
)
TIMESTAMP_FROM
.
invoke
(
null
,
instant
));
long
epochSecond
=
(
long
)
INSTANT_GET_EPOCH_SECOND
.
invoke
(
instant
);
int
nano
=
(
int
)
INSTANT_GET_NANO
.
invoke
(
instant
);
long
absoluteDay
=
epochSecond
/
86_400
;
// Round toward negative infinity
if
(
epochSecond
<
0
&&
(
absoluteDay
*
86_400
!=
epochSecond
))
{
absoluteDay
--;
}
long
timeNanos
=
(
epochSecond
-
absoluteDay
*
86_400
)
*
1_000_000_000
+
nano
;
return
ValueTimestampTimeZone
.
fromDateValueAndNanos
(
DateTimeUtils
.
dateValueFromAbsoluteDay
(
absoluteDay
),
timeNanos
,
(
short
)
0
);
}
catch
(
IllegalAccessException
e
)
{
throw
DbException
.
convert
(
e
);
}
catch
(
InvocationTargetException
e
)
{
...
...
h2/src/main/org/h2/value/DataType.java
浏览文件 @
bef4c05e
...
...
@@ -1020,9 +1020,9 @@ public class DataType {
return
Value
.
DATE
;
}
else
if
(
LocalDateTimeUtils
.
LOCAL_TIME
==
x
)
{
return
Value
.
TIME
;
}
else
if
(
LocalDateTimeUtils
.
LOCAL_DATE_TIME
==
x
||
LocalDateTimeUtils
.
INSTANT
==
x
)
{
}
else
if
(
LocalDateTimeUtils
.
LOCAL_DATE_TIME
==
x
)
{
return
Value
.
TIMESTAMP
;
}
else
if
(
LocalDateTimeUtils
.
OFFSET_DATE_TIME
==
x
)
{
}
else
if
(
LocalDateTimeUtils
.
OFFSET_DATE_TIME
==
x
||
LocalDateTimeUtils
.
INSTANT
==
x
)
{
return
Value
.
TIMESTAMP_TZ
;
}
else
{
if
(
JdbcUtils
.
customDataTypesHandler
!=
null
)
{
...
...
h2/src/main/org/h2/value/Value.java
浏览文件 @
bef4c05e
...
...
@@ -839,9 +839,12 @@ public abstract class Value {
case
TIMESTAMP:
return
ValueDate
.
fromDateValue
(
((
ValueTimestamp
)
this
).
getDateValue
());
case
TIMESTAMP_TZ:
return
ValueDate
.
fromDateValue
(
((
ValueTimestampTimeZone
)
this
).
getDateValue
());
case
TIMESTAMP_TZ:
{
ValueTimestampTimeZone
ts
=
(
ValueTimestampTimeZone
)
this
;
long
dateValue
=
ts
.
getDateValue
(),
timeNanos
=
ts
.
getTimeNanos
();
long
millis
=
DateTimeUtils
.
getMillis
(
dateValue
,
timeNanos
,
ts
.
getTimeZoneOffsetMins
());
return
ValueDate
.
fromMillis
(
millis
);
}
case
ENUM:
throw
DbException
.
get
(
ErrorCode
.
DATA_CONVERSION_ERROR_1
,
getString
());
...
...
@@ -857,9 +860,12 @@ public abstract class Value {
case
TIMESTAMP:
return
ValueTime
.
fromNanos
(
((
ValueTimestamp
)
this
).
getTimeNanos
());
case
TIMESTAMP_TZ:
return
ValueTime
.
fromNanos
(
((
ValueTimestampTimeZone
)
this
).
getTimeNanos
());
case
TIMESTAMP_TZ:
{
ValueTimestampTimeZone
ts
=
(
ValueTimestampTimeZone
)
this
;
long
dateValue
=
ts
.
getDateValue
(),
timeNanos
=
ts
.
getTimeNanos
();
long
millis
=
DateTimeUtils
.
getMillis
(
dateValue
,
timeNanos
,
ts
.
getTimeZoneOffsetMins
());
return
ValueTime
.
fromNanos
(
DateTimeUtils
.
nanosFromDate
(
millis
)
+
timeNanos
%
1_000_000
);
}
case
ENUM:
throw
DbException
.
get
(
ErrorCode
.
DATA_CONVERSION_ERROR_1
,
getString
());
...
...
@@ -874,10 +880,33 @@ public abstract class Value {
case
DATE:
return
ValueTimestamp
.
fromDateValueAndNanos
(
((
ValueDate
)
this
).
getDateValue
(),
0
);
case
TIMESTAMP_TZ:
return
ValueTimestamp
.
fromDateValueAndNanos
(
((
ValueTimestampTimeZone
)
this
).
getDateValue
(),
((
ValueTimestampTimeZone
)
this
).
getTimeNanos
());
case
TIMESTAMP_TZ:
{
ValueTimestampTimeZone
ts
=
(
ValueTimestampTimeZone
)
this
;
long
dateValue
=
ts
.
getDateValue
(),
timeNanos
=
ts
.
getTimeNanos
();
long
millis
=
DateTimeUtils
.
getMillis
(
dateValue
,
timeNanos
,
ts
.
getTimeZoneOffsetMins
());
return
ValueTimestamp
.
fromMillisNanos
(
millis
,
(
int
)
(
timeNanos
%
1_000_000
));
}
case
ENUM:
throw
DbException
.
get
(
ErrorCode
.
DATA_CONVERSION_ERROR_1
,
getString
());
}
break
;
}
case
TIMESTAMP_TZ:
{
switch
(
getType
())
{
case
TIME:
{
ValueTimestamp
ts
=
DateTimeUtils
.
normalizeTimestamp
(
0
,
((
ValueTime
)
this
).
getNanos
());
return
DateTimeUtils
.
timestampTimeZoneFromLocalDateValueAndNanos
(
ts
.
getDateValue
(),
ts
.
getTimeNanos
());
}
case
DATE:
return
DateTimeUtils
.
timestampTimeZoneFromLocalDateValueAndNanos
(
((
ValueDate
)
this
).
getDateValue
(),
0
);
case
TIMESTAMP:
{
ValueTimestamp
ts
=
(
ValueTimestamp
)
this
;
return
DateTimeUtils
.
timestampTimeZoneFromLocalDateValueAndNanos
(
ts
.
getDateValue
(),
ts
.
getTimeNanos
());
}
case
ENUM:
throw
DbException
.
get
(
ErrorCode
.
DATA_CONVERSION_ERROR_1
,
getString
());
...
...
h2/src/main/org/h2/value/ValueTimestampTimeZone.java
浏览文件 @
bef4c05e
...
...
@@ -148,7 +148,7 @@ public class ValueTimestampTimeZone extends Value {
@Override
public
Timestamp
getTimestamp
()
{
throw
new
UnsupportedOperationException
(
"unimplemented"
);
return
DateTimeUtils
.
convertTimestampTimeZoneToTimestamp
(
dateValue
,
timeNanos
,
timeZoneOffsetMins
);
}
@Override
...
...
h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
浏览文件 @
bef4c05e
...
...
@@ -9,6 +9,7 @@ import java.io.ByteArrayInputStream;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.StringReader
;
import
java.lang.reflect.InvocationTargetException
;
import
java.lang.reflect.Method
;
import
java.math.BigDecimal
;
import
java.math.BigInteger
;
...
...
@@ -755,18 +756,26 @@ public class TestPreparedStatement extends TestBase {
if
(!
LocalDateTimeUtils
.
isJava8DateApiPresent
())
{
return
;
}
Method
timestampToInstant
=
Timestamp
.
class
.
getMethod
(
"toInstant"
),
now
=
LocalDateTimeUtils
.
INSTANT
.
getMethod
(
"now"
);
Method
timestampToInstant
=
Timestamp
.
class
.
getMethod
(
"toInstant"
);
Method
now
=
LocalDateTimeUtils
.
INSTANT
.
getMethod
(
"now"
);
Method
parse
=
LocalDateTimeUtils
.
INSTANT
.
getMethod
(
"parse"
,
CharSequence
.
class
);
PreparedStatement
prep
=
conn
.
prepareStatement
(
"SELECT ?"
);
Object
instant1
=
now
.
invoke
(
null
);
prep
.
setObject
(
1
,
instant1
);
testInstant8Impl
(
prep
,
timestampToInstant
,
now
.
invoke
(
null
));
testInstant8Impl
(
prep
,
timestampToInstant
,
parse
.
invoke
(
null
,
"2000-01-15T12:13:14.123456789Z"
));
testInstant8Impl
(
prep
,
timestampToInstant
,
parse
.
invoke
(
null
,
"1500-09-10T23:22:11.123456789Z"
));
}
private
void
testInstant8Impl
(
PreparedStatement
prep
,
Method
timestampToInstant
,
Object
instant
)
throws
SQLException
,
IllegalAccessException
,
InvocationTargetException
{
prep
.
setObject
(
1
,
instant
);
ResultSet
rs
=
prep
.
executeQuery
();
rs
.
next
();
Object
instant2
=
rs
.
getObject
(
1
,
LocalDateTimeUtils
.
INSTANT
);
assertEquals
(
instant
1
,
instant2
);
assertEquals
(
instant
,
instant2
);
Timestamp
ts
=
rs
.
getTimestamp
(
1
);
assertEquals
(
instant
1
,
timestampToInstant
.
invoke
(
ts
));
assertEquals
(
instant
,
timestampToInstant
.
invoke
(
ts
));
assertFalse
(
rs
.
next
());
rs
.
close
();
...
...
@@ -774,7 +783,7 @@ public class TestPreparedStatement extends TestBase {
rs
=
prep
.
executeQuery
();
rs
.
next
();
instant2
=
rs
.
getObject
(
1
,
LocalDateTimeUtils
.
INSTANT
);
assertEquals
(
instant
1
,
instant2
);
assertEquals
(
instant
,
instant2
);
assertFalse
(
rs
.
next
());
rs
.
close
();
}
...
...
h2/src/test/org/h2/test/unit/TestTimeStampWithTimeZone.java
浏览文件 @
bef4c05e
...
...
@@ -11,11 +11,16 @@ import java.sql.ResultSet;
import
java.sql.ResultSetMetaData
;
import
java.sql.SQLException
;
import
java.sql.Statement
;
import
java.util.TimeZone
;
import
org.h2.api.TimestampWithTimeZone
;
import
org.h2.test.TestBase
;
import
org.h2.util.DateTimeUtils
;
import
org.h2.util.LocalDateTimeUtils
;
import
org.h2.value.Value
;
import
org.h2.value.ValueDate
;
import
org.h2.value.ValueTime
;
import
org.h2.value.ValueTimestamp
;
import
org.h2.value.ValueTimestampTimeZone
;
/**
...
...
@@ -40,6 +45,7 @@ public class TestTimeStampWithTimeZone extends TestBase {
test4
();
test5
();
testOrder
();
testConversions
();
deleteDb
(
getTestName
());
}
...
...
@@ -186,4 +192,39 @@ public class TestTimeStampWithTimeZone extends TestBase {
conn
.
close
();
}
private
void
testConversionsImpl
(
String
timeStr
,
boolean
testReverse
)
{
ValueTimestamp
ts
=
ValueTimestamp
.
parse
(
timeStr
);
ValueDate
d
=
(
ValueDate
)
ts
.
convertTo
(
Value
.
DATE
);
ValueTime
t
=
(
ValueTime
)
ts
.
convertTo
(
Value
.
TIME
);
ValueTimestampTimeZone
tstz
=
ValueTimestampTimeZone
.
parse
(
timeStr
);
assertEquals
(
ts
,
tstz
.
convertTo
(
Value
.
TIMESTAMP
));
assertEquals
(
d
,
tstz
.
convertTo
(
Value
.
DATE
));
assertEquals
(
t
,
tstz
.
convertTo
(
Value
.
TIME
));
assertEquals
(
ts
.
getTimestamp
(),
tstz
.
getTimestamp
());
if
(
testReverse
)
{
assertEquals
(
0
,
tstz
.
compareTo
(
ts
.
convertTo
(
Value
.
TIMESTAMP_TZ
),
null
));
assertEquals
(
d
.
convertTo
(
Value
.
TIMESTAMP
).
convertTo
(
Value
.
TIMESTAMP_TZ
),
d
.
convertTo
(
Value
.
TIMESTAMP_TZ
));
assertEquals
(
t
.
convertTo
(
Value
.
TIMESTAMP
).
convertTo
(
Value
.
TIMESTAMP_TZ
),
t
.
convertTo
(
Value
.
TIMESTAMP_TZ
));
}
}
private
void
testConversions
()
{
TimeZone
current
=
TimeZone
.
getDefault
();
try
{
for
(
String
id
:
TimeZone
.
getAvailableIDs
())
{
TimeZone
.
setDefault
(
TimeZone
.
getTimeZone
(
id
));
DateTimeUtils
.
resetCalendar
();
testConversionsImpl
(
"2017-12-05 23:59:30.987654321-12:00"
,
true
);
testConversionsImpl
(
"2000-01-02 10:20:30.123456789+07:30"
,
true
);
boolean
testReverse
=
!
"Africa/Monrovia"
.
equals
(
id
);
testConversionsImpl
(
"1960-04-06 12:13:14.777666555+12:00"
,
testReverse
);
}
}
finally
{
TimeZone
.
setDefault
(
current
);
DateTimeUtils
.
resetCalendar
();
}
}
}
h2/src/tools/org/h2/build/doc/dictionary.txt
浏览文件 @
bef4c05e
...
...
@@ -765,4 +765,4 @@ jacoco xdata invokes sourcefiles classfiles duplication crypto stacktraces prt d
interpolated thead
die weekdiff osx subprocess dow proleptic microsecond microseconds divisible cmp denormalized suppressed saturated mcs
london dfs weekdays intermittent looked
london dfs weekdays intermittent looked
msec tstz africa monrovia
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论