Skip to content

Commit ce77a34

Browse files
committed
1 parent 98467e7 commit ce77a34

File tree

4 files changed

+61
-37
lines changed

4 files changed

+61
-37
lines changed

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/ser/DurationSerializer.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.fasterxml.jackson.datatype.jsr310.DecimalUtils;
3232

3333
import java.io.IOException;
34+
import java.math.BigDecimal;
3435
import java.time.Duration;
3536
import java.time.format.DateTimeFormatter;
3637

@@ -81,9 +82,20 @@ public void serialize(Duration duration, JsonGenerator generator, SerializerProv
8182
{
8283
if (useTimestamp(provider)) {
8384
if (useNanoseconds(provider)) {
84-
generator.writeNumber(DecimalUtils.toBigDecimal(
85-
duration.getSeconds(), duration.getNano()
86-
));
85+
// 20-Oct-2020, tatu: [modules-java8#165] Need to take care of
86+
// negative values too, and without work-around values
87+
// returned are wonky wrt conversions
88+
BigDecimal bd;
89+
if (duration.isNegative()) {
90+
duration = duration.abs();
91+
bd = DecimalUtils.toBigDecimal(duration.getSeconds(),
92+
duration.getNano())
93+
.negate();
94+
} else {
95+
bd = DecimalUtils.toBigDecimal(duration.getSeconds(),
96+
duration.getNano());
97+
}
98+
generator.writeNumber(bd);
8799
} else {
88100
generator.writeNumber(duration.toMillis());
89101
}

datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/DurationSerTest.java

+39-34
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ public void testSerializationAsTimestampNanoseconds01() throws Exception
3232
.with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
3333
.with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
3434
.writeValueAsString(duration);
35-
36-
assertNotNull("The value should not be null.", value);
3735
assertEquals("The value is not correct.", "60"+NO_NANOSECS_SUFFIX, value);
3836
}
3937

@@ -45,34 +43,47 @@ public void testSerializationAsTimestampNanoseconds02() throws Exception
4543
.with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
4644
.with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
4745
.writeValueAsString(duration);
48-
49-
assertNotNull("The value should not be null.", value);
5046
assertEquals("The value is not correct.", "13498.000008374", value);
5147
}
5248

49+
// [modules-java8#165]
50+
@Test
51+
public void testSerializationAsTimestampNanoseconds03() throws Exception
52+
{
53+
ObjectWriter w = WRITER
54+
.with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
55+
.with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);
56+
57+
// 20-Oct-2020, tatu: Very weird, but "use nanoseconds" actually results
58+
// in unit being seconds, with fractions (with nanosec precision)
59+
String value = w.writeValueAsString(Duration.ofMillis(1L));
60+
assertEquals("The value is not correct.", "0.001000000", value);
61+
62+
value = w.writeValueAsString(Duration.ofMillis(-1L));
63+
assertEquals("The value is not correct.", "-0.001000000", value);
64+
}
65+
5366
@Test
5467
public void testSerializationAsTimestampMilliseconds01() throws Exception
5568
{
56-
Duration duration = Duration.ofSeconds(60L, 0);
57-
String value = WRITER
69+
final ObjectWriter w = WRITER
5870
.with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
59-
.without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
60-
.writeValueAsString(duration);
71+
.without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);
72+
String value = w.writeValueAsString(Duration.ofSeconds(45L, 0));
73+
assertEquals("The value is not correct.", "45000", value);
6174

62-
assertNotNull("The value should not be null.", value);
63-
assertEquals("The value is not correct.", "60000", value);
75+
// and with negative value too
76+
value = w.writeValueAsString(Duration.ofSeconds(-32L, 0));
77+
assertEquals("The value is not correct.", "-32000", value);
6478
}
6579

6680
@Test
6781
public void testSerializationAsTimestampMilliseconds02() throws Exception
6882
{
69-
Duration duration = Duration.ofSeconds(13498L, 8374);
7083
String value = WRITER
7184
.with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
7285
.without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
73-
.writeValueAsString(duration);
74-
75-
assertNotNull("The value should not be null.", value);
86+
.writeValueAsString(Duration.ofSeconds(13498L, 8374));
7687
assertEquals("The value is not correct.", "13498000", value);
7788
}
7889

@@ -84,8 +95,6 @@ public void testSerializationAsTimestampMilliseconds03() throws Exception
8495
.with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
8596
.without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
8697
.writeValueAsString(duration);
87-
88-
assertNotNull("The value should not be null.", value);
8998
assertEquals("The value is not correct.", "13498837", value);
9099
}
91100

@@ -96,8 +105,6 @@ public void testSerializationAsString01() throws Exception
96105
String value = WRITER
97106
.without(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
98107
.writeValueAsString(duration);
99-
100-
assertNotNull("The value should not be null.", value);
101108
assertEquals("The value is not correct.", '"' + duration.toString() + '"', value);
102109
}
103110

@@ -108,51 +115,49 @@ public void testSerializationAsString02() throws Exception
108115
String value = WRITER
109116
.without(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
110117
.writeValueAsString(duration);
111-
112-
assertNotNull("The value should not be null.", value);
113118
assertEquals("The value is not correct.", '"' + duration.toString() + '"', value);
114119
}
115120

116121
@Test
117122
public void testSerializationWithTypeInfo01() throws Exception
118123
{
119-
ObjectMapper mapper = newMapper(); // need new to add mix-ins:
120-
mapper.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, true);
121-
mapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, true);
122-
mapper.addMixIn(TemporalAmount.class, MockObjectConfiguration.class);
124+
ObjectMapper mapper = mapperBuilder()
125+
.addMixIn(TemporalAmount.class, MockObjectConfiguration.class)
126+
.enable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
127+
.enable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
128+
.build();
123129
Duration duration = Duration.ofSeconds(13498L, 8374);
124130
String value = mapper.writeValueAsString(duration);
125131

126-
assertNotNull("The value should not be null.", value);
127132
assertEquals("The value is not correct.",
128133
"[\"" + Duration.class.getName() + "\",13498.000008374]", value);
129134
}
130135

131136
@Test
132137
public void testSerializationWithTypeInfo02() throws Exception
133138
{
134-
ObjectMapper mapper = newMapper(); // need new to add mix-ins:
135-
mapper.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, true);
136-
mapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
137-
mapper.addMixIn(TemporalAmount.class, MockObjectConfiguration.class);
139+
ObjectMapper mapper = mapperBuilder()
140+
.addMixIn(TemporalAmount.class, MockObjectConfiguration.class)
141+
.enable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
142+
.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
143+
.build();
138144
Duration duration = Duration.ofSeconds(13498L, 837481723);
139145
String value = mapper.writeValueAsString(duration);
140146

141-
assertNotNull("The value should not be null.", value);
142147
assertEquals("The value is not correct.",
143148
"[\"" + Duration.class.getName() + "\",13498837]", value);
144149
}
145150

146151
@Test
147152
public void testSerializationWithTypeInfo03() throws Exception
148153
{
149-
ObjectMapper mapper = newMapper(); // need new to add mix-ins:
150-
mapper.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false);
151-
mapper.addMixIn(TemporalAmount.class, MockObjectConfiguration.class);
154+
ObjectMapper mapper = mapperBuilder()
155+
.addMixIn(TemporalAmount.class, MockObjectConfiguration.class)
156+
.disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
157+
.build();
152158
Duration duration = Duration.ofSeconds(13498L, 8374);
153159
String value = mapper.writeValueAsString(duration);
154160

155-
assertNotNull("The value should not be null.", value);
156161
assertEquals("The value is not correct.",
157162
"[\"" + Duration.class.getName() + "\",\"" + duration.toString() + "\"]", value);
158163
}

release-notes/CREDITS-2.x

+5
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ Samantha Williamson (samwill@github)
104104
* Contributed fix to #148: Allow strict `LocalDate` parsing
105105
(2.11.0)
106106

107+
Joni Syri (jpsyri@github)
108+
* Reported #165: Problem in serializing negative Duration values
109+
(2.12.0)
110+
107111
Moritz Orth ([email protected])
108112
* Reported and suggested fix for #166: Cannot deserialize OffsetDateTime.MIN and
109113
OffsetDateTime.MAX with ADJUST_DATES_TO_CONTEXT_TIME_ZONE enabled
@@ -128,3 +132,4 @@ Oriol Barcelona (obarcelonap@github)
128132
* Contributed fix for #184: `DurationDeserializer` should use `@JsonFormat.pattern`
129133
(and config override) to support configurable `ChronoUnit`
130134
(2.12.0)
135+

release-notes/VERSION-2.x

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Modules:
1010

1111
2.12.0 (not yet released)
1212

13+
#165: Problem in serializing negative Duration values
14+
(reported by Joni S)
1315
#166: Cannot deserialize `OffsetDateTime.MIN` or `OffsetDateTime.MAX` with
1416
`ADJUST_DATES_TO_CONTEXT_TIME_ZONE` enabled
1517
(reported, fix suggested by Moritz O)

0 commit comments

Comments
 (0)