diff --git a/fhirpath/src/main/java/au/csiro/pathling/sql/dates/datetime/DateTimeUDFRegistrar.java b/fhirpath/src/main/java/au/csiro/pathling/sql/dates/datetime/DateTimeUDFRegistrar.java
index 3f8444d750..b195325d9b 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/sql/dates/datetime/DateTimeUDFRegistrar.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/sql/dates/datetime/DateTimeUDFRegistrar.java
@@ -33,7 +33,6 @@ protected void registerUDFs(final UDFRegistrar udfRegistrar) {
.register(new DateTimeGreaterThanFunction())
.register(new DateTimeGreaterThanOrEqualToFunction())
.register(new DateTimeLessThanFunction())
- .register(new DateTimeLessThanOrEqualToFunction())
- .register(new NormalizeDateTimeFunction());
+ .register(new DateTimeLessThanOrEqualToFunction());
}
}
diff --git a/fhirpath/src/main/java/au/csiro/pathling/sql/dates/time/NormalizeTimeFunction.java b/fhirpath/src/main/java/au/csiro/pathling/sql/dates/time/NormalizeTimeFunction.java
new file mode 100644
index 0000000000..cc9db27b0a
--- /dev/null
+++ b/fhirpath/src/main/java/au/csiro/pathling/sql/dates/time/NormalizeTimeFunction.java
@@ -0,0 +1,39 @@
+package au.csiro.pathling.sql.dates.time;
+
+import au.csiro.pathling.sql.udf.SqlFunction1;
+import java.time.LocalTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Objects;
+import org.apache.spark.sql.types.DataType;
+import org.apache.spark.sql.types.DataTypes;
+
+/**
+ * A function for normalizing a time string to a long (nanoseconds) for comparison purposes.
+ *
+ * Truncates the precision of the time to milliseconds, as this is the highest precision supported
+ * by FHIR.
+ */
+public class NormalizeTimeFunction implements SqlFunction1 {
+
+ private static final long serialVersionUID = 4117774962772392910L;
+
+ public static final String FUNCTION_NAME = "normalize_time";
+
+ @Override
+ public String getName() {
+ return FUNCTION_NAME;
+ }
+
+ @Override
+ public DataType getReturnType() {
+ return DataTypes.LongType;
+ }
+
+ @Override
+ public Long call(final String timeString) throws Exception {
+ final LocalTime time = Objects.requireNonNull(
+ LocalTime.parse(timeString));
+ return time.truncatedTo(ChronoUnit.MILLIS).toNanoOfDay();
+ }
+
+}
diff --git a/fhirpath/src/main/java/au/csiro/pathling/sql/misc/MiscUDFRegistrar.java b/fhirpath/src/main/java/au/csiro/pathling/sql/misc/MiscUDFRegistrar.java
index 18c5c79909..f16528f6fd 100644
--- a/fhirpath/src/main/java/au/csiro/pathling/sql/misc/MiscUDFRegistrar.java
+++ b/fhirpath/src/main/java/au/csiro/pathling/sql/misc/MiscUDFRegistrar.java
@@ -16,6 +16,8 @@
*/
package au.csiro.pathling.sql.misc;
+import au.csiro.pathling.sql.dates.datetime.NormalizeDateTimeFunction;
+import au.csiro.pathling.sql.dates.time.NormalizeTimeFunction;
import au.csiro.pathling.sql.udf.AbstractUDFRegistrar;
/**
@@ -27,6 +29,8 @@ public class MiscUDFRegistrar extends AbstractUDFRegistrar {
protected void registerUDFs(final UDFRegistrar udfRegistrar) {
udfRegistrar
.register(new CodingToLiteral())
- .register(new TemporalDifferenceFunction());
+ .register(new TemporalDifferenceFunction())
+ .register(new NormalizeDateTimeFunction())
+ .register(new NormalizeTimeFunction());
}
}
diff --git a/fhirpath/src/test/java/au/csiro/pathling/views/FhirViewTest.java b/fhirpath/src/test/java/au/csiro/pathling/views/FhirViewTest.java
index f504950215..b4612e10e8 100644
--- a/fhirpath/src/test/java/au/csiro/pathling/views/FhirViewTest.java
+++ b/fhirpath/src/test/java/au/csiro/pathling/views/FhirViewTest.java
@@ -18,6 +18,7 @@
import au.csiro.pathling.encoders.datatypes.DecimalCustomCoder;
import au.csiro.pathling.io.source.DataSource;
import au.csiro.pathling.sql.dates.datetime.NormalizeDateTimeFunction;
+import au.csiro.pathling.sql.dates.time.NormalizeTimeFunction;
import au.csiro.pathling.terminology.TerminologyServiceFactory;
import au.csiro.pathling.test.SpringBootUnitTest;
import ca.uhn.fhir.context.FhirContext;
@@ -142,6 +143,8 @@ class Expect implements ResultExpectation {
public static final String FHIR_DATE_TIME_PATTERN = "^([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|"
+ "[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:"
+ "([0-5][0-9]|60)(\\.[0-9]{1,9})?)?)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)?)?)?$";
+ public static final String FHIR_TIME_PATTERN = "^([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)"
+ + "(\\.[0-9]+)?$";
Path expectedJson;
List expectedColumns;
@@ -166,6 +169,9 @@ public void expectResult(@Nonnull final Dataset rowDataset) {
return when(
col(field.name()).rlike(FHIR_DATE_TIME_PATTERN),
callUDF(NormalizeDateTimeFunction.FUNCTION_NAME, col(field.name()))
+ ).when(
+ col(field.name()).rlike(FHIR_TIME_PATTERN),
+ callUDF(NormalizeTimeFunction.FUNCTION_NAME, col(field.name()))
).otherwise(col(field.name())).alias(field.name());
} else {
// Add the field to the selection without alteration.