Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Formatting parameters for changeTimeZone function #16939

Merged
merged 6 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions prime-router/src/main/kotlin/common/DateUtilities.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ object DateUtilities {

/** wraps around all the possible variations of a date for finding something that matches */
const val variableDateTimePattern = "[yyyyMMdd]" +
"[yyyyMMddHHmmss.SSSSxx]" +
"[yyyyMMdd[HHmm][ss][.S][Z]]" +
"[yyyy-MM-dd HH:mm:ss.ZZZ]" +
// nano seconds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import org.hl7.fhir.r4.model.HumanName
import org.hl7.fhir.r4.model.IntegerType
import org.hl7.fhir.r4.model.StringType
import java.time.DateTimeException
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeParseException
import java.util.TimeZone

/**
Expand Down Expand Up @@ -109,9 +111,13 @@ object CustomFHIRFunctions : FhirPathFunctions {

CustomFHIRFunctionNames.ChangeTimezone -> {
FunctionDetails(
"changes the timezone of a dateTime, instant, or date resource to the timezone passed in",
"changes the timezone of a dateTime, instant, or date resource to the timezone passed in. " +
"optional params: " +
"dateTimeFormat ('OFFSET', 'LOCAL', 'HIGH_PRECISION_OFFSET', 'DATE_ONLY')(default: 'OFFSET')," +
" convertPositiveDateTimeOffsetToNegative (boolean)(default: false)," +
" useHighPrecisionHeaderDateTimeFormat (boolean)(default: false)",
1,
1
4
oslynn marked this conversation as resolved.
Show resolved Hide resolved
)
}

Expand Down Expand Up @@ -446,10 +452,19 @@ object CustomFHIRFunctions : FhirPathFunctions {
throw SchemaException("Must call changeTimezone on a single element")
}

if (parameters == null || parameters[0].size != 1) {
if (parameters == null || parameters.first().isEmpty()) {
throw SchemaException("Must pass a timezone as the parameter")
}

var dateTimeFormat = DateUtilities.DateTimeFormat.OFFSET
if (parameters.size > 1) {
try {
dateTimeFormat = DateUtilities.DateTimeFormat.valueOf(parameters.get(1).first().primitiveValue())
} catch (e: IllegalArgumentException) {
throw SchemaException("Date time format not found.")
}
}

val inputTimeZone = parameters.first().first().primitiveValue()
val timezonePassed = try {
TimeZone.getTimeZone(ZoneId.of(inputTimeZone))
Expand All @@ -462,27 +477,24 @@ object CustomFHIRFunctions : FhirPathFunctions {
}

return if (focus[0] is StringType) {
if (focus[0].toString().length <= 8) { // we don't want to convert Date-only strings
return mutableListOf(StringType(focus[0].toString()))
val inputDate = try {
DateUtilities.parseDate((focus[0].toString()))
} catch (e: DateTimeParseException) {
throw SchemaException("Error trying to change time zone: " + e.message)
}

// TODO: find a way to pass in these values from receiver settings

val dateTimeFormat = null
val convertPositiveDateTimeOffsetToNegative = null
val useHighPrecisionHeaderDateTimeFormat = null
if (inputDate is LocalDate) {
return mutableListOf(StringType(focus[0].toString()))
}

val formattedDate = DateUtilities.formatDateForReceiver(
DateUtilities.parseDate((focus[0].toString())),
inputDate,
ZoneId.of(inputTimeZone),
dateTimeFormat ?: DateUtilities.DateTimeFormat.OFFSET,
convertPositiveDateTimeOffsetToNegative ?: false,
useHighPrecisionHeaderDateTimeFormat ?: false
)

mutableListOf(
StringType(formattedDate)
dateTimeFormat,
parameters.getOrNull(2)?.first()?.primitiveValue()?.toBoolean() ?: false,
parameters.getOrNull(3)?.first()?.primitiveValue()?.toBoolean() ?: false
)
mutableListOf(StringType(formattedDate))
} else {
val inputDate = focus[0] as? BaseDateTimeType ?: throw SchemaException(
"Must call changeTimezone on a dateTime, instant, or date; " +
Expand Down
19 changes: 19 additions & 0 deletions prime-router/src/test/kotlin/common/DateUtilitiesTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ class DateUtilitiesTests {
"12/1/1900" to "19001201000000",
"2/3/02" to "20020203000000",
"2/3/02 8:00" to "20020203080000",
"20241220194528.4230+0000" to "20241220194528"
).forEach { (input, expected) ->
val parsed = DateUtilities.parseDate(input)
assertThat(
Expand All @@ -417,6 +418,24 @@ class DateUtilitiesTests {
)
).isEqualTo(expected)
}

// test high precision offset
mapOf(
"1975-08-01T11:39:00Z" to "19750801113900.0000+0000",
"2022-04-29T15:43:02.307Z" to "20220429154302.3070+0000",
"20241220194528.4230+0000" to "20241220194528.4230+0000"
).forEach { (input, expected) ->
val parsed = DateUtilities.parseDate(input)
assertThat(
DateUtilities.formatDateForReceiver(
parsed,
DateUtilities.utcZone,
DateUtilities.DateTimeFormat.HIGH_PRECISION_OFFSET,
false,
false
)
).isEqualTo(expected)
}
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,120 @@ class CustomFHIRFunctionsTests {
}.hasClass(SchemaException::class.java)
}

@Test
fun `test changeTimezone with date as string - success`() {
val date = StringType("20241220194528.4230+0000")
val timezone = StringType("UTC")
val dateTimeFormat = StringType("HIGH_PRECISION_OFFSET")
val convertToNegative = StringType("true")
val useHighPrecision = StringType("false")

// test format
var outputDate = CustomFHIRFunctions.changeTimezone(
mutableListOf(date),
mutableListOf(
mutableListOf(timezone), mutableListOf(dateTimeFormat), mutableListOf(convertToNegative),
mutableListOf(useHighPrecision)
)
)
assertThat(outputDate[0]).isInstanceOf(StringType::class.java)
assertThat(outputDate[0].primitiveValue()).isEqualTo("20241220194528.4230-0000")

// test timezone change with offset format
timezone.value = "America/Phoenix"
dateTimeFormat.value = "OFFSET"
convertToNegative.value = "false"
useHighPrecision.value = "false"

outputDate = CustomFHIRFunctions.changeTimezone(
mutableListOf(date),
mutableListOf(
mutableListOf(timezone), mutableListOf(dateTimeFormat), mutableListOf(convertToNegative),
mutableListOf(useHighPrecision)
)
)
assertThat(outputDate[0]).isInstanceOf(StringType::class.java)
assertThat(outputDate[0].primitiveValue()).isEqualTo("20241220124528-0700")

// test different format for input date
val date2 = StringType("2021-08-09T08:52:34-04:00")
timezone.value = "America/Phoenix"
dateTimeFormat.value = "LOCAL"
convertToNegative.value = "false"
useHighPrecision.value = "false"

outputDate = CustomFHIRFunctions.changeTimezone(
mutableListOf(date2),
mutableListOf(
mutableListOf(timezone), mutableListOf(dateTimeFormat), mutableListOf(convertToNegative),
mutableListOf(useHighPrecision)
)
)
assertThat(outputDate[0]).isInstanceOf(StringType::class.java)
assertThat(outputDate[0].primitiveValue()).isEqualTo("20210809055234")

// test date without time should return same date string
val date3 = StringType("2021-08-09")
timezone.value = "America/Phoenix"
dateTimeFormat.value = "OFFSET"
convertToNegative.value = "false"
useHighPrecision.value = "false"

outputDate = CustomFHIRFunctions.changeTimezone(
mutableListOf(date3),
mutableListOf(
mutableListOf(timezone), mutableListOf(dateTimeFormat), mutableListOf(convertToNegative),
mutableListOf(useHighPrecision)
)
)
assertThat(outputDate[0]).isInstanceOf(StringType::class.java)
assertThat(outputDate[0].primitiveValue()).isEqualTo("2021-08-09")

// test timezone change with required param only
timezone.value = "America/Phoenix"

outputDate = CustomFHIRFunctions.changeTimezone(
mutableListOf(date),
mutableListOf(mutableListOf(timezone))
)
assertThat(outputDate[0]).isInstanceOf(StringType::class.java)
assertThat(outputDate[0].primitiveValue()).isEqualTo("20241220124528-0700")
}

@Test
fun `test changeTimezone with date as string - exception`() {
val date = StringType("20241220194528.4230+0000")
val timezone = StringType("UTC")
val dateTimeFormat = StringType("HIGH_PRECISION_OFFSET")
val convertToNegative = StringType("true")
val useHighPrecision = StringType("false")

assertFailure {
dateTimeFormat.value = "HIGH_PRECISION_OFFS"
// test invalid dateTime format
CustomFHIRFunctions.changeTimezone(
mutableListOf(date),
mutableListOf(
mutableListOf(timezone), mutableListOf(dateTimeFormat), mutableListOf(convertToNegative),
mutableListOf(useHighPrecision)
)
)
}.hasClass(SchemaException::class.java)

assertFailure {
dateTimeFormat.value = "HIGH_PRECISION_OFFSET"
date.value = "2021-08-09T"
// test invalid dateTime string input
CustomFHIRFunctions.changeTimezone(
mutableListOf(date),
mutableListOf(
mutableListOf(timezone), mutableListOf(dateTimeFormat), mutableListOf(convertToNegative),
mutableListOf(useHighPrecision)
)
)
}.hasClass(SchemaException::class.java)
}

@Test
fun `test deidentifies a human name`() {
val name = HumanName()
Expand Down
Loading