Skip to content

Commit

Permalink
add handling of JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS (
Browse files Browse the repository at this point in the history
  • Loading branch information
raman-babich committed Jun 19, 2023
1 parent 818afc1 commit b0da1e6
Show file tree
Hide file tree
Showing 13 changed files with 612 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.math.BigDecimal;
import java.time.DateTimeException;
import java.time.Duration;
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonFormat;

Expand Down Expand Up @@ -61,9 +62,17 @@ public class DurationDeserializer extends JSR310DeserializerBase<Duration>
*/
protected final DurationUnitConverter _durationUnitConverter;

/**
* Flag for <code>JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS</code>
*
* @since 2.16
*/
protected final Boolean _readTimestampsAsNanosOverride;

public DurationDeserializer() {
super(Duration.class);
_durationUnitConverter = null;
_readTimestampsAsNanosOverride = null;
}

/**
Expand All @@ -72,6 +81,7 @@ public DurationDeserializer() {
protected DurationDeserializer(DurationDeserializer base, Boolean leniency) {
super(base, leniency);
_durationUnitConverter = base._durationUnitConverter;
_readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride;
}

/**
Expand All @@ -80,6 +90,19 @@ protected DurationDeserializer(DurationDeserializer base, Boolean leniency) {
protected DurationDeserializer(DurationDeserializer base, DurationUnitConverter converter) {
super(base, base._isLenient);
_durationUnitConverter = converter;
_readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride;
}

/**
* @since 2.16
*/
protected DurationDeserializer(DurationDeserializer base,
Boolean leniency,
DurationUnitConverter converter,
Boolean readTimestampsAsNanosOverride) {
super(base, leniency);
_durationUnitConverter = converter;
_readTimestampsAsNanosOverride = readTimestampsAsNanosOverride;
}

@Override
Expand All @@ -97,24 +120,31 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
{
JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType());
DurationDeserializer deser = this;
boolean leniency = _isLenient;
DurationUnitConverter unitConverter = _durationUnitConverter;
Boolean timestampsAsNanosOverride = _readTimestampsAsNanosOverride;
if (format != null) {
if (format.hasLenient()) {
Boolean leniency = format.getLenient();
if (leniency != null) {
deser = deser.withLeniency(leniency);
}
leniency = format.getLenient();
}
if (format.hasPattern()) {
final String pattern = format.getPattern();
DurationUnitConverter p = DurationUnitConverter.from(pattern);
if (p == null) {
unitConverter = DurationUnitConverter.from(pattern);
if (unitConverter == null) {
ctxt.reportBadDefinition(getValueType(ctxt),
String.format(
"Bad 'pattern' definition (\"%s\") for `Duration`: expected one of [%s]",
pattern, DurationUnitConverter.descForAllowed()));
}
deser = deser.withConverter(p);
}
timestampsAsNanosOverride =
format.getFeature(JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
}
if (leniency != _isLenient
|| !Objects.equals(unitConverter, _durationUnitConverter)
|| !Objects.equals(timestampsAsNanosOverride, _readTimestampsAsNanosOverride)) {
return new DurationDeserializer(
this, leniency, unitConverter, timestampsAsNanosOverride);
}
return deser;
}
Expand Down Expand Up @@ -177,9 +207,14 @@ protected Duration _fromTimestamp(DeserializationContext ctxt, long ts) {
}
// 20-Oct-2020, tatu: This makes absolutely no sense but... somehow
// became the default handling.
if (ctxt.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) {
if (shouldReadTimestampsAsNanoseconds(ctxt)) {
return Duration.ofSeconds(ts);
}
return Duration.ofMillis(ts);
}

protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext context) {
return (_readTimestampsAsNanosOverride != null) ? _readTimestampsAsNanosOverride :
context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ public class InstantDeserializer<T extends Temporal>
*/
protected final Boolean _adjustToContextTZOverride;

/**
* Flag for <code>JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS</code>
*
* @since 2.16
*/
protected final Boolean _readTimestampsAsNanosOverride;

protected InstantDeserializer(Class<T> supportedType,
DateTimeFormatter formatter,
Function<TemporalAccessor, T> parsedToValue,
Expand All @@ -126,7 +133,8 @@ protected InstantDeserializer(Class<T> supportedType,
this.fromNanoseconds = fromNanoseconds;
this.adjust = adjust == null ? ((d, z) -> d) : adjust;
this.replaceZeroOffsetAsZ = replaceZeroOffsetAsZ;
_adjustToContextTZOverride = null;
this._adjustToContextTZOverride = null;
this._readTimestampsAsNanosOverride = null;
}

@SuppressWarnings("unchecked")
Expand All @@ -139,6 +147,7 @@ protected InstantDeserializer(InstantDeserializer<T> base, DateTimeFormatter f)
adjust = base.adjust;
replaceZeroOffsetAsZ = (_formatter == DateTimeFormatter.ISO_INSTANT);
_adjustToContextTZOverride = base._adjustToContextTZOverride;
_readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride;
}

@SuppressWarnings("unchecked")
Expand All @@ -151,6 +160,7 @@ protected InstantDeserializer(InstantDeserializer<T> base, Boolean adjustToConte
adjust = base.adjust;
replaceZeroOffsetAsZ = base.replaceZeroOffsetAsZ;
_adjustToContextTZOverride = adjustToContextTimezoneOverride;
_readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride;
}

@SuppressWarnings("unchecked")
Expand All @@ -163,19 +173,40 @@ protected InstantDeserializer(InstantDeserializer<T> base, DateTimeFormatter f,
adjust = base.adjust;
replaceZeroOffsetAsZ = (_formatter == DateTimeFormatter.ISO_INSTANT);
_adjustToContextTZOverride = base._adjustToContextTZOverride;
_readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride;
}

/**
* @since 2.16
*/
protected InstantDeserializer(InstantDeserializer<T> base,
Boolean leniency,
DateTimeFormatter formatter,
JsonFormat.Shape shape,
Boolean adjustToContextTimezoneOverride,
Boolean readTimestampsAsNanosOverride)
{
super(base, leniency, formatter, shape);
parsedToValue = base.parsedToValue;
fromMilliseconds = base.fromMilliseconds;
fromNanoseconds = base.fromNanoseconds;
adjust = base.adjust;
replaceZeroOffsetAsZ = base.replaceZeroOffsetAsZ;
_adjustToContextTZOverride = adjustToContextTimezoneOverride;
_readTimestampsAsNanosOverride = readTimestampsAsNanosOverride;
}

@Override
protected InstantDeserializer<T> withDateFormat(DateTimeFormatter dtf) {
if (dtf == _formatter) {
return this;
}
return new InstantDeserializer<T>(this, dtf);
return new InstantDeserializer<>(this, dtf);
}

@Override
protected InstantDeserializer<T> withLeniency(Boolean leniency) {
return new InstantDeserializer<T>(this, _formatter, leniency);
return new InstantDeserializer<>(this, _formatter, leniency);
}

@Override
Expand All @@ -188,9 +219,14 @@ protected JSR310DateTimeDeserializerBase<?> _withFormatOverrides(Deserialization
{
InstantDeserializer<T> deser = (InstantDeserializer<T>) super._withFormatOverrides(ctxt,
property, formatOverrides);
Boolean B = formatOverrides.getFeature(JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
if (!Objects.equals(B, deser._adjustToContextTZOverride)) {
return new InstantDeserializer<T>(deser, B);
Boolean adjustToContextTZOverride = formatOverrides.getFeature(
JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
Boolean readTimestampsAsNanosOverride = formatOverrides.getFeature(
JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
if (!Objects.equals(adjustToContextTZOverride, deser._adjustToContextTZOverride)
|| !Objects.equals(readTimestampsAsNanosOverride, deser._readTimestampsAsNanosOverride)) {
return new InstantDeserializer<>(deser, deser._isLenient, deser._formatter,
deser._shape, adjustToContextTZOverride, readTimestampsAsNanosOverride);
}
return deser;
}
Expand Down Expand Up @@ -230,6 +266,11 @@ protected boolean shouldAdjustToContextTimezone(DeserializationContext context)
context.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
}

protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext context) {
return (_readTimestampsAsNanosOverride != null) ? _readTimestampsAsNanosOverride :
context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
}

// Helper method to find Strings of form "all digits" and "digits-comma-digits"
protected int _countPeriods(String str)
{
Expand Down Expand Up @@ -305,7 +346,7 @@ protected T _fromString(JsonParser p, DeserializationContext ctxt,

protected T _fromLong(DeserializationContext context, long timestamp)
{
if(context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)){
if(shouldReadTimestampsAsNanoseconds(context)){
return fromNanoseconds.apply(new FromDecimalArguments(
timestamp, 0, this.getZone(context)
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase<T> base,
_shape = shape;
}

/**
* @since 2.16
*/
protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase<T> base,
Boolean leniency,
DateTimeFormatter formatter,
JsonFormat.Shape shape) {
super(base, leniency);
_formatter = formatter;
_shape = shape;
}

protected abstract JSR310DateTimeDeserializerBase<T> withDateFormat(DateTimeFormatter dtf);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonFormat;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.JsonTokenId;

import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
Expand All @@ -46,19 +47,40 @@ public class LocalDateTimeDeserializer

public static final LocalDateTimeDeserializer INSTANCE = new LocalDateTimeDeserializer();

/**
* Flag for <code>JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS</code>
*
* @since 2.16
*/
protected final Boolean _readTimestampsAsNanosOverride;

protected LocalDateTimeDeserializer() { // was private before 2.12
this(DEFAULT_FORMATTER);
}

public LocalDateTimeDeserializer(DateTimeFormatter formatter) {
super(LocalDateTime.class, formatter);
_readTimestampsAsNanosOverride = null;
}

/**
* Since 2.10
*/
protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, Boolean leniency) {
super(base, leniency);
_readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride;
}

/**
* Since 2.16
*/
protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base,
Boolean leniency,
DateTimeFormatter formatter,
JsonFormat.Shape shape,
Boolean readTimestampsAsNanosOverride) {
super(base, leniency, formatter, shape);
_readTimestampsAsNanosOverride = readTimestampsAsNanosOverride;
}

@Override
Expand All @@ -74,6 +96,20 @@ protected LocalDateTimeDeserializer withLeniency(Boolean leniency) {
@Override
protected LocalDateTimeDeserializer withShape(JsonFormat.Shape shape) { return this; }

@Override
protected JSR310DateTimeDeserializerBase<?> _withFormatOverrides(DeserializationContext ctxt,
BeanProperty property, JsonFormat.Value formatOverrides) {
LocalDateTimeDeserializer deser = (LocalDateTimeDeserializer)
super._withFormatOverrides(ctxt, property, formatOverrides);
Boolean readTimestampsAsNanosOverride = formatOverrides.getFeature(
JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
if (!Objects.equals(readTimestampsAsNanosOverride, deser._readTimestampsAsNanosOverride)) {
return new LocalDateTimeDeserializer(deser, deser._isLenient, deser._formatter,
deser._shape, readTimestampsAsNanosOverride);
}
return deser;
}

@Override
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException
{
Expand Down Expand Up @@ -117,8 +153,7 @@ public LocalDateTime deserialize(JsonParser parser, DeserializationContext conte
result = LocalDateTime.of(year, month, day, hour, minute, second);
} else {
int partialSecond = parser.getIntValue();
if (partialSecond < 1_000 &&
!context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS))
if (partialSecond < 1_000 && !shouldReadTimestampsAsNanoseconds(context))
partialSecond *= 1_000_000; // value is milliseconds, convert it to nanoseconds
if (parser.nextToken() != JsonToken.END_ARRAY) {
throw context.wrongTokenException(parser, handledType(), JsonToken.END_ARRAY,
Expand All @@ -142,6 +177,11 @@ public LocalDateTime deserialize(JsonParser parser, DeserializationContext conte
return _handleUnexpectedToken(context, parser, "Expected array or string.");
}

protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext context) {
return (_readTimestampsAsNanosOverride != null) ? _readTimestampsAsNanosOverride :
context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
}

protected LocalDateTime _fromString(JsonParser p, DeserializationContext ctxt,
String string0) throws IOException
{
Expand Down

0 comments on commit b0da1e6

Please sign in to comment.