Ensure ' ' is treated as '+' in timezones with offsets. (#6115)

This commit is contained in:
Luke deGruchy 2024-07-16 15:29:06 -04:00 committed by GitHub
parent 13e2d41165
commit e3b749e61b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 6 deletions

View File

@ -230,17 +230,41 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
return Long.parseLong(retVal);
}
/**
* Find the offset for a timestamp. If it exists. An offset may start either with '-', 'Z', '+', or ' '.
* <p/>
* There is a special case where ' ' is considered a valid offset initial character and this is because when
* handling URLs with timestamps, '+' is considered an escape character for ' ', so '+' may have been replaced with
* ' ' by the time execution reaches this method. This is why this method handles both characters.
*
* @param theValueString A timestamp containing either a timezone offset or nothing.
* @return The index of the offset portion of the timestamp, if applicable, otherwise -1
*/
private int getOffsetIndex(String theValueString) {
int plusIndex = theValueString.indexOf('+', 16);
int spaceIndex = theValueString.indexOf(' ', 16);
int minusIndex = theValueString.indexOf('-', 16);
int zIndex = theValueString.indexOf('Z', 16);
int retVal = Math.max(Math.max(plusIndex, minusIndex), zIndex);
if (retVal == -1) {
int maxIndexPlusAndMinus = Math.max(Math.max(plusIndex, minusIndex), zIndex);
int maxIndexSpaceAndMinus = Math.max(Math.max(spaceIndex, minusIndex), zIndex);
if (maxIndexPlusAndMinus == -1 && maxIndexSpaceAndMinus == -1) {
return -1;
}
if ((retVal - 2) != (plusIndex + minusIndex + zIndex)) {
throwBadDateFormat(theValueString);
int retVal = 0;
if (maxIndexPlusAndMinus != -1) {
if ((maxIndexPlusAndMinus - 2) != (plusIndex + minusIndex + zIndex)) {
throwBadDateFormat(theValueString);
}
retVal = maxIndexPlusAndMinus;
}
if (maxIndexSpaceAndMinus != -1) {
if ((maxIndexSpaceAndMinus - 2) != (spaceIndex + minusIndex + zIndex)) {
throwBadDateFormat(theValueString);
}
retVal = maxIndexSpaceAndMinus;
}
return retVal;
}
@ -574,13 +598,15 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
setTimeZoneZulu(true);
} else if (theValue.length() != 6) {
throwBadDateFormat(theWholeValue, "Timezone offset must be in the form \"Z\", \"-HH:mm\", or \"+HH:mm\"");
} else if (theValue.charAt(3) != ':' || !(theValue.charAt(0) == '+' || theValue.charAt(0) == '-')) {
} else if (theValue.charAt(3) != ':'
|| !(theValue.charAt(0) == '+' || theValue.charAt(0) == ' ' || theValue.charAt(0) == '-')) {
throwBadDateFormat(theWholeValue, "Timezone offset must be in the form \"Z\", \"-HH:mm\", or \"+HH:mm\"");
} else {
parseInt(theWholeValue, theValue.substring(1, 3), 0, 23);
parseInt(theWholeValue, theValue.substring(4, 6), 0, 59);
clearTimeZone();
setTimeZone(getTimeZone("GMT" + theValue));
final String valueToUse = theValue.startsWith(" ") ? theValue.replace(' ', '+') : theValue;
setTimeZone(getTimeZone("GMT" + valueToUse));
}
return this;

View File

@ -0,0 +1,7 @@
---
type: fix
issue: 6094
jira: SMILE-8693
title: "Searching or conditional creating/updating with a timestamp with an offset containing '+' fails with HAPI-1883.
For example: 'Observation?date=2024-07-08T20:47:12.123+03:30'
This has been fixed."

View File

@ -13,6 +13,8 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
@ -721,6 +723,22 @@ public class BaseDateTimeDtDstu2Test {
assertEquals("2010-01-01T09:00:00.12345Z", dt.getValueAsString());
}
@ParameterizedTest
@ValueSource(strings = {"2024-07-08T20:47:12.123+03:30", "2024-07-08T20:47:12.123 03:30"})
public void testParseTimeZonePositiveOffset(String theTimestampLiteral) {
myDateInstantParser.setTimeZone(TimeZone.getTimeZone("Asia/Tehran"));
final DateTimeDt dt = new DateTimeDt(theTimestampLiteral);
assertEquals(theTimestampLiteral, dt.getValueAsString());
assertEquals("2024-07-08 20:47:12.123", myDateInstantParser.format(dt.getValue()));
assertEquals("GMT+03:30", dt.getTimeZone().getID());
assertEquals(12600000, dt.getTimeZone().getRawOffset());
dt.setTimeZoneZulu(true);
assertEquals("2024-07-08T17:17:12.123Z", dt.getValueAsString());
}
@Test
public void testParseYear() throws DataFormatException {
DateTimeDt dt = new DateTimeDt();