提交 a7b804e6 authored 作者: Thomas Mueller's avatar Thomas Mueller

There is a problem when opening a database file in a timezone that has different…

There is a problem when opening a database file in a timezone that has different daylight saving rules.
上级 843796ef
......@@ -18,7 +18,17 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>-
<ul><li>There is a problem when opening a database file in a timezone that has different
daylight saving rules: the time part of dates where the daylight saving doesn't match
will differ. This is not a problem within regions that use the same rules (such as, within
USA, or within Europe), even if the timezone itself is different. As a workaround, export the
database to a SQL script using the old timezone, and create a new database in the new
timezone. There is a new system property "h2.storeLocalTime" that takes daylight time
saving into account when storing and reading, however database files that are created or
modified with this flag set can not be opened with older versions of H2. The new storage
format is already supported for reading however, so that this version is forward compatible.
The same problem occurs when accessing a database over TCP/IP if the client and server
timezones use different rules, if an old server or client is used.
</li></ul>
<h2>Version 1.3.156 (2011-06-17)</h2>
......
......@@ -24,6 +24,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
<h2>Version 1.4.x: Planned Changes</h2>
<ul><li>Build the jar file for Java 6 by default (JDBC API 4.1).
</li><li>Enable the new storage format for dates (system property "h2.storeLocalTime").
</li><li>Remove the old connection pool logic (system property "h2.fastConnectionPool").
</li></ul>
......
......@@ -348,6 +348,13 @@ public class SysProperties {
*/
public static final long SPLIT_FILE_SIZE_SHIFT = Utils.getProperty("h2.splitFileSizeShift", 30);
/**
* System property <code>h2.storeLocalTime</code> (default: false).<br />
* Store the local time in milliseconds since 1970 in the database file. If
* disabled, the daylight saving offset is not taken into account.
*/
public static final boolean STORE_LOCAL_TIME = Utils.getProperty("h2.storeLocalTime", false);
/**
* System property <code>h2.syncMethod</code> (default: sync).<br />
* What method to call when closing the database, on checkpoint, and on
......
......@@ -56,6 +56,11 @@ public class Constants {
*/
public static final int TCP_PROTOCOL_VERSION_8 = 8;
/**
* The TCP protocol version number 9.
*/
public static final int TCP_PROTOCOL_VERSION_9 = 9;
/**
* The major version of this database.
*/
......
......@@ -172,6 +172,7 @@ public class Database implements DataHandler {
private int defaultTableType = Table.TYPE_CACHED;
private final DbSettings dbSettings;
private final int reconnectCheckDelay;
private int logMode;
public Database(ConnectionInfo ci, String cipher) {
String name = ci.getName();
......@@ -205,6 +206,7 @@ public class Database implements DataHandler {
this.mode = Mode.getInstance(modeName);
}
this.multiVersion = ci.getProperty("MVCC", false);
this.logMode = ci.getProperty("LOG", PageStore.LOG_MODE_SYNC);
boolean closeAtVmShutdown = dbSettings.dbCloseOnExit;
int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT,
......@@ -2039,6 +2041,7 @@ public class Database implements DataHandler {
if (!readOnly && fileLockMethod == FileLock.LOCK_FS) {
pageStore.setLockFile(true);
}
pageStore.setLogMode(logMode);
pageStore.open();
}
return pageStore;
......@@ -2267,6 +2270,7 @@ public class Database implements DataHandler {
// disabling a dangerous mode
trace.error(null, "log {0}", log);
}
this.logMode = log;
pageStore.setLogMode(log);
}
}
......
......@@ -94,7 +94,7 @@ public class SessionRemote extends SessionWithState implements DataHandler {
trans.setSSL(ci.isSSL());
trans.init();
trans.writeInt(Constants.TCP_PROTOCOL_VERSION_6);
trans.writeInt(Constants.TCP_PROTOCOL_VERSION_8);
trans.writeInt(Constants.TCP_PROTOCOL_VERSION_9);
trans.writeString(db);
trans.writeString(ci.getOriginalURL());
trans.writeString(ci.getUserName());
......
......@@ -71,12 +71,12 @@ public class TcpServerThread implements Runnable {
int minClientVersion = transfer.readInt();
if (minClientVersion < Constants.TCP_PROTOCOL_VERSION_6) {
throw DbException.get(ErrorCode.DRIVER_VERSION_ERROR_2, "" + clientVersion, "" + Constants.TCP_PROTOCOL_VERSION_6);
} else if (minClientVersion > Constants.TCP_PROTOCOL_VERSION_8) {
} else if (minClientVersion > Constants.TCP_PROTOCOL_VERSION_9) {
throw DbException.get(ErrorCode.DRIVER_VERSION_ERROR_2, "" + clientVersion, "" + Constants.TCP_PROTOCOL_VERSION_8);
}
int maxClientVersion = transfer.readInt();
if (maxClientVersion >= Constants.TCP_PROTOCOL_VERSION_8) {
clientVersion = Constants.TCP_PROTOCOL_VERSION_8;
if (maxClientVersion >= Constants.TCP_PROTOCOL_VERSION_9) {
clientVersion = Constants.TCP_PROTOCOL_VERSION_9;
} else {
clientVersion = minClientVersion;
}
......
......@@ -75,6 +75,9 @@ public class Data {
private static final int LONG_NEG = 67;
private static final int STRING_0_31 = 68;
private static final int BYTES_0_31 = 100;
private static final int LOCAL_TIME = 132;
private static final int LOCAL_DATE = 133;
private static final int LOCAL_TIMESTAMP = 134;
private static final long MILLIS_PER_MINUTE = 1000 * 60;
......@@ -466,19 +469,35 @@ public class Data {
break;
}
case Value.TIME:
writeByte((byte) type);
writeVarLong(DateTimeUtils.getTimeLocal(v.getTimeNoCopy()));
if (SysProperties.STORE_LOCAL_TIME) {
writeByte((byte) LOCAL_TIME);
writeVarLong(DateTimeUtils.getTimeLocal(v.getTimeNoCopy()));
} else {
writeByte((byte) type);
writeVarLong(DateTimeUtils.getTimeLocalWithoutDst(v.getTimeNoCopy()));
}
break;
case Value.DATE: {
writeByte((byte) type);
long x = DateTimeUtils.getTimeLocal(v.getDateNoCopy());
writeVarLong(x / MILLIS_PER_MINUTE);
if (SysProperties.STORE_LOCAL_TIME) {
writeByte((byte) LOCAL_DATE);
long x = DateTimeUtils.getTimeLocal(v.getDateNoCopy());
writeVarLong(x / MILLIS_PER_MINUTE);
} else {
writeByte((byte) type);
long x = DateTimeUtils.getTimeLocalWithoutDst(v.getDateNoCopy());
writeVarLong(x / MILLIS_PER_MINUTE);
}
break;
}
case Value.TIMESTAMP: {
writeByte((byte) type);
Timestamp ts = v.getTimestampNoCopy();
writeVarLong(DateTimeUtils.getTimeLocal(ts));
if (SysProperties.STORE_LOCAL_TIME) {
writeByte((byte) LOCAL_TIMESTAMP);
writeVarLong(DateTimeUtils.getTimeLocal(ts));
} else {
writeByte((byte) type);
writeVarLong(DateTimeUtils.getTimeLocalWithoutDst(ts));
}
writeVarInt(ts.getNanos());
break;
}
......@@ -659,18 +678,30 @@ public class Data {
BigInteger b = new BigInteger(buff);
return ValueDecimal.get(new BigDecimal(b, scale));
}
case Value.DATE: {
case LOCAL_DATE: {
long x = readVarLong() * MILLIS_PER_MINUTE;
return ValueDate.getNoCopy(new Date(DateTimeUtils.getTimeGMT(x)));
}
case Value.TIME:
case Value.DATE: {
long x = readVarLong() * MILLIS_PER_MINUTE;
return ValueDate.getNoCopy(new Date(DateTimeUtils.getTimeGMTWithoutDst(x)));
}
case LOCAL_TIME:
// need to normalize the year, month and day
return ValueTime.get(new Time(DateTimeUtils.getTimeGMT(readVarLong())));
case Value.TIMESTAMP: {
case Value.TIME:
// need to normalize the year, month and day
return ValueTime.get(new Time(DateTimeUtils.getTimeGMTWithoutDst(readVarLong())));
case LOCAL_TIMESTAMP: {
Timestamp ts = new Timestamp(DateTimeUtils.getTimeGMT(readVarLong()));
ts.setNanos(readVarInt());
return ValueTimestamp.getNoCopy(ts);
}
case Value.TIMESTAMP: {
Timestamp ts = new Timestamp(DateTimeUtils.getTimeGMTWithoutDst(readVarLong()));
ts.setNanos(readVarInt());
return ValueTimestamp.getNoCopy(ts);
}
case Value.BYTES: {
int len = readVarInt();
byte[] b = Utils.newBytes(len);
......@@ -861,14 +892,25 @@ public class Data {
return 1 + getVarIntLen(scale) + getVarIntLen(bytes.length) + bytes.length;
}
case Value.TIME:
return 1 + getVarLongLen(DateTimeUtils.getTimeLocal(v.getTimeNoCopy()));
if (SysProperties.STORE_LOCAL_TIME) {
return 1 + getVarLongLen(DateTimeUtils.getTimeLocal(v.getTimeNoCopy()));
}
return 1 + getVarLongLen(DateTimeUtils.getTimeLocalWithoutDst(v.getTimeNoCopy()));
case Value.DATE: {
long x = DateTimeUtils.getTimeLocal(v.getDateNoCopy());
if (SysProperties.STORE_LOCAL_TIME) {
long x = DateTimeUtils.getTimeLocal(v.getDateNoCopy());
return 1 + getVarLongLen(x / MILLIS_PER_MINUTE);
}
long x = DateTimeUtils.getTimeLocalWithoutDst(v.getDateNoCopy());
return 1 + getVarLongLen(x / MILLIS_PER_MINUTE);
}
case Value.TIMESTAMP: {
if (SysProperties.STORE_LOCAL_TIME) {
Timestamp ts = v.getTimestampNoCopy();
return 1 + getVarLongLen(DateTimeUtils.getTimeLocal(ts)) + getVarIntLen(ts.getNanos());
}
Timestamp ts = v.getTimestampNoCopy();
return 1 + getVarLongLen(DateTimeUtils.getTimeLocal(ts)) + getVarIntLen(ts.getNanos());
return 1 + getVarLongLen(DateTimeUtils.getTimeLocalWithoutDst(ts)) + getVarIntLen(ts.getNanos());
}
case Value.JAVA_OBJECT: {
byte[] b = v.getBytesNoCopy();
......
......@@ -230,7 +230,7 @@ public class FileLock implements Runnable {
transfer.setSocket(socket);
transfer.init();
transfer.writeInt(Constants.TCP_PROTOCOL_VERSION_6);
transfer.writeInt(Constants.TCP_PROTOCOL_VERSION_8);
transfer.writeInt(Constants.TCP_PROTOCOL_VERSION_9);
transfer.writeString(null);
transfer.writeString(null);
transfer.writeString(id);
......
......@@ -32,24 +32,30 @@ public class DateTimeUtils {
private static final int DEFAULT_MONTH = 1;
private static final int DEFAULT_DAY = 1;
private static final int DEFAULT_HOUR = 0;
private static final int ZONE_OFFSET = Calendar.getInstance().get(Calendar.ZONE_OFFSET);
private static Calendar cachedCalendar = Calendar.getInstance();
private static int zoneOffset;
private static Calendar cachedCalendar;
private DateTimeUtils() {
// utility class
}
static {
getCalendar();
}
/**
* Reset the calendar, for example after changing the default timezone.
*/
public static void resetCalendar() {
cachedCalendar = null;
getCalendar();
}
private static Calendar getCalendar() {
if (cachedCalendar == null) {
cachedCalendar = Calendar.getInstance();
zoneOffset = cachedCalendar.get(Calendar.ZONE_OFFSET);
}
return cachedCalendar;
}
......@@ -425,7 +431,22 @@ public class DateTimeUtils {
* @return the milliseconds
*/
public static long getTimeLocal(java.util.Date d) {
return d.getTime() + ZONE_OFFSET;
Calendar c = getCalendar();
synchronized (c) {
c.setTime(d);
return c.getTimeInMillis() + c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET);
}
}
/**
* Get the number of milliseconds since 1970-01-01 in the local timezone, but
* without daylight saving time into account.
*
* @param d the date
* @return the milliseconds
*/
public static long getTimeLocalWithoutDst(java.util.Date d) {
return d.getTime() + zoneOffset;
}
/**
......@@ -436,7 +457,23 @@ public class DateTimeUtils {
* @return the number of milliseconds in GMT
*/
public static long getTimeGMT(long millis) {
return millis - ZONE_OFFSET;
Calendar c = getCalendar();
synchronized (c) {
c.setTimeInMillis(millis);
return c.getTime().getTime() - c.get(Calendar.ZONE_OFFSET) - c.get(Calendar.DST_OFFSET);
}
}
/**
* Convert the number of milliseconds since 1970-01-01 in the local timezone
* to GMT, but
* without daylight saving time into account.
*
* @param millis the number of milliseconds in the local timezone
* @return the number of milliseconds in GMT
*/
public static long getTimeGMTWithoutDst(long millis) {
return millis - zoneOffset;
}
/**
......
......@@ -322,23 +322,29 @@ public class Transfer {
writeByte(v.getByte());
break;
case Value.TIME:
if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
writeLong(DateTimeUtils.getTimeLocal(v.getTimeNoCopy()));
} else if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
writeLong(DateTimeUtils.getTimeLocalWithoutDst(v.getTimeNoCopy()));
} else {
writeLong(v.getTimeNoCopy().getTime());
}
break;
case Value.DATE:
if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
writeLong(DateTimeUtils.getTimeLocal(v.getDateNoCopy()));
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
writeLong(DateTimeUtils.getTimeLocal(v.getTimeNoCopy()));
} else if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
writeLong(DateTimeUtils.getTimeLocalWithoutDst(v.getDateNoCopy()));
} else {
writeLong(v.getDateNoCopy().getTime());
}
break;
case Value.TIMESTAMP: {
Timestamp ts = v.getTimestampNoCopy();
if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
writeLong(DateTimeUtils.getTimeLocal(ts));
} else if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
writeLong(DateTimeUtils.getTimeLocalWithoutDst(ts));
} else {
writeLong(ts.getTime());
}
......@@ -462,20 +468,28 @@ public class Transfer {
case Value.BYTE:
return ValueByte.get(readByte());
case Value.DATE:
if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
return ValueDate.getNoCopy(new Date(DateTimeUtils.getTimeGMT(readLong())));
} else if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
return ValueDate.getNoCopy(new Date(DateTimeUtils.getTimeGMTWithoutDst(readLong())));
}
return ValueDate.getNoCopy(new Date(readLong()));
case Value.TIME:
if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
return ValueTime.getNoCopy(new Time(DateTimeUtils.getTimeGMT(readLong())));
} else if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
return ValueTime.getNoCopy(new Time(DateTimeUtils.getTimeGMTWithoutDst(readLong())));
}
return ValueTime.getNoCopy(new Time(readLong()));
case Value.TIMESTAMP: {
if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
Timestamp ts = new Timestamp(DateTimeUtils.getTimeGMT(readLong()));
ts.setNanos(readInt());
return ValueTimestamp.getNoCopy(ts);
} else if (version >= Constants.TCP_PROTOCOL_VERSION_7) {
Timestamp ts = new Timestamp(DateTimeUtils.getTimeGMTWithoutDst(readLong()));
ts.setNanos(readInt());
return ValueTimestamp.getNoCopy(ts);
}
Timestamp ts = new Timestamp(readLong());
ts.setNanos(readInt());
......
......@@ -23,6 +23,7 @@ import org.h2.test.db.TestCheckpoint;
import org.h2.test.db.TestCluster;
import org.h2.test.db.TestCompatibility;
import org.h2.test.db.TestCsv;
import org.h2.test.db.TestDateStorage;
import org.h2.test.db.TestDeadlock;
import org.h2.test.db.TestEncryptedDb;
import org.h2.test.db.TestExclusive;
......@@ -550,6 +551,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestCluster().runTest(this);
new TestCompatibility().runTest(this);
new TestCsv().runTest(this);
new TestDateStorage().runTest(this);
new TestDeadlock().runTest(this);
new TestEncryptedDb().runTest(this);
new TestExclusive().runTest(this);
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.db;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.TimeZone;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.test.TestBase;
import org.h2.test.unit.TestDate;
import org.h2.util.DateTimeUtils;
import org.h2.value.Value;
/**
* Tests the date transfer and storage.
*/
public class TestDateStorage extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
System.setProperty("h2.storeLocalTime", "true");
TestBase.createCaller().init().test();
}
public void test() throws SQLException {
deleteDb("date");
testMoveDatabaseToAnotherTimezone();
testAllTimeZones();
testCurrentTimeZone();
}
private void testMoveDatabaseToAnotherTimezone() throws SQLException {
if (config.memory) {
return;
}
if (!SysProperties.STORE_LOCAL_TIME) {
return;
}
String db = "date;LOG=0;FILE_LOCK=NO";
Connection conn = getConnection(db);
Statement stat;
stat = conn.createStatement();
stat.execute("create table date_list(tz varchar, t varchar, ts timestamp)");
conn.close();
TimeZone defaultTimeZone = TimeZone.getDefault();
ArrayList<TimeZone> distinct = TestDate.getDistinctTimeZones();
try {
for (TimeZone tz : distinct) {
TimeZone.setDefault(tz);
DateTimeUtils.resetCalendar();
conn = getConnection(db);
PreparedStatement prep = conn.prepareStatement("insert into date_list values(?, ?, ?)");
prep.setString(1, tz.getID());
for (int m = 1; m < 10; m++) {
String s = "2000-0" + m + "-01 15:00:00";
prep.setString(2, s);
prep.setTimestamp(3, Timestamp.valueOf(s));
prep.execute();
}
conn.close();
}
printTime("inserted");
for (TimeZone target : distinct) {
if ("Pacific/Kiritimati".equals(target)) {
// there is a problem with this time zone, but it seems
// unrelated to this database (possibly wrong timezone
// information?)
continue;
}
TimeZone.setDefault(target);
DateTimeUtils.resetCalendar();
conn = getConnection(db);
stat = conn.createStatement();
ResultSet rs = stat.executeQuery("select * from date_list order by t");
while (rs.next()) {
String source = rs.getString(1);
String a = rs.getString(2);
String b = rs.getString(3);
b = b.substring(0, a.length());
if (!a.equals(b)) {
assertEquals(source + ">" + target, a, b);
}
}
conn.close();
}
} finally {
TimeZone.setDefault(defaultTimeZone);
DateTimeUtils.resetCalendar();
}
printTime("done");
conn = getConnection(db);
stat = conn.createStatement();
stat.execute("drop table date_list");
conn.close();
}
private static void testCurrentTimeZone() {
for (int year = 1970; year < 2050; year += 3) {
for (int month = 1; month <= 12; month++) {
for (int day = 1; day < 29; day++) {
for (int hour = 0; hour < 24; hour++) {
test(year, month, day, hour);
}
}
}
}
}
private static void test(int year, int month, int day, int hour) {
DateTimeUtils.parseDateTime(year + "-" + month + "-" + day + " " + hour + ":00:00", Value.TIMESTAMP, ErrorCode.TIMESTAMP_CONSTANT_2);
}
private void testAllTimeZones() throws SQLException {
Connection conn = getConnection("date");
TimeZone defaultTimeZone = TimeZone.getDefault();
PreparedStatement prep = conn.prepareStatement("CALL CAST(? AS DATE)");
try {
String[] ids = TimeZone.getAvailableIDs();
for (int i = 0; i < ids.length; i++) {
TimeZone.setDefault(TimeZone.getTimeZone(ids[i]));
DateTimeUtils.resetCalendar();
for (int d = 101; d < 129; d++) {
test(prep, d);
}
}
} finally {
TimeZone.setDefault(defaultTimeZone);
DateTimeUtils.resetCalendar();
}
conn.close();
deleteDb("date");
}
private void test(PreparedStatement prep, int d) throws SQLException {
String s = "2040-10-" + ("" + d).substring(1);
// some dates don't work in some versions of Java
// java.sql.Date date = java.sql.Date.valueOf(s);
// long time = date.getTime();
// int plus = 0;
// while (true) {
// date = new java.sql.Date(time);
// String x = date.toString();
// if (x.equals(s)) {
// break;
// }
// time += 1000;
// plus += 1000;
// }
prep.setString(1, s);
ResultSet rs = prep.executeQuery();
rs.next();
String t = rs.getString(1);
assertEquals(s, t);
}
}
......@@ -6,25 +6,26 @@
*/
package org.h2.test.unit;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.store.Data;
import org.h2.test.TestBase;
import org.h2.util.DateTimeUtils;
import org.h2.util.New;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueNull;
import org.h2.value.ValueString;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
/**
* Tests the data parsing. The problem is that some dates are not allowed
* Tests the date parsing. The problem is that some dates are not allowed
* because of the summer time change. Most countries change at 2 o'clock in the
* morning to 3 o'clock, but some (for example Chile) change at midnight.
* Non-lenient parsing would not work in this case.
......@@ -37,13 +38,89 @@ public class TestDate extends TestBase {
* @param a ignored
*/
public static void main(String... a) throws Exception {
System.setProperty("h2.storeLocalTime", "true");
TestBase.createCaller().init().test();
}
public void test() throws SQLException {
testTimeOperationsAcrossTimeZones();
testDateTimeUtils();
testAllTimeZones();
testCurrentTimeZone();
}
private void testTimeOperationsAcrossTimeZones() {
if (!SysProperties.STORE_LOCAL_TIME) {
return;
}
TimeZone defaultTimeZone = TimeZone.getDefault();
ArrayList<TimeZone> distinct = TestDate.getDistinctTimeZones();
Data d = Data.create(null, 10240);
try {
for (TimeZone tz : distinct) {
TimeZone.setDefault(tz);
DateTimeUtils.resetCalendar();
d.reset();
for (int m = 1; m <= 12; m++) {
for (int h = 0; h <= 23; h++) {
if (h == 0 || h == 2 || h == 3) {
// those hours may not exist for all days in all
// timezones because of daylight saving
continue;
}
String s = "2000-" + (m < 10 ? "0" + m : m) + "-01 " + (h < 10 ? "0" + h : h) + ":00:00.0";
d.writeValue(ValueString.get(s));
d.writeValue(ValueTimestamp.get(Timestamp.valueOf(s)));
}
}
d.writeValue(ValueNull.INSTANCE);
d.reset();
for (TimeZone target : distinct) {
if ("Pacific/Kiritimati".equals(target)) {
// there is a problem with this time zone, but it seems
// unrelated to this database (possibly wrong timezone
// information?)
continue;
}
TimeZone.setDefault(target);
DateTimeUtils.resetCalendar();
while (true) {
Value v = d.readValue();
if (v == ValueNull.INSTANCE) {
break;
}
String a = v.getString();
String b = d.readValue().getString();
if (!a.equals(b)) {
assertEquals("source: " + tz.getID() + " target: " + target.getID(), a, b);
}
}
}
}
} finally {
TimeZone.setDefault(defaultTimeZone);
DateTimeUtils.resetCalendar();
}
}
/**
* Get the list of timezones with distinct rules.
*
* @return the list
*/
public static ArrayList<TimeZone> getDistinctTimeZones() {
ArrayList<TimeZone> distinct = New.arrayList();
for (String id : TimeZone.getAvailableIDs()) {
TimeZone t = TimeZone.getTimeZone(id);
for (TimeZone d : distinct) {
if (t.hasSameRules(d)) {
t = null;
break;
}
}
if (t != null) {
distinct.add(t);
}
}
return distinct;
}
private void testDateTimeUtils() {
......@@ -70,63 +147,4 @@ public class TestDate extends TestBase {
assertEquals("19999-08-07 13:14:15.16", ValueTimestamp.get(ts2a).getString());
}
private static void testCurrentTimeZone() {
for (int year = 1970; year < 2050; year += 3) {
for (int month = 1; month <= 12; month++) {
for (int day = 1; day < 29; day++) {
for (int hour = 0; hour < 24; hour++) {
test(year, month, day, hour);
}
}
}
}
}
private static void test(int year, int month, int day, int hour) {
DateTimeUtils.parseDateTime(year + "-" + month + "-" + day + " " + hour + ":00:00", Value.TIMESTAMP, ErrorCode.TIMESTAMP_CONSTANT_2);
}
private void testAllTimeZones() throws SQLException {
Connection conn = getConnection("date");
TimeZone defaultTimeZone = TimeZone.getDefault();
PreparedStatement prep = conn.prepareStatement("CALL CAST(? AS DATE)");
try {
String[] ids = TimeZone.getAvailableIDs();
for (int i = 0; i < ids.length; i++) {
TimeZone.setDefault(TimeZone.getTimeZone(ids[i]));
DateTimeUtils.resetCalendar();
for (int d = 101; d < 129; d++) {
test(prep, d);
}
}
} finally {
TimeZone.setDefault(defaultTimeZone);
DateTimeUtils.resetCalendar();
}
conn.close();
deleteDb("date");
}
private void test(PreparedStatement prep, int d) throws SQLException {
String s = "2040-10-" + ("" + d).substring(1);
// some dates don't work in some versions of Java
// java.sql.Date date = java.sql.Date.valueOf(s);
// long time = date.getTime();
// int plus = 0;
// while (true) {
// date = new java.sql.Date(time);
// String x = date.toString();
// if (x.equals(s)) {
// break;
// }
// time += 1000;
// plus += 1000;
// }
prep.setString(1, s);
ResultSet rs = prep.executeQuery();
rs.next();
String t = rs.getString(1);
assertEquals(s, t);
}
}
......@@ -684,5 +684,5 @@ diagnostics checkout somewhat icu delegation classifications karlsson applet
litailang springsource eccn springframework spr growth teams gigabytes europe
mcleod decade experience travel willing scjp himself routinely tsi retrieving
multiplied ross judson closeable watcher enqueued referent refs watch tracked
preserving disallowed restrictive
preserving disallowed restrictive dst regions kiritimati
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论