Skip to content

Commit

Permalink
Add an ability to write timestamps in a numeric format
Browse files Browse the repository at this point in the history
  • Loading branch information
plokhotnyuk committed Dec 12, 2024
1 parent 1531ffb commit 4f1f650
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,27 @@ final class JsonWriter private[jsoniter_scala](
writeDouble(x)
}

/**
* Writes a timestamp value as a JSON value.
*
* @param epochSecond the epoch second of the timestamp to write
* @param nano the nanoseconds of the timestamp to write
* @throws JsonWriterException if the nanoseconds value is less than 0 or greater than 999999999
*/
def writeTimestampVal(epochSecond: Long, nano: Int): Unit = {
if (nano < 0 || nano > 999999999) encodeError("illegal nanoseconds value: " + nano)
writeOptionalCommaAndIndentionBeforeValue()
var pos = ensureBufCapacity(30)
val buf = this.buf
pos = writeLong(epochSecond, pos, buf)
if (nano != 0) {
val dotPos = pos
pos = writeSignificantFractionDigits(nano, pos + 9, pos, buf, digits)
buf(dotPos) = '.'
}
this.count = pos
}

/**
* Writes a `BigDecimal` value as a JSON string value.
*
Expand Down Expand Up @@ -871,6 +892,31 @@ final class JsonWriter private[jsoniter_scala](
writeBytes('"')
}

/**
* Writes a timestamp value as a JSON string value.
*
* @param epochSecond the epoch second of the timestamp to write
* @param nano the nanoseconds of the timestamp to write
* @throws JsonWriterException if the nanoseconds value is less than 0 or greater than 999999999
*/
def writeTimestampValAsString(epochSecond: Long, nano: Int): Unit = {
if (nano < 0 || nano > 999999999) encodeError("illegal nanoseconds value: " + nano)
writeOptionalCommaAndIndentionBeforeValue()
var pos = ensureBufCapacity(32)
val buf = this.buf
buf(pos) = '"'
pos += 1
pos = writeLong(epochSecond, pos, buf)
if (nano != 0) {
val dotPos = pos
pos = writeSignificantFractionDigits(nano, pos + 9, pos, buf, digits)
buf(dotPos) = '.'
}
buf(pos) = '"'
pos += 1
this.count = pos
}

/**
* Writes a byte array as a JSON hexadecimal string value.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,27 @@ final class JsonWriter private[jsoniter_scala](
writeDouble(x)
}

/**
* Writes a timestamp value as a JSON value.
*
* @param epochSecond the epoch second of the timestamp to write
* @param nano the nanoseconds of the timestamp to write
* @throws JsonWriterException if the nanoseconds value is less than 0 or greater than 999999999
*/
def writeTimestampVal(epochSecond: Long, nano: Int): Unit = {
if (nano < 0 || nano > 999999999) encodeError("illegal nanoseconds value: " + nano)
writeOptionalCommaAndIndentionBeforeValue()
var pos = ensureBufCapacity(30)
val buf = this.buf
pos = writeLong(epochSecond, pos, buf)
if (nano != 0) {
val dotPos = pos
pos = writeSignificantFractionDigits(nano, pos + 9, pos, buf, digits)
buf(dotPos) = '.'
}
this.count = pos
}

/**
* Writes a `BigDecimal` value as a JSON string value.
*
Expand Down Expand Up @@ -808,6 +829,31 @@ final class JsonWriter private[jsoniter_scala](
writeBytes('"')
}

/**
* Writes a timestamp value as a JSON string value.
*
* @param epochSecond the epoch second of the timestamp to write
* @param nano the nanoseconds of the timestamp to write
* @throws JsonWriterException if the nanoseconds value is less than 0 or greater than 999999999
*/
def writeTimestampValAsString(epochSecond: Long, nano: Int): Unit = {
if (nano < 0 || nano > 999999999) encodeError("illegal nanoseconds value: " + nano)
writeOptionalCommaAndIndentionBeforeValue()
var pos = ensureBufCapacity(32)
val buf = this.buf
buf(pos) = '"'
pos += 1
pos = writeLong(epochSecond, pos, buf)
if (nano != 0) {
val dotPos = pos
pos = writeSignificantFractionDigits(nano, pos + 9, pos, buf, digits)
buf(dotPos) = '.'
}
buf(pos) = '"'
pos += 1
this.count = pos
}

/**
* Writes a byte array as a JSON hexadecimal string value.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,27 @@ final class JsonWriter private[jsoniter_scala](
writeDouble(x)
}

/**
* Writes a timestamp value as a JSON value.
*
* @param epochSecond the epoch second of the timestamp to write
* @param nano the nanoseconds of the timestamp to write
* @throws JsonWriterException if the nanoseconds value is less than 0 or greater than 999999999
*/
def writeTimestampVal(epochSecond: Long, nano: Int): Unit = {
if (nano < 0 || nano > 999999999) encodeError("illegal nanoseconds value: " + nano)
writeOptionalCommaAndIndentionBeforeValue()
var pos = ensureBufCapacity(30)
val buf = this.buf
pos = writeLong(epochSecond, pos, buf)
if (nano != 0) {
val dotPos = pos
pos = writeSignificantFractionDigits(nano, pos + 9, pos, buf, digits)
buf(dotPos) = '.'
}
this.count = pos
}

/**
* Writes a `BigDecimal` value as a JSON string value.
*
Expand Down Expand Up @@ -808,6 +829,31 @@ final class JsonWriter private[jsoniter_scala](
writeBytes('"')
}

/**
* Writes a timestamp value as a JSON string value.
*
* @param epochSecond the epoch second of the timestamp to write
* @param nano the nanoseconds of the timestamp to write
* @throws JsonWriterException if the nanoseconds value is less than 0 or greater than 999999999
*/
def writeTimestampValAsString(epochSecond: Long, nano: Int): Unit = {
if (nano < 0 || nano > 999999999) encodeError("illegal nanoseconds value: " + nano)
writeOptionalCommaAndIndentionBeforeValue()
var pos = ensureBufCapacity(32)
val buf = this.buf
buf(pos) = '"'
pos += 1
pos = writeLong(epochSecond, pos, buf)
if (nano != 0) {
val dotPos = pos
pos = writeSignificantFractionDigits(nano, pos + 9, pos, buf, digits)
buf(dotPos) = '.'
}
buf(pos) = '"'
pos += 1
this.count = pos
}

/**
* Writes a byte array as a JSON hexadecimal string value.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,40 @@ class JsonWriterSpec extends AnyWordSpec with Matchers with ScalaCheckPropertyCh
forAll(genBigInt, minSuccessful(10000))(check)
}
}
"JsonWriter.writeVal for a timestamp" should {
"write timestamp values" in {
def check(epochSecond: Long, nano: Int): Unit = {
val s = BigDecimal({
val es = java.math.BigDecimal.valueOf(epochSecond)
if (nano == 0) es
else es.add(java.math.BigDecimal.valueOf({
if (epochSecond < 0) -nano
else nano
}.toLong, 9).stripTrailingZeros)
}).toString
withWriter(_.writeTimestampVal(epochSecond, nano)) shouldBe s
withWriter(_.writeTimestampValAsString(epochSecond, nano)) shouldBe s""""$s""""
}

check(1L, 0)
check(1L, 900000000)
check(1L, 990000000)
check(1L, 999000000)
check(1L, 999900000)
check(1L, 999990000)
check(1L, 999999000)
check(1L, 999999900)
check(1L, 999999990)
check(1L, 999999999)
forAll(arbitrary[Long], Gen.choose(0, 999999999), minSuccessful(10000))((es, n) => check(es, n))
}
"throw i/o exception for illegal nanos" in {
forAll(arbitrary[Long], Gen.oneOf(Gen.choose(Int.MinValue, -1), Gen.choose(1000000000, Int.MaxValue))) { (es, n) =>
assert(intercept[JsonWriterException](withWriter(_.writeTimestampVal(es, n))).getMessage.startsWith("illegal nanoseconds"))
assert(intercept[JsonWriterException](withWriter(_.writeTimestampValAsString(es, n))).getMessage.startsWith("illegal nanoseconds"))
}
}
}
"JsonWriter.writeVal and JsonWriter.writeValAsString and JsonWriter.writeKey for BigDecimal" should {
"don't write null value" in {
intercept[NullPointerException](withWriter(_.writeVal(null.asInstanceOf[BigDecimal])))
Expand Down
2 changes: 1 addition & 1 deletion version.sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ThisBuild / version := "2.31.4-SNAPSHOT"
ThisBuild / version := "2.32.0-SNAPSHOT"

0 comments on commit 4f1f650

Please sign in to comment.