提交 f6e531cd authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Reimplement dateValueFromDate() and nanosFromDate() without a Calendar

上级 b3f66155
...@@ -85,6 +85,11 @@ public class DateTimeUtils { ...@@ -85,6 +85,11 @@ public class DateTimeUtils {
private static final ThreadLocal<GregorianCalendar> CACHED_CALENDAR_NON_DEFAULT_TIMEZONE = private static final ThreadLocal<GregorianCalendar> CACHED_CALENDAR_NON_DEFAULT_TIMEZONE =
new ThreadLocal<>(); new ThreadLocal<>();
/**
* Cached local time zone.
*/
private static volatile TimeZone timeZone;
/** /**
* Observed JVM behaviour is that if the timezone of the host computer is * 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 * changed while the JVM is running, the zone offset does not change but
...@@ -100,12 +105,26 @@ public class DateTimeUtils { ...@@ -100,12 +105,26 @@ public class DateTimeUtils {
// utility class // 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 * Reset the cached calendar for default timezone, for example after
* changing the default timezone. * changing the default timezone.
*/ */
public static void resetCalendar() { public static void resetCalendar() {
CACHED_CALENDAR.remove(); CACHED_CALENDAR.remove();
timeZone = null;
zoneOffsetMillis = DateTimeUtils.createGregorianCalendar().get(Calendar.ZONE_OFFSET); zoneOffsetMillis = DateTimeUtils.createGregorianCalendar().get(Calendar.ZONE_OFFSET);
} }
...@@ -1043,9 +1062,12 @@ public class DateTimeUtils { ...@@ -1043,9 +1062,12 @@ public class DateTimeUtils {
* @return the date value * @return the date value
*/ */
public static long dateValueFromDate(long ms) { public static long dateValueFromDate(long ms) {
Calendar cal = getCalendar(); ms += getTimeZone().getOffset(ms);
cal.setTimeInMillis(ms); long absoluteDay = ms / 86_400_000;
return dateValueFromCalendar(cal); if (ms < 0 && (absoluteDay * 86_400_000 != ms)) {
absoluteDay--;
}
return dateValueFromAbsoluteDay(absoluteDay);
} }
/** /**
...@@ -1072,9 +1094,12 @@ public class DateTimeUtils { ...@@ -1072,9 +1094,12 @@ public class DateTimeUtils {
* @return the nanoseconds * @return the nanoseconds
*/ */
public static long nanosFromDate(long ms) { public static long nanosFromDate(long ms) {
Calendar cal = getCalendar(); ms += getTimeZone().getOffset(ms);
cal.setTimeInMillis(ms); long absoluteDay = ms / 86_400_000;
return nanosFromCalendar(cal); if (ms < 0 && (absoluteDay * 86_400_000 != ms)) {
absoluteDay--;
}
return (ms - absoluteDay * 86_400_000) * 1_000_000;
} }
/** /**
......
...@@ -6,10 +6,15 @@ ...@@ -6,10 +6,15 @@
package org.h2.test.unit; package org.h2.test.unit;
import static org.h2.util.DateTimeUtils.dateValue; import static org.h2.util.DateTimeUtils.dateValue;
import java.sql.Timestamp;
import java.util.Calendar; import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.value.ValueTimestamp;
/** /**
* Unit tests for the DateTimeUtils class * Unit tests for the DateTimeUtils class
...@@ -22,6 +27,12 @@ public class TestDateTimeUtils extends TestBase { ...@@ -22,6 +27,12 @@ public class TestDateTimeUtils extends TestBase {
* @param a ignored * @param a ignored
*/ */
public static void main(String... a) throws Exception { 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(); TestBase.createCaller().init().test();
} }
...@@ -31,6 +42,7 @@ public class TestDateTimeUtils extends TestBase { ...@@ -31,6 +42,7 @@ public class TestDateTimeUtils extends TestBase {
testDayOfWeek(); testDayOfWeek();
testWeekOfYear(); testWeekOfYear();
testDateValueFromDenormalizedDate(); testDateValueFromDenormalizedDate();
testUTC2Value(false);
} }
private void testParseTimeNanosDB2Format() { private void testParseTimeNanosDB2Format() {
...@@ -104,4 +116,45 @@ public class TestDateTimeUtils extends TestBase { ...@@ -104,4 +116,45 @@ public class TestDateTimeUtils extends TestBase {
assertEquals(dateValue(-100, 2, 29), DateTimeUtils.dateValueFromDenormalizedDate(-100, 2, 30)); 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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论