@ -17,19 +17,14 @@ package org.springframework.data.mongodb.util.json;
@@ -17,19 +17,14 @@ package org.springframework.data.mongodb.util.json;
import static java.time.format.DateTimeFormatter.* ;
import java.lang.reflect.InvocationTargetException ;
import java.lang.reflect.Method ;
import java.time.Instant ;
import java.time.LocalDate ;
import java.time.ZoneId ;
import java.time.ZoneOffset ;
import java.time.ZonedDateTime ;
import java.time.format.DateTimeParseException ;
import java.time.temporal.TemporalAccessor ;
import java.time.temporal.TemporalQuery ;
import java.util.Calendar ;
import java.util.TimeZone ;
/ * *
* JsonBuff er implementation borrowed from < a href =
* DateTimeFormatter implementation borrowed from < a href =
* "https://github.com/mongodb/mongo-java-driver/blob/master/bson/src/main/org/bson/json/DateTimeFormatter.java" > MongoDB
* Inc . < / a > licensed under the Apache License , Version 2 . 0 . < br / >
* Formatted and modified .
@ -40,133 +35,22 @@ import java.util.TimeZone;
@@ -40,133 +35,22 @@ import java.util.TimeZone;
* /
class DateTimeFormatter {
private static final FormatterImpl FORMATTER_IMPL ;
static {
FormatterImpl dateTimeHelper ;
try {
dateTimeHelper = loadDateTimeFormatter (
"org.springframework.data.mongodb.util.json.DateTimeFormatter$Java8DateTimeFormatter" ) ;
} catch ( LinkageError e ) {
// this is expected if running on a release prior to Java 8: fallback to JAXB.
dateTimeHelper = loadDateTimeFormatter (
"org.springframework.data.mongodb.util.json.DateTimeFormatter$JaxbDateTimeFormatter" ) ;
}
FORMATTER_IMPL = dateTimeHelper ;
}
private static FormatterImpl loadDateTimeFormatter ( final String className ) {
try {
return ( FormatterImpl ) Class . forName ( className ) . getDeclaredConstructor ( ) . newInstance ( ) ;
} catch ( ClassNotFoundException e ) {
// this is unexpected as it means the class itself is not found
throw new ExceptionInInitializerError ( e ) ;
} catch ( InstantiationException e ) {
// this is unexpected as it means the class can't be instantiated
throw new ExceptionInInitializerError ( e ) ;
} catch ( IllegalAccessException e ) {
// this is unexpected as it means the no-args constructor isn't accessible
throw new ExceptionInInitializerError ( e ) ;
} catch ( NoSuchMethodException e ) {
throw new ExceptionInInitializerError ( e ) ;
} catch ( InvocationTargetException e ) {
throw new ExceptionInInitializerError ( e ) ;
}
}
private static final int DATE_STRING_LENGTH = "1970-01-01" . length ( ) ;
static long parse ( final String dateTimeString ) {
return FORMATTER_IMPL . parse ( dateTimeString ) ;
// ISO_OFFSET_DATE_TIME will not parse date strings consisting of just year-month-day, so use ISO_LOCAL_DATE for
// those
if ( dateTimeString . length ( ) = = DATE_STRING_LENGTH ) {
return LocalDate . parse ( dateTimeString , ISO_LOCAL_DATE ) . atStartOfDay ( ) . toInstant ( ZoneOffset . UTC ) . toEpochMilli ( ) ;
} else {
return ISO_OFFSET_DATE_TIME . parse ( dateTimeString , Instant : : from ) . toEpochMilli ( ) ;
}
}
static String format ( final long dateTime ) {
return FORMATTER_IMPL . format ( dateTime ) ;
}
private interface FormatterImpl {
long parse ( String dateTimeString ) ;
String format ( long dateTime ) ;
}
// Reflective use of DatatypeConverter avoids a compile-time dependency on the java.xml.bind module in Java 9
static class JaxbDateTimeFormatter implements FormatterImpl {
private static final Method DATATYPE_CONVERTER_PARSE_DATE_TIME_METHOD ;
private static final Method DATATYPE_CONVERTER_PRINT_DATE_TIME_METHOD ;
static {
try {
DATATYPE_CONVERTER_PARSE_DATE_TIME_METHOD = Class . forName ( "jakarta.xml.bind.DatatypeConverter" )
. getDeclaredMethod ( "parseDateTime" , String . class ) ;
DATATYPE_CONVERTER_PRINT_DATE_TIME_METHOD = Class . forName ( "jakarta.xml.bind.DatatypeConverter" )
. getDeclaredMethod ( "printDateTime" , Calendar . class ) ;
} catch ( NoSuchMethodException e ) {
throw new ExceptionInInitializerError ( e ) ;
} catch ( ClassNotFoundException e ) {
throw new ExceptionInInitializerError ( e ) ;
}
}
@Override
public long parse ( final String dateTimeString ) {
try {
return ( ( Calendar ) DATATYPE_CONVERTER_PARSE_DATE_TIME_METHOD . invoke ( null , dateTimeString ) ) . getTimeInMillis ( ) ;
} catch ( IllegalAccessException e ) {
throw new IllegalStateException ( e ) ;
} catch ( InvocationTargetException e ) {
throw ( RuntimeException ) e . getCause ( ) ;
}
}
@Override
public String format ( final long dateTime ) {
Calendar calendar = Calendar . getInstance ( ) ;
calendar . setTimeInMillis ( dateTime ) ;
calendar . setTimeZone ( TimeZone . getTimeZone ( "Z" ) ) ;
try {
return ( String ) DATATYPE_CONVERTER_PRINT_DATE_TIME_METHOD . invoke ( null , calendar ) ;
} catch ( IllegalAccessException e ) {
throw new IllegalStateException ( ) ;
} catch ( InvocationTargetException e ) {
throw ( RuntimeException ) e . getCause ( ) ;
}
}
return ZonedDateTime . ofInstant ( Instant . ofEpochMilli ( dateTime ) , ZoneId . of ( "Z" ) ) . format ( ISO_OFFSET_DATE_TIME ) ;
}
static class Java8DateTimeFormatter implements FormatterImpl {
// if running on Java 8 or above then java.time.format.DateTimeFormatter will be available and initialization will
// succeed.
// Otherwise it will fail.
static {
try {
Class . forName ( "java.time.format.DateTimeFormatter" ) ;
} catch ( ClassNotFoundException e ) {
throw new ExceptionInInitializerError ( e ) ;
}
}
@Override
public long parse ( final String dateTimeString ) {
try {
return ISO_OFFSET_DATE_TIME . parse ( dateTimeString , new TemporalQuery < Instant > ( ) {
@Override
public Instant queryFrom ( final TemporalAccessor temporal ) {
return Instant . from ( temporal ) ;
}
} ) . toEpochMilli ( ) ;
} catch ( DateTimeParseException e ) {
throw new IllegalArgumentException ( e . getMessage ( ) ) ;
}
}
@Override
public String format ( final long dateTime ) {
return ZonedDateTime . ofInstant ( Instant . ofEpochMilli ( dateTime ) , ZoneId . of ( "Z" ) ) . format ( ISO_OFFSET_DATE_TIME ) ;
}
private DateTimeFormatter ( ) {
}
private DateTimeFormatter ( ) { }
}