diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Filters/DateFiltersTests.cs b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Filters/DateFiltersTests.cs index 0a18aea77..6d953f06c 100644 --- a/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Filters/DateFiltersTests.cs +++ b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Filters/DateFiltersTests.cs @@ -220,6 +220,24 @@ public static IEnumerable GetInvalidDataForAddSeconds() yield return new object[] { @"2001-01T" }; } + public static IEnumerable GetValidDataAndFormatForTimeHandling() + { + yield return new object[] { @"0730", "HHmm", "preserve", false, "07:30:00" }; + yield return new object[] { @"1730", "HHmm", "preserve", false, "17:30:00" }; + yield return new object[] { @"12010000", "HHmmssff", "preserve", false, "12:01:00" }; + yield return new object[] { @"12010001", "HHmmssff", "preserve", false, "12:01:00.0100000" }; + yield return new object[] { @"094010", "HHmmss", "preserve", false, "09:40:10" }; + yield return new object[] { @"09:45:50", "HH:mm:ss", "preserve", false, "09:45:50" }; + yield return new object[] { @"144100.0000+0300", "HHmmss.ffffzzz", "utc", false, "11:41:00" }; + yield return new object[] { @"144100.0000+0300", "HHmmss.ffffzzz", "preserve", false, "14:41:00" }; + yield return new object[] { @"18:45", "HHmm", "preserve", true, "18:45" }; // time format does not match format string, but date parsing error is suppressed + } + + public static IEnumerable GetInvalidDataForTimeHandling() + { + yield return new object[] { @"9999", "HHmm" }; + } + [Theory] [MemberData(nameof(GetValidDataForAddSeconds))] public void GivenSeconds_WhenAddOnValidDateTime_CorrectDateTimeStringShouldBeReturned(string originalDateTime, double seconds, string timeZoneHandling, string expectedDateTime) @@ -356,6 +374,22 @@ public void GivenAnInvalidDateTime_WhenFormatAsHl7DateTime_ExceptionShouldBeThro Assert.Equal(FhirConverterErrorCode.InvalidDateTimeFormat, exception.FhirConverterErrorCode); } + [Theory] + [MemberData(nameof(GetValidDataAndFormatForTimeHandling))] + public void GivenAValidData_WhenFormatTimeWithColonAndInputFormatProvided_FormattedTimeOrOriginalInputShouldBeReturned(string input, string inputFormat, string timeZoneHandling, bool suppressParsingError, string expectedDateTime) + { + var result = Filters.FormatTimeWithColon(input, inputFormat, timeZoneHandling, suppressParsingError); + Assert.Equal(expectedDateTime, result); + } + + [Theory] + [MemberData(nameof(GetInvalidDataForTimeHandling))] + public void GivenInvalidData_WhenFormatAsTimeInterval_ExceptionThrown(string input, string inputFormat) + { + var exception = Assert.Throws(() => Filters.FormatTimeWithColon(input, inputFormat)); + Assert.Equal(FhirConverterErrorCode.InvalidDateTimeFormat, exception.FhirConverterErrorCode); + } + [Fact] public void NowTest() { diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter/Filters/DateFilters.cs b/src/Microsoft.Health.Fhir.Liquid.Converter/Filters/DateFilters.cs index d89c92cd0..977250143 100644 --- a/src/Microsoft.Health.Fhir.Liquid.Converter/Filters/DateFilters.cs +++ b/src/Microsoft.Health.Fhir.Liquid.Converter/Filters/DateFilters.cs @@ -4,6 +4,8 @@ // ------------------------------------------------------------------------------------------------- using System; +using System.Globalization; +using System.Linq; using Microsoft.Health.Fhir.Liquid.Converter.Exceptions; using Microsoft.Health.Fhir.Liquid.Converter.Models; @@ -96,6 +98,41 @@ public static string Now(string input, string format = "yyyy-MM-ddTHH:mm:ss.FFFZ return DateTime.UtcNow.ToString(format); } + /* Converts an HL7v2 time to the FHIR format for time + / HL7v2 - 2.8.35.2 Explicit time interval (ST). Format: HHmm + / HL7v2 - 2.4.5.6 TM time. Format: HHMM[SS[.SSSS]][+/-ZZZZ] + / FHIR - time https://build.fhir.org/datatypes.html#time + */ + public static string FormatTimeWithColon(string input, string inputFormat, string timeZoneHandling = "preserve", bool suppressParsingError = false) + { + if (!Enum.TryParse(timeZoneHandling, true, out TimeZoneHandlingMethod outputTimeZoneHandling)) + { + throw new RenderException(FhirConverterErrorCode.InvalidTimeZoneHandling, Resources.InvalidTimeZoneHandling); + } + + var dt = TimeSpan.Zero; + try + { + dt = outputTimeZoneHandling switch + { + TimeZoneHandlingMethod.Preserve => DateTimeOffset.ParseExact(input, inputFormat, CultureInfo.InvariantCulture).TimeOfDay, + TimeZoneHandlingMethod.Utc => DateTimeOffset.ParseExact(input, inputFormat, CultureInfo.InvariantCulture).ToUniversalTime().TimeOfDay, + _ => throw new ArgumentException(Resources.InvalidTimeZoneHandling), + }; + } + catch (Exception) + { + if (suppressParsingError) + { + return input; + } + + throw new RenderException(FhirConverterErrorCode.InvalidDateTimeFormat, string.Format(Resources.InvalidDateTimeFormat, input)); + } + + return dt.ToString(); + } + public static string FormatAsHl7v2DateTime(string input, string timeZoneHandling = "preserve") { if (string.IsNullOrEmpty(input))