diff --git a/modules/bootstrapped/test/src/smithy4s/DocumentSpec.scala b/modules/bootstrapped/test/src/smithy4s/DocumentSpec.scala index b2d99be50..fa3f749ae 100644 --- a/modules/bootstrapped/test/src/smithy4s/DocumentSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/DocumentSpec.scala @@ -581,6 +581,31 @@ class DocumentSpec() extends FunSuite { ) } + test("Document encoder - timestamp epoch seconds with nanos") { + val timestampWithNanos = + Timestamp(1716459630L, 500 * 1000 * 1000 /* half a second */ ) + val result = Document.Encoder + .withExplicitDefaultsEncoding(false) + .fromSchema(TimestampOperationInput.schema) + .encode( + TimestampOperationInput( + timestampWithNanos, + timestampWithNanos, + timestampWithNanos + ) + ) + expect.same( + Document.obj( + "httpDate" -> Document.fromString("Thu, 23 May 2024 10:20:30.500 GMT"), + "dateTime" -> Document.fromString("2024-05-23T10:20:30.500Z"), + "epochSeconds" -> Document.fromBigDecimal( + BigDecimal("1716459630.500000") + ) + ), + result + ) + } + test("Document decoder - timestamp defaults") { val doc = Document.obj() val result = Document.Decoder diff --git a/modules/core/src/smithy4s/internals/DocumentEncoderSchemaVisitor.scala b/modules/core/src/smithy4s/internals/DocumentEncoderSchemaVisitor.scala index 37204c562..c6b555d02 100644 --- a/modules/core/src/smithy4s/internals/DocumentEncoderSchemaVisitor.scala +++ b/modules/core/src/smithy4s/internals/DocumentEncoderSchemaVisitor.scala @@ -99,9 +99,14 @@ class DocumentEncoderSchemaVisitor( hints .get(TimestampFormat) .getOrElse(TimestampFormat.EPOCH_SECONDS) match { - case DATE_TIME => ts => DString(ts.format(DATE_TIME)) - case HTTP_DATE => ts => DString(ts.format(HTTP_DATE)) - case EPOCH_SECONDS => ts => DNumber(BigDecimal(ts.epochSecond)) + case DATE_TIME => ts => DString(ts.format(DATE_TIME)) + case HTTP_DATE => ts => DString(ts.format(HTTP_DATE)) + case EPOCH_SECONDS => + ts => + val epochSecondsWithNanos = + BigDecimal(ts.epochSecond) + (BigDecimal(ts.nano) * BigDecimal(10) + .pow(-9)) + DNumber(epochSecondsWithNanos) } case PDocument => from(identity) case PFloat => from(float => DNumber(BigDecimal(float.toDouble)))