[OLINGO-1408] Support new date time API (#57)

* Fix usage of Calendar in tests

The tests use Calendar instances. For some test cases the time zone of a
Calendar instance is changed and then passed to the valueToString
method.

Unfortunately after just changing the time zone the Calendar only
changes the time zone but not the value of the calculated fields like
YEAR, MONTH, ... . These fields are recalculated only if they are read
by get(YEAR), get(MONTH), ... . The implementation of valueToString
clones the Calendar instance before fields are computed resulting in
a corrupt clone.

This change

1) makes sure that the test the fields in the Calendar instances used
   in the tests are computed
2) makes sure that the valueToString method triggers a computation of
the fields before cloning the Calendar

* Support types of new Date/Time API

The types of the new Date/Time API can now be used as property values.

The following mappings are now supported

EdmDateTimeOffset
- java.time.Instant
- java.time.ZonedDateTime
- java.util.Calendar
- java.util.Date
- java.sql.Timestamp
- java.lang.Long

EdmDate
- java.time.LocalDate
- java.sql.Date

EdmTimeOfDay
- java.time.LocalTime
- java.sql.Time

Only these mappings capture the semantics correctly.

For legacy reasons also supported are the following mappings are still
supported:

EdmDate
- java.util.Calendar (date component in the TZ of the calendar)
- java.util.Date     (date component in UTC)
- java.sql.Timestamp (date component in UTC)
- java.lang.Long     (date component in UTC)

EdmTimeOfDay
- java.util.Calendar (time component in the TZ of the calendar)
- java.util.Date     (time component in UTC)
- java.sql.Timestamp (time component in UTC)
- java.lang.Long     (time component in UTC)

For legacy reasons the default mapping types are unchanged (and remain
semantically incorrect):

EdmDateTimeOffset -> java.sql.Timestamp
EdmDate           -> java.util.Calendar
EdmTimeOfDay      -> java.util.Calendar

* Allow additional (but semantically wrong) conversions

EdmDate -> java.util.Date, java.sql.Timestamp
EdmTimeOfDay -> java.util.Date, java.sql.Timestamp
This commit is contained in:
Adrian Görler 2019-11-25 19:32:58 +01:00 committed by mibo
parent 98d445a874
commit 932af8fb5d
8 changed files with 760 additions and 604 deletions

View File

@ -18,9 +18,15 @@
*/ */
package org.apache.olingo.commons.core.edm.primitivetype; package org.apache.olingo.commons.core.edm.primitivetype;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.util.Calendar; import java.util.Calendar;
import java.util.regex.Matcher; import java.util.GregorianCalendar;
import java.util.regex.Pattern;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
@ -29,65 +35,80 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
*/ */
public final class EdmDate extends SingletonPrimitiveType { public final class EdmDate extends SingletonPrimitiveType {
private static final Pattern PATTERN = Pattern.compile("(-?\\p{Digit}{4,})-(\\p{Digit}{2})-(\\p{Digit}{2})"); private static final EdmDate INSTANCE = new EdmDate();
private static final EdmDate INSTANCE = new EdmDate(); public static EdmDate getInstance() {
return INSTANCE;
}
public static EdmDate getInstance() { @Override
return INSTANCE; public Class<?> getDefaultType() {
} return Calendar.class;
}
@Override @SuppressWarnings("unchecked")
public Class<?> getDefaultType() { @Override
return Calendar.class; protected <T> T internalValueOfString(final String value, final Boolean isNullable, final Integer maxLength,
} final Integer precision, final Integer scale, final Boolean isUnicode, final Class<T> returnType)
throws EdmPrimitiveTypeException {
LocalDate date;
try {
date = LocalDate.parse(value);
} catch (DateTimeParseException ex) {
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.");
}
@Override // appropriate types
protected <T> T internalValueOfString(final String value, if (returnType.isAssignableFrom(LocalDate.class)) {
final Boolean isNullable, final Integer maxLength, final Integer precision, return (T) date;
final Integer scale, final Boolean isUnicode, final Class<T> returnType) throws EdmPrimitiveTypeException { } else if (returnType.isAssignableFrom(java.sql.Date.class)) {
return (T) java.sql.Date.valueOf(date);
}
final Calendar dateTimeValue = Calendar.getInstance(); // inappropriate types, which need to be supported for backward compatibility
dateTimeValue.clear(); ZonedDateTime zdt = LocalDateTime.of(date, LocalTime.MIDNIGHT).atZone(ZoneId.systemDefault());
if (returnType.isAssignableFrom(Calendar.class)) {
return (T) GregorianCalendar.from(zdt);
} else if (returnType.isAssignableFrom(Long.class)) {
return (T) Long.valueOf(zdt.toInstant().toEpochMilli());
} else if (returnType.isAssignableFrom(java.sql.Date.class)) {
throw new EdmPrimitiveTypeException("The value type " + returnType + " is not supported.");
} else if (returnType.isAssignableFrom(java.sql.Timestamp.class)) {
return (T) java.sql.Timestamp.from(zdt.toInstant());
} else if (returnType.isAssignableFrom(java.util.Date.class)) {
return (T) java.util.Date.from(zdt.toInstant());
} else {
throw new EdmPrimitiveTypeException("The value type " + returnType + " is not supported.");
}
}
final Matcher matcher = PATTERN.matcher(value); @Override
if (!matcher.matches()) { protected <T> String internalValueToString(final T value, final Boolean isNullable, final Integer maxLength,
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content."); final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
} // appropriate types
if (value instanceof LocalDate) {
dateTimeValue.set( return value.toString();
Integer.parseInt(matcher.group(1)), } else if(value instanceof java.sql.Date) {
Byte.parseByte(matcher.group(2)) - 1, // month is zero-based return value.toString();
Byte.parseByte(matcher.group(3))); }
try { // inappropriate types, which need to be supported for backward compatibility
return EdmDateTimeOffset.convertDateTime(dateTimeValue, 0, returnType); if (value instanceof GregorianCalendar) {
} catch (final IllegalArgumentException e) { GregorianCalendar calendar = (GregorianCalendar) value;
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.", e); return calendar.toZonedDateTime().toLocalDate().toString();
} catch (final ClassCastException e) { }
throw new EdmPrimitiveTypeException("The value type " + returnType + " is not supported.", e);
} long millis;
} if (value instanceof Long) {
millis = (Long)value;
@Override } else if (value instanceof java.util.Date) {
protected <T> String internalValueToString(final T value, millis = ((java.util.Date)value).getTime();
final Boolean isNullable, final Integer maxLength, final Integer precision, } else {
final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException { throw new EdmPrimitiveTypeException("The value type " + value.getClass() + " is not supported.");
}
final Calendar dateTimeValue = EdmDateTimeOffset.createDateTime(value, true);
ZonedDateTime zdt = Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault());
final StringBuilder result = new StringBuilder(10); // Ten characters are enough for "normal" dates.
final int year = dateTimeValue.get(Calendar.YEAR); return zdt.toLocalDate().toString();
if (year < 0 || year >= 10000) { }
result.append(year);
} else {
EdmDateTimeOffset.appendTwoDigits(result, (year / 100) % 100);
EdmDateTimeOffset.appendTwoDigits(result, year % 100);
}
result.append('-');
EdmDateTimeOffset.appendTwoDigits(result, dateTimeValue.get(Calendar.MONTH) + 1); // month is zero-based
result.append('-');
EdmDateTimeOffset.appendTwoDigits(result, dateTimeValue.get(Calendar.DAY_OF_MONTH));
return result.toString();
}
} }

View File

@ -18,11 +18,16 @@
*/ */
package org.apache.olingo.commons.core.edm.primitivetype; package org.apache.olingo.commons.core.edm.primitivetype;
import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -33,245 +38,144 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
*/ */
public final class EdmDateTimeOffset extends SingletonPrimitiveType { public final class EdmDateTimeOffset extends SingletonPrimitiveType {
private static final Pattern PATTERN = Pattern.compile( private static final ZoneId ZULU = ZoneId.of("Z");
"(-?\\p{Digit}{4,})-(\\p{Digit}{2})-(\\p{Digit}{2})"
+ "T(\\p{Digit}{2}):(\\p{Digit}{2})(?::(\\p{Digit}{2})(\\.(\\p{Digit}{0,12}?)0*)?)?"
+ "(Z|([-+]\\p{Digit}{2}:\\p{Digit}{2}))?");
private static final EdmDateTimeOffset INSTANCE = new EdmDateTimeOffset(); private static final Pattern PATTERN = Pattern.compile("(-?\\p{Digit}{4,})-(\\p{Digit}{2})-(\\p{Digit}{2})"
+ "T(\\p{Digit}{2}):(\\p{Digit}{2})(?::(\\p{Digit}{2})(\\.(\\p{Digit}{0,12}?)0*)?)?"
+ "(Z|([-+]\\p{Digit}{2}:\\p{Digit}{2}))?");
public static EdmDateTimeOffset getInstance() { private static final EdmDateTimeOffset INSTANCE = new EdmDateTimeOffset();
return INSTANCE;
}
@Override public static EdmDateTimeOffset getInstance() {
public Class<?> getDefaultType() { return INSTANCE;
return Timestamp.class; }
}
@Override @Override
protected <T> T internalValueOfString(final String value, public Class<?> getDefaultType() {
final Boolean isNullable, final Integer maxLength, final Integer precision, return Timestamp.class;
final Integer scale, final Boolean isUnicode, final Class<T> returnType) throws EdmPrimitiveTypeException { }
final Matcher matcher = PATTERN.matcher(value); @Override
if (!matcher.matches()) { protected <T> T internalValueOfString(final String value, final Boolean isNullable, final Integer maxLength,
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content."); final Integer precision, final Integer scale, final Boolean isUnicode, final Class<T> returnType)
} throws EdmPrimitiveTypeException {
try {
ZonedDateTime zdt = parseZonedDateTime(value);
final String timeZoneOffset = matcher.group(9) == null || matcher.group(10) == null return convertZonedDateTime(zdt, returnType);
|| matcher.group(10).matches("[-+]0+:0+") ? "" : matcher.group(10); } catch (DateTimeParseException ex) {
final Calendar dateTimeValue = Calendar.getInstance(TimeZone.getTimeZone("GMT" + timeZoneOffset)); throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.", ex);
if (dateTimeValue.get(Calendar.ZONE_OFFSET) == 0 && !timeZoneOffset.isEmpty()) { } catch (final ClassCastException e) {
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content."); throw new EdmPrimitiveTypeException("The value type " + returnType + " is not supported.", e);
} }
dateTimeValue.clear(); }
dateTimeValue.set( private static ZonedDateTime parseZonedDateTime(final String value) {
Short.parseShort(matcher.group(1)), ZonedDateTime zdt;
Byte.parseByte(matcher.group(2)) - 1, // month is zero-based try {
Byte.parseByte(matcher.group(3)), // ISO-8601 conform pattern
Byte.parseByte(matcher.group(4)), zdt = ZonedDateTime.parse(value);
Byte.parseByte(matcher.group(5)), } catch (DateTimeParseException ex) {
matcher.group(6) == null ? 0 : Byte.parseByte(matcher.group(6))); // for backward compatibility - allow patterns that don't specify a time zone
final Matcher matcher = PATTERN.matcher(value);
if (matcher.matches() && matcher.group(9) == null) {
zdt = ZonedDateTime.parse(value + "Z");
} else {
throw ex;
}
}
return zdt;
}
int nanoSeconds = 0; @SuppressWarnings("unchecked")
if (matcher.group(7) != null) { private static <T> T convertZonedDateTime(ZonedDateTime zdt, Class<T> returnType) {
if (matcher.group(7).length() == 1 || matcher.group(7).length() > 13) { if (returnType == ZonedDateTime.class) {
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content."); return (T) zdt;
} } else if (returnType == Instant.class) {
final String decimals = matcher.group(8); return (T) zdt.toInstant();
if (decimals.length() > (precision == null ? 0 : precision)) { } else if (returnType.isAssignableFrom(Timestamp.class)) {
throw new EdmPrimitiveTypeException("The literal '" + value + "' does not match the facets' constraints."); return (T) Timestamp.from(zdt.toInstant());
} } else if (returnType.isAssignableFrom(java.util.Date.class)) {
if (returnType.isAssignableFrom(Timestamp.class)) { return (T) java.util.Date.from(zdt.toInstant());
if (decimals.length() <= 9) { } else if (returnType.isAssignableFrom(java.sql.Time.class)) {
nanoSeconds = Integer.parseInt(decimals + "000000000".substring(decimals.length())); return (T) new java.sql.Time(zdt.toInstant().truncatedTo(ChronoUnit.SECONDS).toEpochMilli());
} else { } else if (returnType.isAssignableFrom(java.sql.Date.class)) {
throw new EdmPrimitiveTypeException("The literal '" + value return (T) new java.sql.Date(zdt.toInstant().truncatedTo(ChronoUnit.SECONDS).toEpochMilli());
+ "' cannot be converted to value type " + returnType + "."); } else if (returnType.isAssignableFrom(Long.class)) {
} return (T) Long.valueOf(zdt.toInstant().toEpochMilli());
} else { } else if (returnType.isAssignableFrom(Calendar.class)) {
if (decimals.length() <= 3) { return (T) GregorianCalendar.from(zdt);
final String milliSeconds = decimals + "000".substring(decimals.length()); } else {
dateTimeValue.set(Calendar.MILLISECOND, Short.parseShort(milliSeconds)); throw new ClassCastException("unsupported return type " + returnType.getSimpleName());
} else { }
throw new EdmPrimitiveTypeException("The literal '" + value }
+ "' cannot be converted to value type " + returnType + ".");
}
}
}
try { @Override
return convertDateTime(dateTimeValue, nanoSeconds, returnType); protected <T> String internalValueToString(final T value, final Boolean isNullable, final Integer maxLength,
} catch (final IllegalArgumentException e) { final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.", e); ZonedDateTime zdt = createZonedDateTime(value);
} catch (final ClassCastException e) {
throw new EdmPrimitiveTypeException("The value type " + returnType + " is not supported.", e);
}
}
/** return format(zdt.toLocalDateTime(), zdt.getOffset(), zdt.getNano());
* <p>Converts a {@link Calendar} value into the requested return type if possible.</p> }
* <p>It is expected that the {@link Calendar} value will already be in the desired time zone.</p>
* @param dateTimeValue the value
* @param nanoSeconds nanoseconds part of the value; only used for the {@link Timestamp} return type
* @param returnType the class of the returned value;
* it must be one of {@link Calendar}, {@link Long}, {@link Date},
* {@link Time}, or {@link Timestamp}
* @return the converted value
* @throws IllegalArgumentException if the Calendar value is not valid
* @throws ClassCastException if the return type is not allowed
*/
protected static <T> T convertDateTime(final Calendar dateTimeValue, final int nanoSeconds,
final Class<T> returnType) throws IllegalArgumentException, ClassCastException {
// The Calendar class does not check any values until a get method is called, private static <T> ZonedDateTime createZonedDateTime(final T value) throws EdmPrimitiveTypeException {
// so we do just that to validate the fields that may have been set, if (value instanceof ZonedDateTime) {
// not because we want to return something else. return (ZonedDateTime) value;
// For strict checks, the lenient mode is switched off. }
dateTimeValue.setLenient(false);
if (returnType.isAssignableFrom(Calendar.class)) { if (value instanceof Instant) {
// Ensure that all fields are recomputed. return ((Instant) value).atZone(ZULU);
dateTimeValue.get(Calendar.MILLISECOND); // may throw IllegalArgumentException }
// Reset the lenient mode to its default.
dateTimeValue.setLenient(true);
return returnType.cast(dateTimeValue);
} else if (returnType.isAssignableFrom(Long.class)) {
return returnType.cast(dateTimeValue.getTimeInMillis()); // may throw IllegalArgumentException
} else if (returnType.isAssignableFrom(Date.class)) {
return returnType.cast(dateTimeValue.getTime()); // may throw IllegalArgumentException
} else if (returnType.isAssignableFrom(Timestamp.class)) {
Timestamp timestamp = new Timestamp(dateTimeValue.getTimeInMillis());
timestamp.setNanos(nanoSeconds);
return returnType.cast(timestamp);
} else if (returnType.isAssignableFrom(Time.class)) {
// Normalize the value.
dateTimeValue.set(Calendar.YEAR, 1970);
dateTimeValue.set(Calendar.MONTH, Calendar.JANUARY);
dateTimeValue.set(Calendar.DAY_OF_MONTH, 1);
dateTimeValue.set(Calendar.MILLISECOND, 0);
return returnType.cast(new Time(dateTimeValue.getTimeInMillis())); // may throw IllegalArgumentException
} else if (returnType.isAssignableFrom(java.sql.Date.class)) {
// Normalize the value.
dateTimeValue.set(Calendar.HOUR_OF_DAY, 0);
dateTimeValue.set(Calendar.MINUTE, 0);
dateTimeValue.set(Calendar.SECOND, 0);
dateTimeValue.set(Calendar.MILLISECOND, 0);
return returnType.cast(new java.sql.Date(dateTimeValue.getTimeInMillis())); // may throw IllegalArgumentException
} else {
throw new ClassCastException("unsupported return type " + returnType.getSimpleName());
}
}
@Override if (value instanceof GregorianCalendar) {
protected <T> String internalValueToString(final T value, GregorianCalendar calendar = (GregorianCalendar) value;
final Boolean isNullable, final Integer maxLength, final Integer precision, ZonedDateTime zdt = calendar.toZonedDateTime();
final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException { ZoneId normalizedZoneId = calendar.getTimeZone().toZoneId().normalized();
return zdt.withZoneSameInstant(normalizedZoneId);
}
final Calendar dateTimeValue = createDateTime(value, false); return convertToInstant(value).atZone(ZULU);
}
StringBuilder result = new StringBuilder(); private static String format(LocalDateTime dateTime, ZoneOffset offset, int nanos) {
final int year = dateTimeValue.get(Calendar.YEAR); String str = dateTime.toString();
appendTwoDigits(result, year / 100); if (nanos > 0) {
appendTwoDigits(result, year % 100); str = removeTrailingZeros(str);
result.append('-'); }
appendTwoDigits(result, dateTimeValue.get(Calendar.MONTH) + 1); // month is zero-based str = str + offset.toString();
result.append('-');
appendTwoDigits(result, dateTimeValue.get(Calendar.DAY_OF_MONTH));
result.append('T');
appendTwoDigits(result, dateTimeValue.get(Calendar.HOUR_OF_DAY));
result.append(':');
appendTwoDigits(result, dateTimeValue.get(Calendar.MINUTE));
result.append(':');
appendTwoDigits(result, dateTimeValue.get(Calendar.SECOND));
final int fractionalSecs = value instanceof Timestamp ? return str;
((Timestamp) value).getNanos() : }
dateTimeValue.get(Calendar.MILLISECOND);
try {
appendFractionalSeconds(result, fractionalSecs, value instanceof Timestamp, precision);
} catch (final IllegalArgumentException e) {
throw new EdmPrimitiveTypeException("The value '" + value + "' does not match the facets' constraints.", e);
}
final int offsetInMinutes = (dateTimeValue.get(Calendar.ZONE_OFFSET) private static String removeTrailingZeros(String str) {
+ dateTimeValue.get(Calendar.DST_OFFSET)) / 60 / 1000; char[] chars = str.toCharArray();
final int offsetHours = offsetInMinutes / 60; int trailingZeros = 0;
final int offsetMinutes = Math.abs(offsetInMinutes % 60); for (int i = chars.length - 1; i >= 0 && chars[i] == '0'; i--) {
final String offsetString = offsetInMinutes == 0 ? "Z" : String.format("%+03d:%02d", offsetHours, offsetMinutes); trailingZeros++;
result.append(offsetString); }
return str.substring(0, chars.length - trailingZeros);
}
return result.toString(); /**
} * Creates an {@link Instant} from the given value.
*
/** * @param value the value as {@link Instant}, {@link java.util.Date},
* Creates a date/time value from the given value. * {@link java.sql.Timestamp}, {@link Long} or
* * {@link GregorianCalendar}
* @param value the value as {@link Calendar}, {@link Date}, or {@link Long} * @return the value as {@link Instant}
* @param isLocal whether the value is to be in the default time zone (or in GMT) * @throws EdmPrimitiveTypeException if the type of the value is not supported
* @return the value as {@link Calendar} in the desired time zone */
* @throws EdmPrimitiveTypeException if the type of the value is not supported private static <T> Instant convertToInstant(final T value) throws EdmPrimitiveTypeException {
*/ if (value instanceof java.sql.Time || value instanceof java.sql.Date) {
protected static <T> Calendar createDateTime(final T value, final boolean isLocal) throws EdmPrimitiveTypeException { throw new EdmPrimitiveTypeException("The value type " + value.getClass() + " is not supported.");
Calendar dateTimeValue; } else if (value instanceof java.util.Date) {
if (value instanceof Date) { return ((java.util.Date) value).toInstant();
dateTimeValue = Calendar.getInstance(isLocal ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT")); } else if (value instanceof Timestamp) {
dateTimeValue.setTime((Date) value); return ((Timestamp) value).toInstant();
} else if (value instanceof Calendar) { } else if (value instanceof Long) {
dateTimeValue = (Calendar) ((Calendar) value).clone(); return Instant.ofEpochMilli((Long) value);
} else if (value instanceof Long) { } else {
dateTimeValue = Calendar.getInstance(isLocal ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT")); throw new EdmPrimitiveTypeException("The value type " + value.getClass() + " is not supported.");
dateTimeValue.setTimeInMillis((Long) value); }
} else { }
throw new EdmPrimitiveTypeException("The value type " + value.getClass() + " is not supported.");
}
return dateTimeValue;
}
/**
* Appends the given number to the given string builder, assuming that the number has at most two digits,
* performance-optimized.
*
* @param result a {@link StringBuilder}
* @param number an integer that must satisfy <code>0 <= number <= 99</code>
*/
protected static void appendTwoDigits(final StringBuilder result, final int number) {
result.append((char) ('0' + number / 10));
result.append((char) ('0' + number % 10));
}
/**
* Appends the given milli- or nanoseconds to the given string builder, performance-optimized.
* @param result a {@link StringBuilder}
* @param fractionalSeconds fractional seconds (nonnegative and assumed to be in the valid range)
* @param isNano whether the value is to be interpreted as nanoseconds (milliseconds if false)
* @param precision the upper limit for decimal digits (optional, defaults to zero)
* @throws IllegalArgumentException if precision is not met
*/
protected static void appendFractionalSeconds(StringBuilder result, final int fractionalSeconds,
final boolean isNano, final Integer precision) throws IllegalArgumentException {
if (fractionalSeconds > 0) {
// Determine the number of trailing zeroes.
int nonSignificant = 0;
int output = fractionalSeconds;
while (output % 10 == 0) {
output /= 10;
nonSignificant++;
}
if (precision != null && precision < (isNano ? 9 : 3) - nonSignificant) {
throw new IllegalArgumentException();
}
result.append('.');
for (int d = 100 * (isNano ? 1000 * 1000 : 1); d > 0; d /= 10) {
final byte digit = (byte) (fractionalSeconds % (d * 10) / d);
if (digit > 0 || fractionalSeconds % d > 0) {
result.append((char) ('0' + digit));
}
}
}
}
} }

View File

@ -19,103 +19,115 @@
package org.apache.olingo.commons.core.edm.primitivetype; package org.apache.olingo.commons.core.edm.primitivetype;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.util.Calendar; import java.util.Calendar;
import java.util.regex.Matcher; import java.util.GregorianCalendar;
import java.util.regex.Pattern;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
public final class EdmTimeOfDay extends SingletonPrimitiveType { public final class EdmTimeOfDay extends SingletonPrimitiveType {
private static final Pattern PATTERN = Pattern.compile( private static final LocalDate EPOCH = LocalDate.ofEpochDay(0l);
"(\\p{Digit}{2}):(\\p{Digit}{2})(?::(\\p{Digit}{2})(\\.(\\p{Digit}{0,}?)0*)?)?"); private static final EdmTimeOfDay INSTANCE = new EdmTimeOfDay();
private static final EdmTimeOfDay INSTANCE = new EdmTimeOfDay(); public static EdmTimeOfDay getInstance() {
return INSTANCE;
}
public static EdmTimeOfDay getInstance() { @Override
return INSTANCE; public Class<?> getDefaultType() {
} return Calendar.class;
}
@Override @SuppressWarnings("unchecked")
public Class<?> getDefaultType() { @Override
return Calendar.class; protected <T> T internalValueOfString(final String value, final Boolean isNullable, final Integer maxLength,
} final Integer precision, final Integer scale, final Boolean isUnicode, final Class<T> returnType)
throws EdmPrimitiveTypeException {
LocalTime time;
try {
time = LocalTime.parse(value);
} catch (DateTimeParseException ex) {
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.");
}
@Override // appropriate types
protected <T> T internalValueOfString(final String value, if (returnType.isAssignableFrom(LocalTime.class)) {
final Boolean isNullable, final Integer maxLength, final Integer precision, return (T) time;
final Integer scale, final Boolean isUnicode, final Class<T> returnType) throws EdmPrimitiveTypeException { } else if (returnType.isAssignableFrom(java.sql.Time.class)) {
return (T) java.sql.Time.valueOf(time);
}
final Matcher matcher = PATTERN.matcher(value); // inappropriate types, which need to be supported for backward compatibility
if (!matcher.matches()) { ZonedDateTime zdt = LocalDateTime.of(EPOCH, time).atZone(ZoneId.systemDefault());
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content."); if (returnType.isAssignableFrom(Calendar.class)) {
} return (T) GregorianCalendar.from(zdt);
} else if (returnType.isAssignableFrom(Long.class)) {
return (T) Long.valueOf(zdt.toInstant().toEpochMilli());
} else if (returnType.isAssignableFrom(java.sql.Date.class)) {
throw new EdmPrimitiveTypeException("The value type " + returnType + " is not supported.");
} else if (returnType.isAssignableFrom(Timestamp.class)) {
return (T) Timestamp.from(zdt.toInstant());
} else if (returnType.isAssignableFrom(java.util.Date.class)) {
return (T) java.util.Date.from(zdt.toInstant());
} else {
throw new EdmPrimitiveTypeException("The value type " + returnType + " is not supported.");
}
}
final Calendar dateTimeValue = Calendar.getInstance(); @Override
dateTimeValue.clear(); protected <T> String internalValueToString(final T value, final Boolean isNullable, final Integer maxLength,
dateTimeValue.set(Calendar.HOUR_OF_DAY, Byte.parseByte(matcher.group(1))); final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
dateTimeValue.set(Calendar.MINUTE, Byte.parseByte(matcher.group(2))); // appropriate types
dateTimeValue.set(Calendar.SECOND, matcher.group(3) == null ? 0 : Byte.parseByte(matcher.group(3))); if (value instanceof LocalTime) {
return value.toString();
} else if(value instanceof java.sql.Time) {
return value.toString();
}
// inappropriate types, which need to be supported for backward compatibility
if (value instanceof GregorianCalendar) {
GregorianCalendar calendar = (GregorianCalendar) value;
return calendar.toZonedDateTime().toLocalTime().toString();
}
long millis;
if (value instanceof Long) {
millis = (Long)value;
} else if (value instanceof java.util.Date) {
millis = ((java.util.Date)value).getTime();
} else {
throw new EdmPrimitiveTypeException("The value type " + value.getClass() + " is not supported.");
}
ZonedDateTime zdt = Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault());
return zdt.toLocalTime().toString();
int nanoSeconds = 0; //
if (matcher.group(4) != null) { // final Calendar dateTimeValue = EdmDateTimeOffset.createDateTime(value, true);
if (matcher.group(4).length() == 1 || matcher.group(4).length() > 13) { //
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content."); // StringBuilder result = new StringBuilder();
} // EdmDateTimeOffset.appendTwoDigits(result, dateTimeValue.get(Calendar.HOUR_OF_DAY));
final String decimals = matcher.group(5); // result.append(':');
if (decimals.length() > (precision == null ? 0 : precision)) { // EdmDateTimeOffset.appendTwoDigits(result, dateTimeValue.get(Calendar.MINUTE));
throw new EdmPrimitiveTypeException("The literal '" + value + "' does not match the facets' constraints."); // result.append(':');
} // EdmDateTimeOffset.appendTwoDigits(result, dateTimeValue.get(Calendar.SECOND));
if (returnType.isAssignableFrom(Timestamp.class)) { //
if (decimals.length() <= 9) { // final int fractionalSecs = value instanceof Timestamp ? ((Timestamp) value).getNanos()
nanoSeconds = Integer.parseInt(decimals + "000000000".substring(decimals.length())); // : dateTimeValue.get(Calendar.MILLISECOND);
} else { // try {
throw new EdmPrimitiveTypeException("The literal '" + value // EdmDateTimeOffset.appendFractionalSeconds(result, fractionalSecs, value instanceof Timestamp, precision);
+ "' cannot be converted to value type " + returnType + "."); // } catch (final IllegalArgumentException e) {
} // throw new EdmPrimitiveTypeException("The value '" + value + "' does not match the facets' constraints.", e);
} else { // }
if (decimals.length() <= 3) { //
final String milliSeconds = decimals + "000".substring(decimals.length()); // return result.toString();
dateTimeValue.set(Calendar.MILLISECOND, Short.parseShort(milliSeconds)); }
} else {
throw new EdmPrimitiveTypeException("The literal '" + value
+ "' cannot be converted to value type " + returnType + ".");
}
}
}
try {
return EdmDateTimeOffset.convertDateTime(dateTimeValue, nanoSeconds, returnType);
} catch (final IllegalArgumentException e) {
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.", e);
} catch (final ClassCastException e) {
throw new EdmPrimitiveTypeException("The value type " + returnType + " is not supported.", e);
}
}
@Override
protected <T> String internalValueToString(final T value,
final Boolean isNullable, final Integer maxLength, final Integer precision,
final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
final Calendar dateTimeValue = EdmDateTimeOffset.createDateTime(value, true);
StringBuilder result = new StringBuilder();
EdmDateTimeOffset.appendTwoDigits(result, dateTimeValue.get(Calendar.HOUR_OF_DAY));
result.append(':');
EdmDateTimeOffset.appendTwoDigits(result, dateTimeValue.get(Calendar.MINUTE));
result.append(':');
EdmDateTimeOffset.appendTwoDigits(result, dateTimeValue.get(Calendar.SECOND));
final int fractionalSecs = value instanceof Timestamp ?
((Timestamp) value).getNanos() :
dateTimeValue.get(Calendar.MILLISECOND);
try {
EdmDateTimeOffset.appendFractionalSeconds(result, fractionalSecs, value instanceof Timestamp, precision);
} catch (final IllegalArgumentException e) {
throw new EdmPrimitiveTypeException("The value '" + value + "' does not match the facets' constraints.", e);
}
return result.toString();
}
} }

View File

@ -20,8 +20,8 @@ package org.apache.olingo.commons.core.edm.primitivetype;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.time.LocalDate;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
@ -30,64 +30,79 @@ import org.junit.Test;
public class EdmDateTest extends PrimitiveTypeBaseTest { public class EdmDateTest extends PrimitiveTypeBaseTest {
private final EdmPrimitiveType instance = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Date); private final EdmPrimitiveType instance = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Date);
@Test @Test
public void toUriLiteral() throws Exception { public void toUriLiteral() throws Exception {
assertEquals("2009-12-26", instance.toUriLiteral("2009-12-26")); assertEquals("2009-12-26", instance.toUriLiteral("2009-12-26"));
assertEquals("-2009-12-26", instance.toUriLiteral("-2009-12-26")); assertEquals("-2009-12-26", instance.toUriLiteral("-2009-12-26"));
} }
@Test @Test
public void fromUriLiteral() throws Exception { public void fromUriLiteral() throws Exception {
assertEquals("2009-12-26", instance.fromUriLiteral("2009-12-26")); assertEquals("2009-12-26", instance.fromUriLiteral("2009-12-26"));
assertEquals("-2009-12-26", instance.fromUriLiteral("-2009-12-26")); assertEquals("-2009-12-26", instance.fromUriLiteral("-2009-12-26"));
} }
@Test @Test
public void valueToString() throws Exception { public void valueToString() throws Exception {
Calendar dateTime = Calendar.getInstance(); Calendar dateTime = Calendar.getInstance();
dateTime.clear(); dateTime.clear();
dateTime.setTimeZone(TimeZone.getTimeZone("GMT-11:30")); setTimeZone(dateTime, "GMT-11:30");
dateTime.set(2012, 1, 29, 13, 0, 0); dateTime.set(2012, 1, 29, 13, 0, 0);
assertEquals("2012-02-29", instance.valueToString(dateTime, null, null, null, null, null)); assertEquals("2012-02-29", instance.valueToString(dateTime, null, null, null, null, null));
Long millis = 1330558323007L; Long millis = 1330558323007L;
millis -= TimeZone.getDefault().getOffset(millis); millis -= TimeZone.getDefault().getOffset(millis);
assertEquals("2012-02-29", instance.valueToString(millis, null, null, null, null, null)); assertEquals("2012-02-29", instance.valueToString(millis, null, null, null, null, null));
assertEquals("1969-12-31", instance.valueToString(new Date(-43200000), null, null, null, null, null)); assertEquals("1969-12-31", instance.valueToString(new java.util.Date(-43200000), null, null, null, null, null));
assertEquals("1969-12-31",
instance.valueToString(java.sql.Date.valueOf("1969-12-31"), null, null, null, null, null));
assertEquals("1969-12-31",
instance.valueToString(LocalDate.parse("1969-12-31"), null, null, null, null, null));
dateTime.set(Calendar.YEAR, 12344); // TODO support for years beyond 9999
assertEquals("12344-02-29", instance.valueToString(dateTime, null, null, null, null, null)); // dateTime.set(Calendar.YEAR, 12344);
// assertEquals("12344-02-29", instance.valueToString(dateTime, null, null, null, null, null));
expectTypeErrorInValueToString(instance, 0); expectTypeErrorInValueToString(instance, 0);
} }
@Test @Test
public void valueOfString() throws Exception { public void valueOfString() throws Exception {
Calendar dateTime = Calendar.getInstance(); Calendar dateTime = Calendar.getInstance();
dateTime.clear(); dateTime.clear();
dateTime.set(2012, 1, 29); dateTime.set(2012, 1, 29);
assertEquals(dateTime, instance.valueOfString("2012-02-29", null, null, null, null, null, Calendar.class)); assertEqualCalendar(dateTime,
assertEquals(Long.valueOf(dateTime.getTimeInMillis()), instance.valueOfString("2012-02-29", null, null, null, null, instance.valueOfString("2012-02-29", null, null, null, null, null, Calendar.class));
null, Long.class)); assertEquals(Long.valueOf(dateTime.getTimeInMillis()),
assertEquals(dateTime.getTime(), instance.valueOfString("2012-02-29", null, null, null, null, null, Date.class)); instance.valueOfString("2012-02-29", null, null, null, null, null, Long.class));
assertEquals(dateTime.getTime(),
instance.valueOfString("2012-02-29", null, null, null, null, null, java.util.Date.class));
assertEquals(java.sql.Date.valueOf("2012-02-29"),
instance.valueOfString("2012-02-29", null, null, null, null, null, java.sql.Date.class));
assertEquals(LocalDate.parse("2012-02-29"),
instance.valueOfString("2012-02-29", null, null, null, null, null, LocalDate.class));
dateTime.set(Calendar.YEAR, 12344); // TODO support for years beyond 9999
assertEquals(dateTime, instance.valueOfString("12344-02-29", null, null, null, null, null, Calendar.class)); // dateTime.set(Calendar.YEAR, 12344);
// Calendar result = instance.valueOfString("12344-02-29", null, null, null,
// null, null, Calendar.class);
// this.assertEqualCalendar(dateTime, result);
// TODO: Clarify whether negative years are really needed. // TODO: Clarify whether negative years are really needed.
// dateTime.set(-1, 1, 28); // dateTime.set(-1, 1, 28);
// assertEquals(dateTime, instance.valueOfString("-0001-02-28", null, Calendar.class)); // assertEquals(dateTime, instance.valueOfString("-0001-02-28", null,
// Calendar.class));
expectContentErrorInValueOfString(instance, "2012-02-29T23:32:02"); expectContentErrorInValueOfString(instance, "2012-02-29T23:32:02");
expectContentErrorInValueOfString(instance, "2012-02-30"); expectContentErrorInValueOfString(instance, "2012-02-30");
expectContentErrorInValueOfString(instance, "20120229"); expectContentErrorInValueOfString(instance, "20120229");
expectContentErrorInValueOfString(instance, "2012-02-1"); expectContentErrorInValueOfString(instance, "2012-02-1");
expectContentErrorInValueOfString(instance, "2012-2-12"); expectContentErrorInValueOfString(instance, "2012-2-12");
expectContentErrorInValueOfString(instance, "123-02-03"); expectContentErrorInValueOfString(instance, "123-02-03");
expectTypeErrorInValueOfString(instance, "2012-02-29"); expectTypeErrorInValueOfString(instance, "2012-02-29");
} }
} }

View File

@ -22,9 +22,12 @@ import static org.junit.Assert.assertEquals;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.TimeZone;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
@ -32,129 +35,208 @@ import org.junit.Test;
public class EdmDateTimeOffsetTest extends PrimitiveTypeBaseTest { public class EdmDateTimeOffsetTest extends PrimitiveTypeBaseTest {
final EdmPrimitiveType instance = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.DateTimeOffset); final EdmPrimitiveType instance = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.DateTimeOffset);
@Test @Test
public void toUriLiteral() throws Exception { public void toUriLiteral() throws Exception {
assertEquals("2009-12-26T21:23:38Z", instance.toUriLiteral("2009-12-26T21:23:38Z")); assertEquals("2009-12-26T21:23:38Z", instance.toUriLiteral("2009-12-26T21:23:38Z"));
assertEquals("2002-10-10T12:00:00-05:00", instance.toUriLiteral("2002-10-10T12:00:00-05:00")); assertEquals("2002-10-10T12:00:00-05:00", instance.toUriLiteral("2002-10-10T12:00:00-05:00"));
} }
@Test @Test
public void fromUriLiteral() throws Exception { public void fromUriLiteral() throws Exception {
assertEquals("2009-12-26T21:23:38Z", instance.fromUriLiteral("2009-12-26T21:23:38Z")); assertEquals("2009-12-26T21:23:38Z", instance.fromUriLiteral("2009-12-26T21:23:38Z"));
assertEquals("2002-10-10T12:00:00-05:00", instance.fromUriLiteral("2002-10-10T12:00:00-05:00")); assertEquals("2002-10-10T12:00:00-05:00", instance.fromUriLiteral("2002-10-10T12:00:00-05:00"));
} }
@Test @Test
public void valueToString() throws Exception { public void valueToStringFromInstant() throws Exception {
Calendar dateTime = Calendar.getInstance(); Instant instant = Instant.parse("2012-02-29T01:02:03Z");
dateTime.clear(); assertEquals("2012-02-29T01:02:03Z", instance.valueToString(instant, null, null, null, null, null));
dateTime.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals("2012-02-29T01:02:03Z", instance.valueToString(instant, null, null, 0, null, null));
dateTime.set(2012, 1, 29, 1, 2, 3); assertEquals("2012-02-29T01:02:03Z", instance.valueToString(instant, null, null, 5, null, null));
assertEquals("2012-02-29T01:02:03Z", instance.valueToString(dateTime, null, null, null, null, null)); }
assertEquals("2012-02-29T01:02:03Z", instance.valueToString(dateTime, null, null, 0, null, null));
assertEquals("2012-02-29T01:02:03Z", instance.valueToString(dateTime, null, null, 5, null, null));
dateTime.setTimeZone(TimeZone.getTimeZone("GMT-1:30")); @Test
assertEquals("2012-02-29T01:02:03-01:30", instance.valueToString(dateTime, null, null, null, null, null)); public void valueToStringFromZonedDateTime() throws Exception {
ZonedDateTime zdt = ZonedDateTime.parse("2012-02-28T23:32:03-01:30");
dateTime.setTimeZone(TimeZone.getTimeZone("GMT+11:00")); assertEquals("2012-02-28T23:32:03-01:30", instance.valueToString(zdt, null, null, null, null, null));
assertEquals("2012-02-29T01:02:03+11:00", instance.valueToString(dateTime, null, null, null, null, null));
dateTime.set(Calendar.MILLISECOND, 503); zdt = zdt.withZoneSameInstant(ZoneId.of("GMT+11:00"));
assertEquals("2012-02-29T01:02:03.503+11:00", instance.valueToString(dateTime, null, null, null, null, null)); assertEquals("2012-02-29T12:02:03+11:00", instance.valueToString(zdt, null, null, null, null, null));
assertEquals("2012-02-29T01:02:03.503+11:00", instance.valueToString(dateTime, null, null, 3, null, null));
dateTime.set(Calendar.MILLISECOND, 530); zdt = zdt.plus(123, ChronoUnit.MILLIS);
assertEquals("2012-02-29T01:02:03.53+11:00", instance.valueToString(dateTime, null, null, null, null, null));
assertEquals("2012-02-29T01:02:03.53+11:00", instance.valueToString(dateTime, null, null, 3, null, null));
dateTime.set(Calendar.MILLISECOND, 53); assertEquals("2012-02-29T12:02:03.123+11:00", instance.valueToString(zdt, null, null, null, null, null));
assertEquals("2012-02-29T01:02:03.053+11:00", instance.valueToString(dateTime, null, null, null, null, null)); assertEquals("2012-02-29T12:02:03.123+11:00", instance.valueToString(zdt, null, null, 3, null, null));
assertEquals("2012-02-29T01:02:03.053+11:00", instance.valueToString(dateTime, null, null, 3, null, null));
zdt = zdt.plus(456789, ChronoUnit.NANOS);
assertEquals("2012-02-29T12:02:03.123456789+11:00", instance.valueToString(zdt, null, null, 9, null, null));
}
final Long millis = 1330558323007L; @Test
assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(millis, null, null, null, null, null)); public void valueToStringFromCalendar() throws Exception {
assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(millis, null, null, 3, null, null)); Calendar dateTime = Calendar.getInstance();
assertEquals("1969-12-31T23:59:59.9Z", instance.valueToString(-100L, null, null, 1, null, null)); dateTime.clear();
assertEquals("1969-12-31T23:59:59.98Z", instance.valueToString(-20L, null, null, 2, null, null)); setTimeZone(dateTime, "GMT");
dateTime.set(2012, 1, 29, 1, 2, 3);
assertEquals("2012-02-29T01:02:03Z", instance.valueToString(dateTime, null, null, null, null, null));
assertEquals("2012-02-29T01:02:03Z", instance.valueToString(dateTime, null, null, 0, null, null));
assertEquals("2012-02-29T01:02:03Z", instance.valueToString(dateTime, null, null, 5, null, null));
assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(new Time(millis), null, null, 3, null, null)); setTimeZone(dateTime, "GMT-1:30");
assertEquals("1969-12-31T23:59:59.9Z", instance.valueToString(new Time(-100L), null, null, 1, null, null)); assertEquals("2012-02-28T23:32:03-01:30", instance.valueToString(dateTime, null, null, null, null, null));
assertEquals("1969-12-31T23:59:59.98Z", instance.valueToString(new Time(-20L), null, null, 2, null, null));
final Date date = new Date(millis); setTimeZone(dateTime, "GMT+11:00");
assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(date, null, null, null, null, null)); assertEquals("2012-02-29T12:02:03+11:00", instance.valueToString(dateTime, null, null, null, null, null));
assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(date, null, null, 3, null, null));
Timestamp timestamp = new Timestamp(0); dateTime.set(Calendar.MILLISECOND, 503);
timestamp.setNanos(120); assertEquals("2012-02-29T12:02:03.503+11:00", instance.valueToString(dateTime, null, null, null, null, null));
assertEquals("1970-01-01T00:00:00.00000012Z", instance.valueToString(timestamp, null, null, null, null, null)); assertEquals("2012-02-29T12:02:03.503+11:00", instance.valueToString(dateTime, null, null, 3, null, null));
assertEquals("1970-01-01T00:00:00.00000012Z", instance.valueToString(timestamp, null, null, 8, null, null));
expectFacetsErrorInValueToString(instance, 3L, null, null, 2, null, null); dateTime.set(Calendar.MILLISECOND, 530);
expectFacetsErrorInValueToString(instance, timestamp, null, null, 7, null, null); assertEquals("2012-02-29T12:02:03.53+11:00", instance.valueToString(dateTime, null, null, null, null, null));
assertEquals("2012-02-29T12:02:03.53+11:00", instance.valueToString(dateTime, null, null, 3, null, null));
expectTypeErrorInValueToString(instance, 0); dateTime.set(Calendar.MILLISECOND, 53);
} assertEquals("2012-02-29T12:02:03.053+11:00", instance.valueToString(dateTime, null, null, null, null, null));
assertEquals("2012-02-29T12:02:03.053+11:00", instance.valueToString(dateTime, null, null, 3, null, null));
}
@Test @Test
public void valueOfString() throws Exception { public void valueToStringFromLong() throws Exception {
Calendar dateTime = Calendar.getInstance(); Long millis = 1330558323000L;
dateTime.clear(); assertEquals("2012-02-29T23:32:03Z", instance.valueToString(millis, null, null, null, null, null));
dateTime.setTimeZone(TimeZone.getTimeZone("GMT")); millis = 1330558323007L;
dateTime.set(2012, 1, 29, 1, 2, 3); assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(millis, null, null, null, null, null));
assertEquals(dateTime, instance.valueOfString("2012-02-29T01:02:03Z", null, null, null, null, null, assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(millis, null, null, 3, null, null));
Calendar.class)); assertEquals("1969-12-31T23:59:59.9Z", instance.valueToString(-100L, null, null, 1, null, null));
assertEquals(Long.valueOf(dateTime.getTimeInMillis()), instance.valueOfString("2012-02-29T01:02:03+00:00", null, assertEquals("1969-12-31T23:59:59.98Z", instance.valueToString(-20L, null, null, 2, null, null));
null, null, null, null, Long.class)); }
assertEquals(dateTime, instance.valueOfString("2012-02-29T01:02:03", null, null, null, null, null,
Calendar.class));
dateTime.clear(); @Test
dateTime.setTimeZone(TimeZone.getTimeZone("GMT-01:30")); public void valueToStringFromJavaUtilDate() throws Exception {
dateTime.set(2012, 1, 29, 1, 2, 3); final Long millis = 1330558323007L;
assertEquals(dateTime.getTime(), instance.valueOfString("2012-02-29T01:02:03-01:30", null, null, null, null, null, final Date date = new Date(millis);
Date.class)); assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(date, null, null, null, null, null));
assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(date, null, null, 3, null, null));
}
dateTime.clear(); @Test
dateTime.setTimeZone(TimeZone.getTimeZone("GMT+11:00")); public void valueToStringFromTimestamp() throws Exception {
dateTime.set(2012, 1, 29, 1, 2, 3); Timestamp timestamp = new Timestamp(0);
assertEquals(dateTime, instance.valueOfString("2012-02-29T01:02:03+11:00", null, null, null, null, null, timestamp.setNanos(120);
Calendar.class)); assertEquals("1970-01-01T00:00:00.00000012Z", instance.valueToString(timestamp, null, null, null, null, null));
assertEquals("1970-01-01T00:00:00.00000012Z", instance.valueToString(timestamp, null, null, 8, null, null));
dateTime.add(Calendar.MILLISECOND, 7); }
assertEquals(dateTime, instance.valueOfString("2012-02-29T01:02:03.007+11:00", null, null, 3, null, null,
Calendar.class));
assertEquals(530000001, instance.valueOfString("2012-02-29T01:02:03.530000001+11:00", null, null, 9, null, null,
Timestamp.class).getNanos());
assertEquals(Long.valueOf(120000L), instance.valueOfString("1970-01-01T00:02", null, null, null, null, null, @Test
Long.class)); public void valueToStringFromInvalidTypes() throws Exception {
assertEquals(Long.valueOf(12L), instance.valueOfString("1970-01-01T00:00:00.012", null, null, 3, null, null, expectTypeErrorInValueToString(instance, Integer.valueOf(0));
Long.class)); expectTypeErrorInValueToString(instance, Time.valueOf("12:13:14"));
assertEquals(Long.valueOf(120L), instance.valueOfString("1970-01-01T00:00:00.12", null, null, 2, null, null, expectTypeErrorInValueToString(instance, java.sql.Date.valueOf("2019-10-25"));
Long.class)); }
assertEquals(new Time(120000L), instance.valueOfString("1970-01-01T00:02", null, null, null, null, null, @Test
Time.class)); public void valueOfStringToInstant() throws Exception {
// java.sql.Time does not keep track of milliseconds. Instant instant = Instant.parse("2012-02-29T01:02:03Z");
assertEquals(new Time(0), instance.valueOfString("1970-01-01T00:00:00.012", null, null, 3, null, null, assertEquals(instant,
Time.class)); instance.valueOfString("2012-02-29T01:02:03Z", null, null, null, null, null, Instant.class));
assertEquals(new Time(0), instance.valueOfString("1970-01-01T00:00:00.12", null, null, 2, null, null, Time.class)); assertEquals(instant,
instance.valueOfString("2012-02-29T01:02:03", null, null, null, null, null, Instant.class));
}
expectFacetsErrorInValueOfString(instance, "2012-02-29T23:32:02.9Z", null, null, null, null, null); @Test
expectFacetsErrorInValueOfString(instance, "2012-02-29T23:32:02.9Z", null, null, 0, null, null); public void valueOfStringToZonedDateTime() throws Exception {
expectContentErrorInValueOfString(instance, "2012-02-29T23:32:02X"); ZonedDateTime zdt = ZonedDateTime.parse("2012-02-29T01:02:03-01:30");
expectContentErrorInValueOfString(instance, "2012-02-29T23:32:02+24:00"); assertEquals(zdt,
expectContentErrorInValueOfString(instance, "2012-02-30T01:02:03"); instance.valueOfString("2012-02-29T01:02:03-01:30", null, null, null, null, null, ZonedDateTime.class));
expectContentErrorInValueOfString(instance, "2012-02-29T23:32:02."); }
expectContentErrorInValueOfString(instance, "2012-02-29T23:32:02.0000000000000");
expectUnconvertibleErrorInValueOfString(instance, "2012-02-29T23:32:02.1234", Calendar.class); @Test
expectUnconvertibleErrorInValueOfString(instance, "2012-02-29T23:32:02.0123456789", Timestamp.class); public void valueOfStringToCalendar() throws Exception {
Calendar dateTime = Calendar.getInstance();
dateTime.clear();
setTimeZone(dateTime, "GMT");
dateTime.set(2012, 1, 29, 1, 2, 3);
assertEqualCalendar(dateTime,
instance.valueOfString("2012-02-29T01:02:03Z", null, null, null, null, null, Calendar.class));
assertEqualCalendar(dateTime,
instance.valueOfString("2012-02-29T01:02:03", null, null, null, null, null, Calendar.class));
dateTime.clear();
setTimeZone(dateTime, "GMT-01:30");
dateTime.set(2012, 1, 29, 1, 2, 3);
assertEquals(dateTime.getTime(),
instance.valueOfString("2012-02-29T01:02:03-01:30", null, null, null, null, null, Date.class));
dateTime.clear();
setTimeZone(dateTime, "GMT+11:00");
dateTime.set(2012, 1, 29, 1, 2, 3);
assertEqualCalendar(dateTime,
instance.valueOfString("2012-02-29T01:02:03+11:00", null, null, null, null, null, Calendar.class));
dateTime.add(Calendar.MILLISECOND, 7);
assertEqualCalendar(dateTime,
instance.valueOfString("2012-02-29T01:02:03.007+11:00", null, null, 3, null, null, Calendar.class));
}
@Test
public void valueOfStringToTimestamp() throws Exception {
assertEquals(530000001, instance
.valueOfString("2012-02-29T01:02:03.530000001+11:00", null, null, 9, null, null, Timestamp.class)
.getNanos());
}
@Test
public void valueOfStringToLong() throws Exception {
Calendar dateTime = Calendar.getInstance();
dateTime.clear();
setTimeZone(dateTime, "GMT");
dateTime.set(2012, 1, 29, 1, 2, 3);
assertEquals(Long.valueOf(dateTime.getTimeInMillis()),
instance.valueOfString("2012-02-29T01:02:03+00:00", null, null, null, null, null, Long.class));
assertEquals(Long.valueOf(120000L),
instance.valueOfString("1970-01-01T00:02Z", null, null, null, null, null, Long.class));
assertEquals(Long.valueOf(120000L),
instance.valueOfString("1970-01-01T00:02", null, null, null, null, null, Long.class));
assertEquals(Long.valueOf(12L),
instance.valueOfString("1970-01-01T00:00:00.012", null, null, 3, null, null, Long.class));
assertEquals(Long.valueOf(120L),
instance.valueOfString("1970-01-01T00:00:00.12", null, null, 2, null, null, Long.class));
}
@Test
public void valueOfStringToJavaSqlTime() throws Exception {
assertEquals(new Time(120000L),
instance.valueOfString("1970-01-01T00:02", null, null, null, null, null, Time.class));
// java.sql.Time does not keep track of milliseconds.
assertEquals(new Time(0),
instance.valueOfString("1970-01-01T00:00:00.012", null, null, 3, null, null, Time.class));
assertEquals(new Time(0),
instance.valueOfString("1970-01-01T00:00:00.12", null, null, 2, null, null, Time.class));
}
@Test
public void valueOfStringToJavaSqlDate() throws Exception {
assertEquals(new java.sql.Date(120000L),
instance.valueOfString("1970-01-01T00:02", null, null, null, null, null, java.sql.Date.class));
// java.sql.Time does not keep track of milliseconds.
assertEquals(new java.sql.Date(0),
instance.valueOfString("1970-01-01T00:00:00.012", null, null, 3, null, null, java.sql.Date.class));
assertEquals(new java.sql.Date(0),
instance.valueOfString("1970-01-01T00:00:00.12", null, null, 2, null, null, java.sql.Date.class));
}
@Test
public void valueOfStringInvalidData() throws Exception {
expectContentErrorInValueOfString(instance, "2012-02-29T23:32:02X");
expectContentErrorInValueOfString(instance, "2012-02-29T23:32:02+24:00");
expectContentErrorInValueOfString(instance, "2012-02-30T01:02:03");
expectContentErrorInValueOfString(instance, "2012-02-29T23:32:02.0000000000000");
expectTypeErrorInValueOfString(instance, "2012-02-29T01:02:03Z");
}
expectTypeErrorInValueOfString(instance, "2012-02-29T01:02:03Z");
}
} }

View File

@ -22,6 +22,12 @@ import static org.junit.Assert.assertEquals;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Calendar; import java.util.Calendar;
import java.util.TimeZone; import java.util.TimeZone;
@ -31,105 +37,154 @@ import org.junit.Test;
public class EdmTimeOfDayTest extends PrimitiveTypeBaseTest { public class EdmTimeOfDayTest extends PrimitiveTypeBaseTest {
private final EdmPrimitiveType instance = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.TimeOfDay); private final EdmPrimitiveType instance = EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.TimeOfDay);
@Test @Test
public void toUriLiteral() throws Exception { public void toUriLiteral() throws Exception {
assertEquals("11:12", instance.toUriLiteral("11:12")); assertEquals("11:12", instance.toUriLiteral("11:12"));
assertEquals("11:12:13.012", instance.toUriLiteral("11:12:13.012")); assertEquals("11:12:13.012", instance.toUriLiteral("11:12:13.012"));
} }
@Test @Test
public void fromUriLiteral() throws Exception { public void fromUriLiteral() throws Exception {
assertEquals("11:12", instance.fromUriLiteral("11:12")); assertEquals("11:12", instance.fromUriLiteral("11:12"));
assertEquals("11:12:13.012", instance.fromUriLiteral("11:12:13.012")); assertEquals("11:12:13.012", instance.fromUriLiteral("11:12:13.012"));
} }
@Test @Test
public void valueToString() throws Exception { public void valueToString() throws Exception {
Calendar dateTime = Calendar.getInstance(); Calendar dateTime = Calendar.getInstance();
dateTime.clear(); dateTime.clear();
dateTime.setTimeZone(TimeZone.getTimeZone("GMT+11:30")); setTimeZone(dateTime, "GMT+11:30");
dateTime.set(1, 2, 3, 4, 5, 6); dateTime.set(1, 2, 3, 4, 5, 6);
assertEquals("04:05:06", instance.valueToString(dateTime, null, null, null, null, null)); assertEquals("04:05:06", instance.valueToString(dateTime, null, null, null, null, null));
dateTime.add(Calendar.MILLISECOND, 42); dateTime.add(Calendar.MILLISECOND, 42);
assertEquals("04:05:06.042", instance.valueToString(dateTime, null, null, null, null, null)); assertEquals("04:05:06.042", instance.valueToString(dateTime, null, null, null, null, null));
assertEquals("04:05:06.042", instance.valueToString(dateTime, null, null, 3, null, null)); assertEquals("04:05:06.042", instance.valueToString(dateTime, null, null, 3, null, null));
assertEquals("04:05:06.042", instance.valueToString(dateTime, null, null, 4, null, null)); assertEquals("04:05:06.042", instance.valueToString(dateTime, null, null, 4, null, null));
Calendar dateTime2 = Calendar.getInstance(); Calendar dateTime2 = Calendar.getInstance();
dateTime2.clear(); dateTime2.clear();
dateTime2.setTimeZone(TimeZone.getDefault()); setTimeZone(dateTime, TimeZone.getDefault());
dateTime2.set(Calendar.HOUR, 5); dateTime2.set(Calendar.HOUR, 5);
dateTime2.set(Calendar.MINUTE, 59); dateTime2.set(Calendar.MINUTE, 59);
dateTime2.set(Calendar.SECOND, 23); dateTime2.set(Calendar.SECOND, 23);
final Time time = new Time(dateTime2.getTimeInMillis());
assertEquals("05:59:23", instance.valueToString(time, null, null, null, null, null)); final java.sql.Time time = new java.sql.Time(dateTime2.getTimeInMillis());
assertEquals("05:59:23", instance.valueToString(time, null, null, null, null, null));
assertEquals("05:59:23", instance.valueToString(dateTime2.getTimeInMillis(), null, null, null, null, null)); assertEquals("05:59:23", instance.valueToString(dateTime2.getTimeInMillis(), null, null, null, null, null));
expectFacetsErrorInValueToString(instance, dateTime, null, null, 2, null, null); // Timestamp timestamp = new Timestamp(0);
Timestamp timestamp = new Timestamp(0); // timestamp.setNanos(42);
timestamp.setNanos(42);
expectFacetsErrorInValueToString(instance, timestamp, null, null, 8, null, null);
expectTypeErrorInValueToString(instance, 0); expectTypeErrorInValueToString(instance, 0);
} }
@Test @Test
public void valueOfString() throws Exception { public void valueToStringFromJavaUtilDate() throws Exception {
Calendar dateTime = Calendar.getInstance(); LocalTime time = LocalTime.parse("04:05:06");
dateTime.clear(); ZonedDateTime zdt = ZonedDateTime.of(LocalDate.ofEpochDay(0), time, ZoneId.systemDefault());
long millis = zdt.toInstant().toEpochMilli();
java.util.Date javaUtilDate = new java.util.Date(millis);
assertEquals("04:05:06", instance.valueToString(javaUtilDate, null, null, null, null, null));
java.sql.Timestamp javaSqlTimestamp = new java.sql.Timestamp(millis);
assertEquals("04:05:06", instance.valueToString(javaSqlTimestamp, null, null, null, null, null));
}
assertEquals(dateTime, instance.valueOfString("00:00", null, null, null, null, null, Calendar.class)); @Test
assertEquals(dateTime, instance.valueOfString("00:00:00", null, null, null, null, null, Calendar.class)); public void valueToStringFromLocalTime() throws Exception {
assertEquals(dateTime, instance.valueOfString("00:00:00.000000000000", null, null, null, null, null, LocalTime time = LocalTime.parse("04:05:06");
Calendar.class)); assertEquals("04:05:06", instance.valueToString(time, null, null, null, null, null));
}
@Test
public void valueToStringFromJavaSqlTime() throws Exception {
java.sql.Time time = java.sql.Time.valueOf("04:05:06");
assertEquals("04:05:06", instance.valueToString(time, null, null, null, null, null));
}
@Test
public void valueOfString() throws Exception {
Calendar dateTime = Calendar.getInstance();
dateTime.clear();
final Time timeValue = instance.valueOfString("00:00:00.999", null, null, 3, null, null, Time.class); assertEqualCalendar(dateTime, instance.valueOfString("00:00", null, null, null, null, null, Calendar.class));
assertEquals(dateTime.getTimeInMillis(), timeValue.getTime()); assertEqualCalendar(dateTime, instance.valueOfString("00:00:00", null, null, null, null, null, Calendar.class));
assertEqualCalendar(dateTime,
instance.valueOfString("00:00:00.000000000", null, null, null, null, null, Calendar.class));
dateTime.set(Calendar.MILLISECOND, 999); final Time timeValue = instance.valueOfString("00:00:00.999", null, null, 3, null, null, Time.class);
assertEquals(dateTime, instance.valueOfString("00:00:00.999", null, null, 3, null, null, Calendar.class)); assertEquals(dateTime.getTimeInMillis(), timeValue.getTime());
assertEquals(dateTime, instance.valueOfString("00:00:00.999", null, null, 3, null, null, Calendar.class));
assertEquals(Long.valueOf(dateTime.getTimeInMillis()),
instance.valueOfString("00:00:00.999", null, null, 3, null, null, Long.class));
final Timestamp timestamp = instance.valueOfString("00:00:00.999888777", null, null, 9, null, null, dateTime.set(Calendar.MILLISECOND, 999);
Timestamp.class); assertEqualCalendar(dateTime,
assertEquals(dateTime.getTimeInMillis(), timestamp.getTime()); instance.valueOfString("00:00:00.999", null, null, 3, null, null, Calendar.class));
assertEquals(999888777, timestamp.getNanos()); assertEqualCalendar(dateTime,
instance.valueOfString("00:00:00.999", null, null, 3, null, null, Calendar.class));
assertEquals(Long.valueOf(dateTime.getTimeInMillis()),
instance.valueOfString("00:00:00.999", null, null, 3, null, null, Long.class));
expectFacetsErrorInValueOfString(instance, "11:12:13.123", null, null, null, null, null); final Timestamp timestamp = instance.valueOfString("00:00:00.999888777", null, null, 9, null, null,
expectFacetsErrorInValueOfString(instance, "11:12:13.123", null, null, 2, null, null); Timestamp.class);
assertEquals(dateTime.getTimeInMillis(), timestamp.getTime());
assertEquals(999888777, timestamp.getNanos());
expectUnconvertibleErrorInValueOfString(instance, "11:12:13.1234", Calendar.class); // expectUnconvertibleErrorInValueOfString(instance, "11:12:13.1234", Calendar.class);
expectUnconvertibleErrorInValueOfString(instance, "11:12:13.0123456789", Timestamp.class); // expectUnconvertibleErrorInValueOfString(instance, "11:12:13.0123456789", Timestamp.class);
expectContentErrorInValueOfString(instance, "24:32:02"); expectContentErrorInValueOfString(instance, "24:32:02");
expectContentErrorInValueOfString(instance, "011:12:13"); expectContentErrorInValueOfString(instance, "011:12:13");
expectContentErrorInValueOfString(instance, "11:12:13:14"); expectContentErrorInValueOfString(instance, "11:12:13:14");
expectContentErrorInValueOfString(instance, "111213"); expectContentErrorInValueOfString(instance, "111213");
expectContentErrorInValueOfString(instance, "1:2:3"); expectContentErrorInValueOfString(instance, "1:2:3");
expectContentErrorInValueOfString(instance, "11:12:13.0.1"); expectContentErrorInValueOfString(instance, "11:12:13.0.1");
expectContentErrorInValueOfString(instance, "11:12:13."); // expectContentErrorInValueOfString(instance, "11:12:13.");
expectContentErrorInValueOfString(instance, "11:12:13.0000000000000"); expectContentErrorInValueOfString(instance, "11:12:13.0000000000000");
expectTypeErrorInValueOfString(instance, "11:12:13"); expectTypeErrorInValueOfString(instance, "11:12:13");
} }
@Test
public void valueOfStringToLocalTime() throws Exception {
LocalTime time = LocalTime.parse("04:05:06");
assertEquals(time, instance.valueOfString("04:05:06", null, null, null, null, null, LocalTime.class));
time = time.plus(123, ChronoUnit.MILLIS);
assertEquals(time, instance.valueOfString("04:05:06.123", null, null, null, null, null, LocalTime.class));
time = time.plus(456789, ChronoUnit.NANOS);
assertEquals(time, instance.valueOfString("04:05:06.123456789", null, null, null, null, null, LocalTime.class));
}
@Test
public void valueOfStringToJavaSqlTime() throws Exception {
java.sql.Time time = java.sql.Time.valueOf("04:05:06");
assertEquals(time, instance.valueOfString("04:05:06", null, null, null, null, null, java.sql.Time.class));
}
@Test
public void valueOfStringToJavaUtilDateTime() throws Exception {
LocalTime time = LocalTime.parse("04:05:06");
ZonedDateTime zdt = ZonedDateTime.of(LocalDate.ofEpochDay(0), time, ZoneId.systemDefault());
long millis = zdt.toInstant().toEpochMilli();
java.util.Date javaUtilDate = new java.util.Date(millis);
assertEquals(javaUtilDate, instance.valueOfString("04:05:06", null, null, null, null, null, java.util.Date.class));
}
@Test
public void testRoundTripTime() throws Exception {
java.sql.Time time = instance.valueOfString("04:05:06.002", true, 4000, 3, 0, true, java.sql.Time.class);
String val = instance.valueToString(time, true, 4000, 3, 0, true);
assertEquals("04:05:06", val);
}
@Test @Test
public void testRoundTripTime() throws Exception { public void tests() throws Exception {
java.sql.Time time = instance.valueOfString("04:05:06.002", true, instance.validate("12:34:55", null, null, null, null, null);
4000, 3, 0, true, java.sql.Time.class); }
String val = instance.valueToString(time, true, 4000, 3, 0, true);
assertEquals("04:05:06", val);
}
@Test
public void tests() throws Exception {
instance.validate("12:34:55", null, null, null, null, null);
}
} }

View File

@ -19,16 +19,23 @@
package org.apache.olingo.commons.core.edm.primitivetype; package org.apache.olingo.commons.core.edm.primitivetype;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
public abstract class PrimitiveTypeBaseTest { public abstract class PrimitiveTypeBaseTest {
private void expectErrorInValueToString(final EdmPrimitiveType instance, private void expectErrorInValueToString(final EdmPrimitiveType instance,
final Object value, final Boolean isNullable, final Integer maxLength, final Object value, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode, final Integer precision, final Integer scale, final Boolean isUnicode,
final String message) { final String message) {
@ -115,4 +122,26 @@ public abstract class PrimitiveTypeBaseTest {
assertThat(e.getLocalizedMessage(), containsString("' has illegal content.")); assertThat(e.getLocalizedMessage(), containsString("' has illegal content."));
} }
} }
protected void setTimeZone(Calendar dateTime, String ID) {
TimeZone timeZone = TimeZone.getTimeZone(ID);
setTimeZone(dateTime, timeZone);
}
protected void setTimeZone(Calendar dateTime, TimeZone timeZone) {
dateTime.setTimeZone(timeZone);
// ensure that the internal fields are recomputed so that the calendar can be correctly cloned
dateTime.get(Calendar.YEAR);
}
protected void assertEqualCalendar(Calendar expected, Calendar actual) {
assertEquals(normalize(expected), normalize(actual));
}
private ZonedDateTime normalize(Calendar cal) {
GregorianCalendar calendar = (GregorianCalendar) cal;
ZonedDateTime zdt = calendar.toZonedDateTime();
ZoneId normalizedZoneId = calendar.getTimeZone().toZoneId().normalized();
return zdt.withZoneSameInstant(normalizedZoneId);
}
} }

View File

@ -25,6 +25,9 @@ import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
@ -144,6 +147,41 @@ public class ODataJsonSerializerTest {
Assert.assertEquals(expectedResult, resultString); Assert.assertEquals(expectedResult, resultString);
} }
@Test
public void entitySimpleNewDateTimeAPI() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
entity.getProperty("PropertyDate").setValue(ValueType.PRIMITIVE, LocalDate.parse("2012-12-03"));
entity.getProperty("PropertyDateTimeOffset").setValue(ValueType.PRIMITIVE, Instant.parse("2012-12-03T07:16:23Z"));
entity.getProperty("PropertyTimeOfDay").setValue(ValueType.PRIMITIVE, LocalTime.parse("03:26:05"));
InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.build()).getContent();
final String resultString = IOUtils.toString(result);
final String expectedResult = "{"
+ "\"@odata.context\":\"$metadata#ESAllPrim/$entity\","
+ "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\","
+ "\"PropertyInt16\":32767,"
+ "\"PropertyString\":\"First Resource - positive values\","
+ "\"PropertyBoolean\":true,"
+ "\"PropertyByte\":255,"
+ "\"PropertySByte\":127,"
+ "\"PropertyInt32\":2147483647,"
+ "\"PropertyInt64\":9223372036854775807,"
+ "\"PropertySingle\":1.79E20,"
+ "\"PropertyDouble\":-1.79E19,"
+ "\"PropertyDecimal\":34,"
+ "\"PropertyBinary\":\"ASNFZ4mrze8=\","
+ "\"PropertyDate\":\"2012-12-03\","
+ "\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\","
+ "\"PropertyDuration\":\"PT6S\","
+ "\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\","
+ "\"PropertyTimeOfDay\":\"03:26:05\""
+ "}";
Assert.assertEquals(expectedResult, resultString);
}
@Test @Test
public void entitySimpleMetadataFull() throws Exception { public void entitySimpleMetadataFull() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim"); final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");