提交 de2dbaa1 authored 作者: Igor Motov's avatar Igor Motov

Use Gregorian calendar regardless of the default locale

Date parsing logic assumes that the calendar type is Gregorian, so it should use the Gregorian calendar even if the default locale says otherwise.

Fixes #675
上级 326e5833
......@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #675: Fix date operations on Locales with non-Gregorian calendars
</li>
<li>Fix removal of LOB when rolling back transaction on a table containing more than one LOB column.
</li>
<li>Issue #654: List ENUM type values in INFORMATION_SCHEMA.COLUMNS
......
......@@ -1252,7 +1252,7 @@ public class Function extends Expression implements FunctionCall {
case TRUNCATE: {
if (v0.getType() == Value.TIMESTAMP) {
java.sql.Timestamp d = v0.getTimestamp();
Calendar c = Calendar.getInstance();
Calendar c = DateTimeUtils.createCalendar();
c.setTime(d);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
......@@ -1261,7 +1261,7 @@ public class Function extends Expression implements FunctionCall {
result = ValueTimestamp.fromMillis(c.getTimeInMillis());
} else if (v0.getType() == Value.DATE) {
ValueDate vd = (ValueDate) v0;
Calendar c = Calendar.getInstance();
Calendar c = DateTimeUtils.createCalendar();
c.setTime(vd.getDate());
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
......@@ -1270,7 +1270,7 @@ public class Function extends Expression implements FunctionCall {
result = ValueTimestamp.fromMillis(c.getTimeInMillis());
} else if (v0.getType() == Value.STRING) {
ValueString vd = (ValueString) v0;
Calendar c = Calendar.getInstance();
Calendar c = DateTimeUtils.createCalendar();
c.setTime(ValueTimestamp.parse(vd.getString(), session.getDatabase().getMode()).getDate());
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
......@@ -1822,7 +1822,7 @@ public class Function extends Expression implements FunctionCall {
if (count > Integer.MAX_VALUE) {
throw DbException.getInvalidValueException("DATEADD count", count);
}
Calendar calendar = Calendar.getInstance();
Calendar calendar = DateTimeUtils.createCalendar();
int nanos = d.getNanos() % 1000000;
calendar.setTime(d);
calendar.add(field, (int) count);
......@@ -1846,7 +1846,7 @@ public class Function extends Expression implements FunctionCall {
*/
private static long datediff(String part, Timestamp d1, Timestamp d2) {
int field = getDatePart(part);
Calendar calendar = Calendar.getInstance();
Calendar calendar = DateTimeUtils.createCalendar();
long t1 = d1.getTime(), t2 = d2.getTime();
// need to convert to UTC, otherwise we get inconsistent results with
// certain time zones (those that are 30 minutes off)
......@@ -1896,7 +1896,7 @@ public class Function extends Expression implements FunctionCall {
default:
break;
}
calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar = DateTimeUtils.createCalendar(TimeZone.getTimeZone("UTC"));
calendar.setTimeInMillis(t1);
int year1 = calendar.get(Calendar.YEAR);
int month1 = calendar.get(Calendar.MONTH);
......
......@@ -3799,7 +3799,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS
} else if (type == java.util.Date.class) {
return type.cast(new java.util.Date(value.getTimestamp().getTime()));
} else if (type == Calendar.class) {
Calendar calendar = Calendar.getInstance();
Calendar calendar = DateTimeUtils.createCalendar();
calendar.setTime(value.getTimestamp());
return type.cast(calendar);
} else if (type == UUID.class) {
......
......@@ -73,7 +73,7 @@ public class DateTimeUtils {
* use a fixed value throughout the duration of the JVM's life, rather than
* have this offset change, possibly midway through a long-running query.
*/
private static int zoneOffsetMillis = Calendar.getInstance()
private static int zoneOffsetMillis = DateTimeUtils.createCalendar()
.get(Calendar.ZONE_OFFSET);
private DateTimeUtils() {
......@@ -86,7 +86,7 @@ public class DateTimeUtils {
*/
public static void resetCalendar() {
CACHED_CALENDAR.remove();
zoneOffsetMillis = Calendar.getInstance().get(Calendar.ZONE_OFFSET);
zoneOffsetMillis = DateTimeUtils.createCalendar().get(Calendar.ZONE_OFFSET);
}
/**
......@@ -97,7 +97,7 @@ public class DateTimeUtils {
private static Calendar getCalendar() {
Calendar c = CACHED_CALENDAR.get();
if (c == null) {
c = Calendar.getInstance();
c = DateTimeUtils.createCalendar();
CACHED_CALENDAR.set(c);
}
c.clear();
......@@ -113,13 +113,32 @@ public class DateTimeUtils {
private static Calendar getCalendar(TimeZone tz) {
Calendar c = CACHED_CALENDAR_NON_DEFAULT_TIMEZONE.get();
if (c == null || !c.getTimeZone().equals(tz)) {
c = Calendar.getInstance(tz);
c = DateTimeUtils.createCalendar(tz);
CACHED_CALENDAR_NON_DEFAULT_TIMEZONE.set(c);
}
c.clear();
return c;
}
/**
* Creates a calendar for the default timezone.
*
* @return a calendar instance. A cached instance is returned where possible
*/
public static Calendar createCalendar() {
return new GregorianCalendar();
}
/**
* Creates a calendar for the given timezone.
*
* @param tz timezone for the calendar, is never null
* @return a calendar instance. A cached instance is returned where possible
*/
public static Calendar createCalendar(TimeZone tz) {
return new GregorianCalendar(tz);
}
/**
* Convert the date to the specified time zone.
*
......@@ -253,11 +272,9 @@ public class DateTimeUtils {
throw DbException.getInvalidValueException("calendar", null);
}
target = (Calendar) target.clone();
Calendar local = Calendar.getInstance();
synchronized (local) {
local.setTime(x);
convertTime(local, target);
}
Calendar local = DateTimeUtils.createCalendar();
local.setTime(x);
convertTime(local, target);
return target.getTime().getTime();
}
......@@ -531,7 +548,7 @@ public class DateTimeUtils {
* @return the day of the week, Monday as 1 to Sunday as 7
*/
public static int getIsoDayOfWeek(java.util.Date date) {
Calendar cal = Calendar.getInstance();
Calendar cal = DateTimeUtils.createCalendar();
cal.setTimeInMillis(date.getTime());
int val = cal.get(Calendar.DAY_OF_WEEK) - 1;
return val == 0 ? 7 : val;
......@@ -552,7 +569,7 @@ public class DateTimeUtils {
* @return the week of the year
*/
public static int getIsoWeek(java.util.Date date) {
Calendar c = Calendar.getInstance();
Calendar c = DateTimeUtils.createCalendar();
c.setTimeInMillis(date.getTime());
c.setFirstDayOfWeek(Calendar.MONDAY);
c.setMinimalDaysInFirstWeek(4);
......@@ -567,7 +584,7 @@ public class DateTimeUtils {
* @return the year
*/
public static int getIsoYear(java.util.Date date) {
Calendar cal = Calendar.getInstance();
Calendar cal = DateTimeUtils.createCalendar();
cal.setTimeInMillis(date.getTime());
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(4);
......@@ -942,7 +959,7 @@ public class DateTimeUtils {
* @return the new timestamp
*/
public static Timestamp addMonths(Timestamp refDate, int nrOfMonthsToAdd) {
Calendar calendar = Calendar.getInstance();
Calendar calendar = DateTimeUtils.createCalendar();
calendar.setTime(refDate);
calendar.add(Calendar.MONTH, nrOfMonthsToAdd);
......
......@@ -21,7 +21,7 @@ public class ToDateParser {
private final ConfigParam functionName;
private String inputStr;
private String formatStr;
private final Calendar resultCalendar = (Calendar) Calendar.getInstance().clone();
private final Calendar resultCalendar = DateTimeUtils.createCalendar();
private Integer nanos;
/**
......@@ -33,7 +33,7 @@ public class ToDateParser {
private ToDateParser(ConfigParam functionName, String input, String format) {
// reset calendar - default oracle behaviour
resultCalendar.set(Calendar.YEAR, 1970);
resultCalendar.set(Calendar.MONTH, Calendar.getInstance().get(Calendar.MONTH));
resultCalendar.set(Calendar.MONTH, DateTimeUtils.createCalendar().get(Calendar.MONTH));
resultCalendar.clear(Calendar.DAY_OF_YEAR);
resultCalendar.clear(Calendar.DAY_OF_WEEK);
resultCalendar.clear(Calendar.DAY_OF_WEEK_IN_MONTH);
......
......@@ -194,7 +194,7 @@ class ToDateTokenizer {
result.set(Calendar.YEAR, dateNr);
break;
case RR:
Calendar calendar = Calendar.getInstance();
Calendar calendar = DateTimeUtils.createCalendar();
int cc = calendar.get(Calendar.YEAR) / 100;
inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS,
params, formatTokenEnum);
......
......@@ -217,7 +217,7 @@ public class ValueTimestamp extends Value {
tz, year, month, day, hour, minute, (int) second, (int) ms);
ms = DateTimeUtils.convertToLocal(
new Date(millis),
Calendar.getInstance(TimeZone.getTimeZone("UTC")));
DateTimeUtils.createCalendar(TimeZone.getTimeZone("UTC")));
long md = DateTimeUtils.MILLIS_PER_DAY;
long absoluteDay = (ms >= 0 ? ms : ms - md + 1) / md;
dateValue = DateTimeUtils.dateValueFromAbsoluteDay(absoluteDay);
......
......@@ -1199,7 +1199,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
"SELECT CURRENT_TIMESTAMP(), " +
"TRUNCATE(CURRENT_TIMESTAMP()) FROM dual");
rs.next();
Calendar c = Calendar.getInstance();
Calendar c = DateTimeUtils.createCalendar();
c.setTime(rs.getTimestamp(1));
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
......@@ -1305,7 +1305,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
}
private void testToDate() throws ParseException {
final int month = Calendar.getInstance().get(Calendar.MONTH);
final int month = DateTimeUtils.createCalendar().get(Calendar.MONTH);
Date date = null;
date = new SimpleDateFormat("yyyy-MM-dd").parse("1979-11-12");
......@@ -1434,7 +1434,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
}
private static void setMonth(Date date, int month) {
Calendar c = Calendar.getInstance();
Calendar c = DateTimeUtils.createCalendar();
c.setTime(date);
c.set(Calendar.MONTH, month);
date.setTime(c.getTimeInMillis());
......
......@@ -37,6 +37,7 @@ import java.util.TimeZone;
import org.h2.api.ErrorCode;
import org.h2.test.TestBase;
import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils;
import org.h2.util.LocalDateTimeUtils;
......@@ -1408,7 +1409,7 @@ public class TestResultSet extends TestBase {
"D DATE, T TIME, TS TIMESTAMP)");
PreparedStatement prep = conn.prepareStatement(
"INSERT INTO TEST VALUES(?, ?, ?, ?)");
Calendar regular = Calendar.getInstance();
Calendar regular = DateTimeUtils.createCalendar();
Calendar other = null;
// search a locale that has a _different_ raw offset
long testTime = java.sql.Date.valueOf("2001-02-03").getTime();
......@@ -1421,7 +1422,7 @@ public class TestResultSet extends TestBase {
if (rawOffsetDiff != 0 && rawOffsetDiff != 1000 * 60 * 60 * 24) {
if (regular.getTimeZone().getOffset(testTime) !=
zone.getOffset(testTime)) {
other = Calendar.getInstance(zone);
other = DateTimeUtils.createCalendar(zone);
break;
}
}
......
......@@ -42,6 +42,7 @@ import org.h2.test.synth.sql.RandomGen;
import org.h2.tools.Backup;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Restore;
import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.New;
......@@ -485,7 +486,7 @@ public class TestCrashAPI extends TestBase implements Runnable {
// TODO should use generated savepoints
return null;
} else if (type == Calendar.class) {
return Calendar.getInstance();
return DateTimeUtils.createCalendar();
} else if (type == java.net.URL.class) {
return null;
} else if (type == java.math.BigDecimal.class) {
......
......@@ -352,7 +352,7 @@ public class TestDate extends TestBase {
}
private void testValidDate() {
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Calendar c = DateTimeUtils.createCalendar(TimeZone.getTimeZone("UTC"));
c.setLenient(false);
for (int y = -2000; y < 3000; y++) {
for (int m = -3; m <= 14; m++) {
......@@ -467,9 +467,9 @@ public class TestDate extends TestBase {
assertEquals("19999-08-07", d2.getString());
assertEquals("13:14:15.16", t2.getString());
ValueTimestamp ts1a = DateTimeUtils.convertTimestamp(
ts1.getTimestamp(), Calendar.getInstance());
ts1.getTimestamp(), DateTimeUtils.createCalendar());
ValueTimestamp ts2a = DateTimeUtils.convertTimestamp(
ts2.getTimestamp(), Calendar.getInstance());
ts2.getTimestamp(), DateTimeUtils.createCalendar());
assertEquals("-999-08-07 13:14:15.16", ts1a.getString());
assertEquals("19999-08-07 13:14:15.16", ts2a.getString());
......
......@@ -29,6 +29,7 @@ public class TestLocale extends TestBase {
@Override
public void test() throws SQLException {
testSpecialLocale();
testDatesInJapanLocale();
}
private void testSpecialLocale() throws SQLException {
......@@ -55,4 +56,32 @@ public class TestLocale extends TestBase {
conn.close();
}
private void testDatesInJapanLocale() throws SQLException {
deleteDb(getTestName());
Connection conn = getConnection(getTestName());
Statement stat = conn.createStatement();
Locale old = Locale.getDefault();
try {
// when using Japanese as the default locale, the default calendar is
// the imperial japanese calendar
Locale.setDefault(new Locale("ja", "JP", "JP"));
stat.execute("CREATE TABLE test(d TIMESTAMP, dz TIMESTAMP WITH TIME ZONE) " +
"as select '2017-12-03T00:00:00Z', '2017-12-03T00:00:00Z'");
ResultSet rs = stat.executeQuery("select YEAR(d) y, YEAR(dz) yz from test");
rs.next();
assertEquals(2017, rs.getInt("y"));
assertEquals(2017, rs.getInt("yz"));
stat.execute("drop table test");
rs = stat.executeQuery(
"CALL FORMATDATETIME(TIMESTAMP '2001-02-03 04:05:06', 'yyyy-MM-dd HH:mm:ss', 'en')");
rs.next();
assertEquals("2001-02-03 04:05:06", rs.getString(1));
} finally {
Locale.setDefault(old);
}
conn.close();
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论