Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 6 additions & 0 deletions .changes/next-release/bugfix-AmazonDynamoDB-1db6c7e.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "Amazon DynamoDB",
"description": "Fixed DurationAttributeConverter negative fractional duration serialization and parsing. Values like Duration.ofMillis(-1) now serialize as -0.001000000 and round trip correctly. Fixes [#7060](https://github.com/aws/aws-sdk-java-v2/issues/7060).",
"contributor": "Arnab Nandy"
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,16 @@ public AttributeValueType attributeValueType() {

@Override
public AttributeValue transformFrom(Duration input) {
long seconds = input.getSeconds();
int nanos = input.getNano();
String value = seconds + (nanos == 0 ? "" : "." + padLeft(9, nanos));

if (input.isNegative() && nanos != 0) {
value = "-" + Math.abs(seconds + 1) + "." + padLeft(9, 1_000_000_000 - nanos);
}

return AttributeValue.builder()
.n(input.getSeconds() +
(input.getNano() == 0 ? "" : "." + padLeft(9, input.getNano())))
.n(value)
.build();
}

Expand All @@ -103,10 +110,11 @@ private Visitor() {
public Duration convertNumber(String value) {
String[] splitOnDecimal = ConverterUtils.splitNumberOnDecimal(value);

boolean isNegative = value.startsWith("-");
long seconds = Long.parseLong(splitOnDecimal[0]);
int nanoAdjustment = Integer.parseInt(padRight(splitOnDecimal[1]));

if (seconds < 0) {
if (isNegative) {
nanoAdjustment = -nanoAdjustment;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ void testConvertTo_NoPadding(String value, Duration expected) {
assertThat(converted).isEqualByComparingTo(expected);
}

@ParameterizedTest
@MethodSource("roundTripDurations")
void roundTrip(Duration duration) {
Duration converted = converter.transformTo(converter.transformFrom(duration));

assertThat(converted).isEqualByComparingTo(duration);
}

static Stream<Arguments> noPadding() {
return Stream.of(
Arguments.of("0.123456789", Duration.ofNanos(123_456_789)),
Expand All @@ -74,7 +82,10 @@ static Stream<Arguments> noPadding() {
Arguments.of("0.1", Duration.ofMillis(100)),
Arguments.of("0.001", Duration.ofMillis(1)),
Arguments.of("0.000001", Duration.of(1, MICROS)),
Arguments.of("0.001", Duration.ofMillis(1))
Arguments.of("0.001", Duration.ofMillis(1)),
Arguments.of("-0.1", Duration.ofMillis(-100)),
Arguments.of("-0.001", Duration.ofMillis(-1)),
Arguments.of("-0.000001", Duration.of(-1, MICROS))
);
}

Expand Down Expand Up @@ -107,7 +118,22 @@ static Stream<Arguments> durations() {
Arguments.of("9", Duration.ofSeconds(9)),
Arguments.of("-9", Duration.ofSeconds(-9)),
Arguments.of("0.001000000", Duration.ofMillis(1)),
Arguments.of("0.000000001", Duration.ofNanos(1)));
Arguments.of("0.000000001", Duration.ofNanos(1)),
Arguments.of("-0.001000000", Duration.ofMillis(-1)),
Arguments.of("-0.000000001", Duration.ofNanos(-1)),
Arguments.of("-1.234567890", Duration.ofNanos(-1_234_567_890)));
}

static Stream<Duration> roundTripDurations() {
return Stream.of(
Duration.ZERO,
Duration.ofNanos(1),
Duration.ofMillis(1),
Duration.ofSeconds(1, 234_567_890),
Duration.ofSeconds(-9),
Duration.ofMillis(-1),
Duration.ofNanos(-1),
Duration.ofNanos(-1_234_567_890));
}

}
}