Unverified 提交 8556fd23 authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #881 from katzyn/datetime

Reimplement dateValueFromDate() and nanosFromDate() without a Calendar
......@@ -85,6 +85,11 @@ public class DateTimeUtils {
private static final ThreadLocal<GregorianCalendar> CACHED_CALENDAR_NON_DEFAULT_TIMEZONE =
new ThreadLocal<>();
/**
* Cached local time zone.
*/
private static volatile TimeZone timeZone;
/**
* Observed JVM behaviour is that if the timezone of the host computer is
* changed while the JVM is running, the zone offset does not change but
......@@ -100,12 +105,26 @@ public class DateTimeUtils {
// utility class
}
/**
* Returns local time zone.
*
* @return local time zone
*/
private static TimeZone getTimeZone() {
TimeZone tz = timeZone;
if (tz == null) {
timeZone = tz = TimeZone.getDefault();
}
return tz;
}
/**
* Reset the cached calendar for default timezone, for example after
* changing the default timezone.
*/
public static void resetCalendar() {
CACHED_CALENDAR.remove();
timeZone = null;
zoneOffsetMillis = DateTimeUtils.createGregorianCalendar().get(Calendar.ZONE_OFFSET);
}
......@@ -1043,9 +1062,13 @@ public class DateTimeUtils {
* @return the date value
*/
public static long dateValueFromDate(long ms) {
Calendar cal = getCalendar();
cal.setTimeInMillis(ms);
return dateValueFromCalendar(cal);
ms += getTimeZone().getOffset(ms);
long absoluteDay = ms / MILLIS_PER_DAY;
// Round toward negative infinity
if (ms < 0 && (absoluteDay * MILLIS_PER_DAY != ms)) {
absoluteDay--;
}
return dateValueFromAbsoluteDay(absoluteDay);
}
/**
......@@ -1072,9 +1095,13 @@ public class DateTimeUtils {
* @return the nanoseconds
*/
public static long nanosFromDate(long ms) {
Calendar cal = getCalendar();
cal.setTimeInMillis(ms);
return nanosFromCalendar(cal);
ms += getTimeZone().getOffset(ms);
long absoluteDay = ms / MILLIS_PER_DAY;
// Round toward negative infinity
if (ms < 0 && (absoluteDay * MILLIS_PER_DAY != ms)) {
absoluteDay--;
}
return (ms - absoluteDay * MILLIS_PER_DAY) * 1_000_000;
}
/**
......@@ -1129,7 +1156,7 @@ public class DateTimeUtils {
m += 12;
}
long a = ((y * 2922L) >> 3) + DAYS_OFFSET[m - 3] + d - 719484;
if (y <= 1582 && ((y < 1582) || (m * 100 + d < 1005))) {
if (y <= 1582 && ((y < 1582) || (m * 100 + d < 1015))) {
// Julian calendar (cutover at 1582-10-04 / 1582-10-15)
a += 13;
} else if (y < 1901 || y > 2099) {
......
......@@ -37,6 +37,7 @@ public class TestClearReferences extends TestBase {
"org.h2.tools.CompressTool.cachedBuffer",
"org.h2.util.CloseWatcher.queue",
"org.h2.util.CloseWatcher.refs",
"org.h2.util.DateTimeUtils.timeZone",
"org.h2.util.MathUtils.cachedSecureRandom",
"org.h2.util.NetUtils.cachedLocalAddress",
"org.h2.util.StringUtils.softCache",
......
......@@ -6,10 +6,15 @@
package org.h2.test.unit;
import static org.h2.util.DateTimeUtils.dateValue;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.h2.test.TestBase;
import org.h2.util.DateTimeUtils;
import org.h2.value.ValueTimestamp;
/**
* Unit tests for the DateTimeUtils class
......@@ -19,9 +24,18 @@ public class TestDateTimeUtils extends TestBase {
/**
* Run just this test.
*
* @param a ignored
* @param a
* if {@code "testUtc2Value"} only {@link #testUTC2Value(boolean)}
* will be executed with all time zones (slow). Otherwise all tests
* in this test unit will be executed with local time zone.
*/
public static void main(String... a) throws Exception {
if (a.length == 1) {
if ("testUtc2Value".equals(a[0])) {
new TestDateTimeUtils().testUTC2Value(true);
return;
}
}
TestBase.createCaller().init().test();
}
......@@ -31,6 +45,7 @@ public class TestDateTimeUtils extends TestBase {
testDayOfWeek();
testWeekOfYear();
testDateValueFromDenormalizedDate();
testUTC2Value(false);
}
private void testParseTimeNanosDB2Format() {
......@@ -104,4 +119,45 @@ public class TestDateTimeUtils extends TestBase {
assertEquals(dateValue(-100, 2, 29), DateTimeUtils.dateValueFromDenormalizedDate(-100, 2, 30));
}
private void testUTC2Value(boolean allTimeZones) {
TimeZone def = TimeZone.getDefault();
GregorianCalendar gc = new GregorianCalendar();
if (allTimeZones) {
try {
for (String id : TimeZone.getAvailableIDs()) {
System.out.println(id);
TimeZone tz = TimeZone.getTimeZone(id);
TimeZone.setDefault(tz);
DateTimeUtils.resetCalendar();
testUTC2ValueImpl(tz, gc);
}
} finally {
TimeZone.setDefault(def);
DateTimeUtils.resetCalendar();
}
} else {
testUTC2ValueImpl(def, gc);
}
}
private void testUTC2ValueImpl(TimeZone tz, GregorianCalendar gc) {
gc.setTimeZone(tz);
gc.set(Calendar.MILLISECOND, 0);
long absoluteStart = DateTimeUtils.absoluteDayFromDateValue(DateTimeUtils.dateValue(1950, 01, 01));
long absoluteEnd = DateTimeUtils.absoluteDayFromDateValue(DateTimeUtils.dateValue(2050, 01, 01));
for (long i = absoluteStart; i < absoluteEnd; i++) {
long dateValue = DateTimeUtils.dateValueFromAbsoluteDay(i);
int year = DateTimeUtils.yearFromDateValue(dateValue);
int month = DateTimeUtils.monthFromDateValue(dateValue);
int day = DateTimeUtils.dayFromDateValue(dateValue);
for (int j = 0; j < 48; j++) {
gc.set(year, month - 1, day, j / 2, (j & 1) * 30, 0);
long timeMillis = gc.getTimeInMillis();
ValueTimestamp ts = DateTimeUtils.convertTimestamp(new Timestamp(timeMillis), gc);
assertEquals(ts.getDateValue(), DateTimeUtils.dateValueFromDate(timeMillis));
assertEquals(ts.getTimeNanos(), DateTimeUtils.nanosFromDate(timeMillis));
}
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论