mirror of
https://github.com/microsoft/HybridRow.git
synced 2026-01-28 22:03:17 +00:00
Added and debugged some tests
This commit is contained in:
@@ -14,6 +14,30 @@ import java.time.ZoneOffset;
|
|||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides static methods for encoding and decoding {@link OffsetDateTime}s serialized as {@code System.DateTime}s
|
||||||
|
*
|
||||||
|
* {@link OffsetDateTime} values are serialized as unsigned 64-bit integers:
|
||||||
|
*
|
||||||
|
* <table>
|
||||||
|
* <tbody>
|
||||||
|
* <tr><td>
|
||||||
|
* Bits 01-62
|
||||||
|
* </td><td>
|
||||||
|
* Contain the number of 100-nanosecond ticks where 0 represents {@code 1/1/0001 12:00am}, up until the value
|
||||||
|
* {@code 12/31/9999 23:59:59.9999999}.
|
||||||
|
* </td></tr>
|
||||||
|
* <tr><td>
|
||||||
|
* Bits 63-64
|
||||||
|
* </td><td>
|
||||||
|
* Contain a four-state value that describes the {@code System.DateTimeKind} value of the date time, with a
|
||||||
|
* 2nd value for the rare case where the date time is local, but is in an overlapped daylight savings time
|
||||||
|
* hour and it is in daylight savings time. This allows distinction of these otherwise ambiguous local times
|
||||||
|
* and prevents data loss when round tripping from Local to UTC time.
|
||||||
|
* </td></tr>
|
||||||
|
* </tbody>
|
||||||
|
* </table>
|
||||||
|
*/
|
||||||
public final class DateTimeCodec {
|
public final class DateTimeCodec {
|
||||||
|
|
||||||
public static final int BYTES = Long.BYTES;
|
public static final int BYTES = Long.BYTES;
|
||||||
@@ -24,6 +48,8 @@ public final class DateTimeCodec {
|
|||||||
private static final long KIND_UTC = 0x4000000000000000L;
|
private static final long KIND_UTC = 0x4000000000000000L;
|
||||||
private static final long TICKS_MASK = 0x3FFFFFFFFFFFFFFFL;
|
private static final long TICKS_MASK = 0x3FFFFFFFFFFFFFFFL;
|
||||||
|
|
||||||
|
private static final long UNIX_EPOCH_TICKS = 0x89F7FF5F7B58000L;
|
||||||
|
|
||||||
private static final ZoneOffset ZONE_OFFSET_LOCAL = OffsetDateTime.now().getOffset();
|
private static final ZoneOffset ZONE_OFFSET_LOCAL = OffsetDateTime.now().getOffset();
|
||||||
private static final int ZONE_OFFSET_LOCAL_TOTAL_SECONDS = ZONE_OFFSET_LOCAL.getTotalSeconds();
|
private static final int ZONE_OFFSET_LOCAL_TOTAL_SECONDS = ZONE_OFFSET_LOCAL.getTotalSeconds();
|
||||||
private static final int ZONE_OFFSET_UTC_TOTAL_SECONDS = ZoneOffset.UTC.getTotalSeconds();
|
private static final int ZONE_OFFSET_UTC_TOTAL_SECONDS = ZoneOffset.UTC.getTotalSeconds();
|
||||||
@@ -61,10 +87,13 @@ public final class DateTimeCodec {
|
|||||||
in.readableBytes());
|
in.readableBytes());
|
||||||
|
|
||||||
final long data = in.readLongLE();
|
final long data = in.readLongLE();
|
||||||
final long epochSecond = data & TICKS_MASK;
|
final long ticks = data & TICKS_MASK;
|
||||||
final ZoneOffset zoneOffset = (data & FLAGS_MASK) == KIND_UTC ? ZoneOffset.UTC : ZONE_OFFSET_LOCAL;
|
final ZoneOffset zoneOffset = (data & FLAGS_MASK) == KIND_UTC ? ZoneOffset.UTC : ZONE_OFFSET_LOCAL;
|
||||||
|
|
||||||
return OffsetDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), zoneOffset);
|
final long epochSecond = ((ticks - UNIX_EPOCH_TICKS) / 10_000_000L) - zoneOffset.getTotalSeconds();
|
||||||
|
final int nanos = (int) (100L * (ticks % 10_000_000L));
|
||||||
|
|
||||||
|
return OffsetDateTime.ofInstant(Instant.ofEpochSecond(epochSecond, nanos), zoneOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +106,7 @@ public final class DateTimeCodec {
|
|||||||
*/
|
*/
|
||||||
public static byte[] encode(final OffsetDateTime offsetDateTime) {
|
public static byte[] encode(final OffsetDateTime offsetDateTime) {
|
||||||
final byte[] bytes = new byte[BYTES];
|
final byte[] bytes = new byte[BYTES];
|
||||||
encode(offsetDateTime, Unpooled.wrappedBuffer(bytes));
|
encode(offsetDateTime, Unpooled.wrappedBuffer(bytes).clear());
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,21 +120,25 @@ public final class DateTimeCodec {
|
|||||||
*/
|
*/
|
||||||
public static void encode(final OffsetDateTime offsetDateTime, final ByteBuf out) {
|
public static void encode(final OffsetDateTime offsetDateTime, final ByteBuf out) {
|
||||||
|
|
||||||
final long epochSecond = offsetDateTime.toEpochSecond();
|
final ZoneOffset offset = offsetDateTime.getOffset();
|
||||||
|
final Instant instant = offsetDateTime.toInstant();
|
||||||
|
|
||||||
checkArgument(epochSecond <= TICKS_MASK, "expected offsetDateTime epoch second in range [0, %s], not %s",
|
final long ticks = UNIX_EPOCH_TICKS + 10_000_000L * (instant.getEpochSecond() + offset.getTotalSeconds())
|
||||||
|
+ instant.getNano() / 100L;
|
||||||
|
|
||||||
|
checkArgument(ticks <= TICKS_MASK, "expected offsetDateTime epoch second in range [0, %s], not %s",
|
||||||
TICKS_MASK,
|
TICKS_MASK,
|
||||||
epochSecond);
|
ticks);
|
||||||
|
|
||||||
final int zoneOffsetTotalSeconds = offsetDateTime.getOffset().getTotalSeconds();
|
final int zoneOffsetTotalSeconds = offsetDateTime.getOffset().getTotalSeconds();
|
||||||
final long value;
|
final long value;
|
||||||
|
|
||||||
if (zoneOffsetTotalSeconds == ZONE_OFFSET_UTC_TOTAL_SECONDS) {
|
if (zoneOffsetTotalSeconds == ZONE_OFFSET_UTC_TOTAL_SECONDS) {
|
||||||
value = epochSecond | KIND_UTC;
|
value = ticks | KIND_UTC;
|
||||||
} else if (zoneOffsetTotalSeconds == ZONE_OFFSET_LOCAL_TOTAL_SECONDS) {
|
} else if (zoneOffsetTotalSeconds == ZONE_OFFSET_LOCAL_TOTAL_SECONDS) {
|
||||||
value = epochSecond | KIND_LOCAL;
|
value = ticks | KIND_LOCAL;
|
||||||
} else {
|
} else {
|
||||||
value = epochSecond | KIND_AMBIGUOUS;
|
value = ticks | KIND_AMBIGUOUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.writeLongLE(value);
|
out.writeLongLE(value);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class DateTimeCodecTest {
|
|||||||
|
|
||||||
@Test(dataProvider = "dateTimeDataProvider")
|
@Test(dataProvider = "dateTimeDataProvider")
|
||||||
public void testDecodeByteBuf(byte[] buffer, OffsetDateTime value) {
|
public void testDecodeByteBuf(byte[] buffer, OffsetDateTime value) {
|
||||||
ByteBuf byteBuf = Unpooled.wrappedBuffer(new byte[DateTimeCodec.BYTES]);
|
ByteBuf byteBuf = Unpooled.wrappedBuffer(buffer);
|
||||||
OffsetDateTime actual = DateTimeCodec.decode(byteBuf);
|
OffsetDateTime actual = DateTimeCodec.decode(byteBuf);
|
||||||
assertEquals(actual, value);
|
assertEquals(actual, value);
|
||||||
}
|
}
|
||||||
@@ -48,13 +48,14 @@ public class DateTimeCodecTest {
|
|||||||
|
|
||||||
@Test(dataProvider = "dateTimeDataProvider")
|
@Test(dataProvider = "dateTimeDataProvider")
|
||||||
public void testEncodeByteBuf(byte[] buffer, OffsetDateTime value) {
|
public void testEncodeByteBuf(byte[] buffer, OffsetDateTime value) {
|
||||||
ByteBuf actual = Unpooled.wrappedBuffer(new byte[DateTimeCodec.BYTES]);
|
ByteBuf actual = Unpooled.wrappedBuffer(new byte[DateTimeCodec.BYTES]).clear();
|
||||||
DateTimeCodec.encode(value, actual);
|
DateTimeCodec.encode(value, actual);
|
||||||
assertEquals(actual.array(), buffer);
|
assertEquals(actual.array(), buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DataProvider(name = "dateTimeDataProvider")
|
@DataProvider(name = "dateTimeDataProvider")
|
||||||
private Iterator<Object[]> dateTimeData(byte[] buffer, OffsetDateTime value) {
|
private static Iterator<Object[]> dateTimeData() {
|
||||||
|
|
||||||
ImmutableList<DateTimeItem> items = ImmutableList.of(
|
ImmutableList<DateTimeItem> items = ImmutableList.of(
|
||||||
new DateTimeItem(new byte[] {
|
new DateTimeItem(new byte[] {
|
||||||
(byte) 120, (byte) 212, (byte) 106, (byte) 251, (byte) 105, (byte) 48, (byte) 215, (byte) 136 },
|
(byte) 120, (byte) 212, (byte) 106, (byte) 251, (byte) 105, (byte) 48, (byte) 215, (byte) 136 },
|
||||||
@@ -63,6 +64,7 @@ public class DateTimeCodecTest {
|
|||||||
(byte) 226, (byte) 108, (byte) 87, (byte) 194, (byte) 164, (byte) 48, (byte) 215, (byte) 72 },
|
(byte) 226, (byte) 108, (byte) 87, (byte) 194, (byte) 164, (byte) 48, (byte) 215, (byte) 72 },
|
||||||
OffsetDateTime.parse("2019-09-03T19:27:28.9493730Z"))
|
OffsetDateTime.parse("2019-09-03T19:27:28.9493730Z"))
|
||||||
);
|
);
|
||||||
|
|
||||||
return items.stream().map(item -> new Object[] { item.buffer, item.value }).iterator();
|
return items.stream().map(item -> new Object[] { item.buffer, item.value }).iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user