diff --git a/CHANGES.txt b/CHANGES.txt index 549dd7eba8..188ce274b2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,7 @@ * Do not go to disk for reading hints file sizes (CASSANDRA-19477) * Fix system_views.settings to handle array types (CASSANDRA-19475) Merged from 4.0: + * Fix CQL tojson timestamp output on negative timestamp values before Gregorian calendar reform in 1582 (CASSANDRA-19566) * Fix few types issues and implement types compatibility tests (CASSANDRA-19479) * Optionally avoid hint transfer during decommission (CASSANDRA-19525) * Change logging to TRACE when failing to get peer certificate (CASSANDRA-19508) diff --git a/src/java/org/apache/cassandra/db/marshal/DateType.java b/src/java/org/apache/cassandra/db/marshal/DateType.java index 473cedf407..372dac4f2c 100644 --- a/src/java/org/apache/cassandra/db/marshal/DateType.java +++ b/src/java/org/apache/cassandra/db/marshal/DateType.java @@ -80,7 +80,7 @@ public class DateType extends AbstractType @Override public String toJSONString(ByteBuffer buffer, ProtocolVersion protocolVersion) { - return '"' + TimestampSerializer.getJsonDateFormatter().format(TimestampSerializer.instance.deserialize(buffer)) + '"'; + return '"' + TimestampSerializer.getJsonDateFormatter().format(TimestampSerializer.instance.deserialize(buffer).toInstant()) + '"'; } @Override diff --git a/src/java/org/apache/cassandra/db/marshal/TimestampType.java b/src/java/org/apache/cassandra/db/marshal/TimestampType.java index ccf1da3e20..b11391e831 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimestampType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimestampType.java @@ -101,7 +101,7 @@ public class TimestampType extends TemporalType private String toString(Date date) { - return date != null ? TimestampSerializer.getJsonDateFormatter().format(date) : ""; + return date != null ? TimestampSerializer.getJsonDateFormatter().format(date.toInstant()) : ""; } @Override diff --git a/src/java/org/apache/cassandra/serializers/TimestampSerializer.java b/src/java/org/apache/cassandra/serializers/TimestampSerializer.java index 71782e5434..fcbfcc8537 100644 --- a/src/java/org/apache/cassandra/serializers/TimestampSerializer.java +++ b/src/java/org/apache/cassandra/serializers/TimestampSerializer.java @@ -22,7 +22,7 @@ import org.apache.cassandra.db.marshal.ValueAccessor; import org.apache.cassandra.utils.ByteBufferUtil; import java.nio.ByteBuffer; -import java.text.SimpleDateFormat; +import java.text.Format; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -32,7 +32,6 @@ import java.time.temporal.ChronoField; import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.TimeZone; import java.util.regex.Pattern; import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis; @@ -105,23 +104,29 @@ public class TimestampSerializer extends TypeSerializer private static final Pattern timestampPattern = Pattern.compile("^-?\\d+$"); - private static final FastThreadLocal FORMATTER_UTC = new FastThreadLocal() + private static final FastThreadLocal FORMATTER_UTC = new FastThreadLocal() { - protected SimpleDateFormat initialValue() + protected java.text.Format initialValue() { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX"); - sdf.setTimeZone(TimeZone.getTimeZone("UTC")); - return sdf; + return new DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX") + .parseDefaulting(ChronoField.NANO_OF_DAY, 0) + .toFormatter() + .withZone(ZoneId.of("UTC")) + .toFormat(); } }; - private static final FastThreadLocal FORMATTER_TO_JSON = new FastThreadLocal() + private static final FastThreadLocal FORMATTER_TO_JSON = new FastThreadLocal() { - protected SimpleDateFormat initialValue() + protected java.text.Format initialValue() { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSX"); - sdf.setTimeZone(TimeZone.getTimeZone("UTC")); - return sdf; + return new DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd HH:mm:ss.SSSX") + .parseDefaulting(ChronoField.NANO_OF_DAY, 0) + .toFormatter() + .withZone(ZoneId.of("UTC")) + .toFormat(); } }; @@ -171,7 +176,7 @@ public class TimestampSerializer extends TypeSerializer throw new MarshalException(String.format("Unable to parse a date/time from '%s'", source)); } - public static SimpleDateFormat getJsonDateFormatter() + public static Format getJsonDateFormatter() { return FORMATTER_TO_JSON.get(); } @@ -189,7 +194,7 @@ public class TimestampSerializer extends TypeSerializer public String toStringUTC(Date value) { - return value == null ? "" : FORMATTER_UTC.get().format(value); + return value == null ? "" : FORMATTER_UTC.get().format(value.toInstant()); } public Class getType() @@ -206,6 +211,6 @@ public class TimestampSerializer extends TypeSerializer { return buffer == null || !buffer.hasRemaining() ? "null" - : FORMATTER_UTC.get().format(deserialize(buffer)); + : FORMATTER_UTC.get().format(deserialize(buffer).toInstant()); } }