Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add timestamp value support
  • Loading branch information
xerial committed May 12, 2021
commit 5c97c462959b134de9bdaf332e68cd7b933aa6a3
14 changes: 13 additions & 1 deletion msgpack-core/src/main/java/org/msgpack/core/MessagePacker.java
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ else if (s.length() < (1 << 16)) {
* This method writes a timestamp value using timestamp format family.
*
* @param instant the timestamp to be written
* @return this
* @return this packer
* @throws IOException when underlying output throws IOException
*/
public MessagePacker packTimestamp(Instant instant)
Expand All @@ -816,6 +816,18 @@ public MessagePacker packTimestamp(Instant instant)
return packTimestamp(instant.getEpochSecond(), instant.getNano());
}

/**
* Writes a Timesamp value using a millisecond value (e.g., System.currentTimeMillis())
* @param millis the millisecond value
* @return this packer
* @throws IOException when underlying output throws IOException
*/
public MessagePacker packTimestamp(long millis)
throws IOException
{
return packTimestamp(Instant.ofEpochMilli(millis));
}

private static final long NANOS_PER_SECOND = 1000000000L;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,7 @@ public interface ImmutableValue

@Override
public ImmutableStringValue asStringValue();

@Override
public ImmutableTimestampValue asTimestampValue();
}
17 changes: 17 additions & 0 deletions msgpack-core/src/main/java/org/msgpack/value/Value.java
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ public interface Value
*/
boolean isExtensionValue();

/**
* Returns true if the type of this value is Timestamp.
*
* If this method returns true, {@code asTimestamp} never throws exceptions.
* Note that you can't use <code>instanceof</code> or cast <code>((MapValue) thisValue)</code> to check type of a value because type of a mutable value is variable.
*/
boolean isTimestampValue();

/**
* Returns the value as {@code NilValue}. Otherwise throws {@code MessageTypeCastException}.
*
Expand Down Expand Up @@ -281,6 +289,15 @@ public interface Value
*/
ExtensionValue asExtensionValue();

/**
* Returns the value as {@code TimestampValue}. Otherwise throws {@code MessageTypeCastException}.
*
* Note that you can't use <code>instanceof</code> or cast <code>((TimestampValue) thisValue)</code> to check type of a value because type of a mutable value is variable.
*
* @throws MessageTypeCastException If type of this value is not Map.
*/
TimestampValue asTimestampValue();

/**
* Serializes the value using the specified {@code MessagePacker}
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@ public static ImmutableTimestampValue newTimestamp(Instant timestamp)
return new ImmutableTimestampValueImpl(timestamp);
}

public static ImmutableTimestampValue newTimestamp(long millis)
{
return newTimestamp(Instant.ofEpochMilli(millis));
}

public static ImmutableTimestampValue newTimestamp(long epochSecond, int nanoAdjustment)
{
return newTimestamp(Instant.ofEpochSecond(epochSecond, nanoAdjustment));
Expand Down
112 changes: 111 additions & 1 deletion msgpack-core/src/main/java/org/msgpack/value/Variable.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
Expand Down Expand Up @@ -109,6 +110,12 @@ public boolean isExtensionValue()
return getValueType().isExtensionType();
}

@Override
public boolean isTimestampValue()
{
return false;
}

@Override
public NilValue asNilValue()
{
Expand Down Expand Up @@ -175,6 +182,12 @@ public ExtensionValue asExtensionValue()
throw new MessageTypeCastException();
}

@Override
public TimestampValue asTimestampValue()
{
throw new MessageTypeCastException();
}

@Override
public boolean equals(Object obj)
{
Expand Down Expand Up @@ -211,7 +224,8 @@ public static enum Type
RAW_STRING(ValueType.STRING),
LIST(ValueType.ARRAY),
MAP(ValueType.MAP),
EXTENSION(ValueType.EXTENSION);
EXTENSION(ValueType.EXTENSION),
TIMESTAMP(ValueType.EXTENSION);

private final ValueType valueType;

Expand All @@ -235,6 +249,7 @@ public ValueType getValueType()
private final ArrayValueAccessor arrayAccessor = new ArrayValueAccessor();
private final MapValueAccessor mapAccessor = new MapValueAccessor();
private final ExtensionValueAccessor extensionAccessor = new ExtensionValueAccessor();
private final TimestampValueAccessor timestampAccessor = new TimestampValueAccessor();

private Type type;

Expand Down Expand Up @@ -1031,6 +1046,86 @@ public void writeTo(MessagePacker pk)
}
}

public Variable setTimestampValue(Instant timestamp)
{
this.type = Type.TIMESTAMP;
this.accessor = timestampAccessor;
this.objectValue = ValueFactory.newTimestamp(timestamp);
return this;
}

private class TimestampValueAccessor
extends AbstractValueAccessor
implements TimestampValue
{
@Override
public boolean isTimestampValue()
{
return true;
}

@Override
public ValueType getValueType()
{
return ValueType.EXTENSION;
}

@Override
public TimestampValue asTimestampValue()
{
return this;
}

@Override
public ImmutableTimestampValue immutableValue()
{
return (ImmutableTimestampValue) objectValue;
}

@Override
public byte getType()
{
return ((ImmutableTimestampValue) objectValue).getType();
}

@Override
public byte[] getData()
{
return ((ImmutableTimestampValue) objectValue).getData();
}

@Override
public void writeTo(MessagePacker pk)
throws IOException
{
((ImmutableTimestampValue) objectValue).writeTo(pk);
}

@Override
public long getEpochSecond()
{
return ((ImmutableTimestampValue) objectValue).getEpochSecond();
}

@Override
public int getNano()
{
return ((ImmutableTimestampValue) objectValue).getNano();
}

@Override
public long toEpochMillis()
{
return ((ImmutableTimestampValue) objectValue).toEpochMillis();
}

@Override
public Instant toInstant()
{
return ((ImmutableTimestampValue) objectValue).toInstant();
}
}

////
// Value
//
Expand Down Expand Up @@ -1144,6 +1239,12 @@ public boolean isExtensionValue()
return getValueType().isExtensionType();
}

@Override
public boolean isTimestampValue()
{
return this.type == Type.TIMESTAMP;
}

@Override
public NilValue asNilValue()
{
Expand Down Expand Up @@ -1242,4 +1343,13 @@ public ExtensionValue asExtensionValue()
}
return (ExtensionValue) accessor;
}

@Override
public TimestampValue asTimestampValue()
{
if (!isTimestampValue()) {
throw new MessageTypeCastException();
}
return (TimestampValue) accessor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.msgpack.value.ImmutableNumberValue;
import org.msgpack.value.ImmutableRawValue;
import org.msgpack.value.ImmutableStringValue;
import org.msgpack.value.ImmutableTimestampValue;
import org.msgpack.value.ImmutableValue;

abstract class AbstractImmutableValue
Expand Down Expand Up @@ -98,6 +99,12 @@ public boolean isExtensionValue()
return getValueType().isExtensionType();
}

@Override
public boolean isTimestampValue()
{
return false;
}

@Override
public ImmutableNilValue asNilValue()
{
Expand Down Expand Up @@ -163,4 +170,10 @@ public ImmutableExtensionValue asExtensionValue()
{
throw new MessageTypeCastException();
}

@Override
public ImmutableTimestampValue asTimestampValue()
{
throw new MessageTypeCastException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ public ImmutableTimestampValueImpl(Instant timestamp)
this.instant = timestamp;
}

@Override
public boolean isTimestampValue()
{
return true;
}

@Override
public byte getType()
{
Expand Down
17 changes: 17 additions & 0 deletions msgpack-core/src/test/scala/org/msgpack/core/MessagePackTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -591,11 +591,20 @@ class MessagePackTest extends MessagePackSpec {
val v = Instant.ofEpochSecond(second, nano)
check(v, { _.packTimestamp(v) }, { _.unpackTimestamp() })
}
// Using different insterfaces
forAll(posLong, posInt) { (second: Long, nano: Int) =>
val v = Instant.ofEpochSecond(second, nano)
check(v, { _.packTimestamp(second, nano) }, { _.unpackTimestamp() })
}
val secLessThan34bits = Gen.chooseNum[Long](0, 1L << 34)
forAll(secLessThan34bits, posInt) { (second: Long, nano: Int) =>
val v = Instant.ofEpochSecond(second, nano)
check(v, _.packTimestamp(v), _.unpackTimestamp())
}
forAll(secLessThan34bits, posInt) { (second: Long, nano: Int) =>
val v = Instant.ofEpochSecond(second, nano)
check(v, _.packTimestamp(second, nano), _.unpackTimestamp())
}

// Corner cases for u
// sign uint32 nanoseq (out of int32 range)
Expand All @@ -608,6 +617,14 @@ class MessagePackTest extends MessagePackSpec {
check(v, _.packTimestamp(v), _.unpackTimestamp())
}
}
Comment on lines +608 to +616
Copy link
Copy Markdown
Member Author

@xerial xerial May 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These test cases are for fixing uint32 conversion bugs in #431


"pack/unpack timestamp in millis" in {
val posLong = Gen.chooseNum[Long](-31557014167219200L, 31556889864403199L)
forAll(posLong) { (millis: Long) =>
val v = Instant.ofEpochMilli(millis)
check(v, { _.packTimestamp(millis) }, { _.unpackTimestamp() })
}
}
}

"MessagePack.PackerConfig" should {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class ValueFactoryTest extends MessagePackSpec {
isMap: Boolean = false,
isExtension: Boolean = false,
isRaw: Boolean = false,
isNumber: Boolean = false): Boolean = {
isNumber: Boolean = false,
isTimestamp: Boolean = false): Boolean = {
v.isNilValue shouldBe isNil
v.isBooleanValue shouldBe isBoolean
v.isIntegerValue shouldBe isInteger
Expand All @@ -47,6 +48,7 @@ class ValueFactoryTest extends MessagePackSpec {
v.isExtensionValue shouldBe isExtension
v.isRawValue shouldBe isRaw
v.isNumberValue shouldBe isNumber
v.isTimestampValue shouldBe isTimestamp
true
}

Expand Down Expand Up @@ -74,6 +76,10 @@ class ValueFactoryTest extends MessagePackSpec {
forAll { (v: Array[Byte]) =>
isValid(ValueFactory.newExtension(0, v), expected = ValueType.EXTENSION, isExtension = true, isRaw = false)
}

forAll { (millis: Long) =>
isValid(ValueFactory.newTimestamp(millis), expected = ValueType.EXTENSION, isExtension = true, isTimestamp = true)
}
}
}
}