mirror of
https://github.com/microsoft/HybridRow.git
synced 2026-01-20 18:03:14 +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.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 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 TICKS_MASK = 0x3FFFFFFFFFFFFFFFL;
|
||||
|
||||
private static final long UNIX_EPOCH_TICKS = 0x89F7FF5F7B58000L;
|
||||
|
||||
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_UTC_TOTAL_SECONDS = ZoneOffset.UTC.getTotalSeconds();
|
||||
@@ -61,10 +87,13 @@ public final class DateTimeCodec {
|
||||
in.readableBytes());
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
final byte[] bytes = new byte[BYTES];
|
||||
encode(offsetDateTime, Unpooled.wrappedBuffer(bytes));
|
||||
encode(offsetDateTime, Unpooled.wrappedBuffer(bytes).clear());
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -91,21 +120,25 @@ public final class DateTimeCodec {
|
||||
*/
|
||||
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,
|
||||
epochSecond);
|
||||
ticks);
|
||||
|
||||
final int zoneOffsetTotalSeconds = offsetDateTime.getOffset().getTotalSeconds();
|
||||
final long value;
|
||||
|
||||
if (zoneOffsetTotalSeconds == ZONE_OFFSET_UTC_TOTAL_SECONDS) {
|
||||
value = epochSecond | KIND_UTC;
|
||||
value = ticks | KIND_UTC;
|
||||
} else if (zoneOffsetTotalSeconds == ZONE_OFFSET_LOCAL_TOTAL_SECONDS) {
|
||||
value = epochSecond | KIND_LOCAL;
|
||||
value = ticks | KIND_LOCAL;
|
||||
} else {
|
||||
value = epochSecond | KIND_AMBIGUOUS;
|
||||
value = ticks | KIND_AMBIGUOUS;
|
||||
}
|
||||
|
||||
out.writeLongLE(value);
|
||||
|
||||
@@ -35,7 +35,7 @@ public class DateTimeCodecTest {
|
||||
|
||||
@Test(dataProvider = "dateTimeDataProvider")
|
||||
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);
|
||||
assertEquals(actual, value);
|
||||
}
|
||||
@@ -48,13 +48,14 @@ public class DateTimeCodecTest {
|
||||
|
||||
@Test(dataProvider = "dateTimeDataProvider")
|
||||
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);
|
||||
assertEquals(actual.array(), buffer);
|
||||
}
|
||||
|
||||
@DataProvider(name = "dateTimeDataProvider")
|
||||
private Iterator<Object[]> dateTimeData(byte[] buffer, OffsetDateTime value) {
|
||||
private static Iterator<Object[]> dateTimeData() {
|
||||
|
||||
ImmutableList<DateTimeItem> items = ImmutableList.of(
|
||||
new DateTimeItem(new byte[] {
|
||||
(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 },
|
||||
OffsetDateTime.parse("2019-09-03T19:27:28.9493730Z"))
|
||||
);
|
||||
|
||||
return items.stream().map(item -> new Object[] { item.buffer, item.value }).iterator();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user