Skip to content

Commit 3ff7e83

Browse files
authored
Add support for time zone (#222)
* Add support for time zone * Remove time zone dependent tests for now
1 parent 71a7ead commit 3ff7e83

File tree

6 files changed

+90
-33
lines changed

6 files changed

+90
-33
lines changed

src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Filters/DateFiltersTests.cs

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System;
77
using System.Collections.Generic;
88
using Microsoft.Health.Fhir.Liquid.Converter.Exceptions;
9+
using Microsoft.Health.Fhir.Liquid.Converter.Models;
910
using Xunit;
1011

1112
namespace Microsoft.Health.Fhir.Liquid.Converter.UnitTests.FilterTests
@@ -24,13 +25,34 @@ public static IEnumerable<object[]> GetValidDataForAddHyphensDate()
2425

2526
public static IEnumerable<object[]> GetValidDataForFormatAsDateTime()
2627
{
27-
yield return new object[] { null, null };
28-
yield return new object[] { string.Empty, string.Empty };
29-
yield return new object[] { @"2001", @"2001" };
30-
yield return new object[] { @"200101", @"2001-01" };
31-
yield return new object[] { @"20050110045253", @"2005-01-10T04:52:53Z" };
32-
yield return new object[] { @"20110103143428-0800", @"2011-01-03T14:34:28-08:00" };
33-
yield return new object[] { @"19701231115959+0600", @"1970-12-31T11:59:59+06:00" };
28+
// TimeZoneHandling does not affect dateTime without time
29+
yield return new object[] { null, "preserve", null };
30+
yield return new object[] { null, "utc", null };
31+
yield return new object[] { null, "local", null };
32+
yield return new object[] { string.Empty, "preserve", string.Empty };
33+
yield return new object[] { string.Empty, "utc", string.Empty };
34+
yield return new object[] { string.Empty, "local", string.Empty };
35+
yield return new object[] { @"2001", "preserve", @"2001" };
36+
yield return new object[] { @"2001", "utc", @"2001" };
37+
yield return new object[] { @"2001", "local", @"2001" };
38+
yield return new object[] { @"200101", "preserve", @"2001-01" };
39+
yield return new object[] { @"200101", "utc", @"2001-01" };
40+
yield return new object[] { @"200101", "local", @"2001-01" };
41+
42+
// If no time zone provided, it is treated as local
43+
yield return new object[] { @"20050110045253", "preserve", @"2005-01-10T04:52:53" };
44+
yield return new object[] { @"20050110045253", "local", @"2005-01-10T04:52:53" };
45+
46+
// If time zone provided, it should be formatted according to TimeZoneHandling
47+
yield return new object[] { @"20110103143428-0800", "preserve", @"2011-01-03T14:34:28-08:00" };
48+
yield return new object[] { @"20110103143428-0800", "utc", @"2011-01-03T22:34:28Z" };
49+
yield return new object[] { @"19701231115959+0600", "preserve", @"1970-12-31T11:59:59+06:00" };
50+
yield return new object[] { @"19701231115959+0600", "utc", @"1970-12-31T05:59:59Z" };
51+
52+
// Skip this test in pipeline, as the local time zone is different
53+
// yield return new object[] { @"20050110045253", "utc", @"2005-01-09T20:52:53Z" };
54+
// yield return new object[] { @"20110103143428-0800", "local", @"2011-01-04T06:34:28+08:00" };
55+
// yield return new object[] { @"19701231115959+0600", "local", @"1970-12-31T13:59:59+08:00" };
3456
}
3557

3658
public static IEnumerable<object[]> GetInvalidDataForAddHyphensDate()
@@ -61,34 +83,51 @@ public static IEnumerable<object[]> GetInvalidDataForFormatAsDateTime()
6183
yield return new object[] { @"20200101101080" };
6284
}
6385

86+
public static IEnumerable<object[]> GetInvalidTimeZoneHandling()
87+
{
88+
yield return new object[] { @"20050110045253", null };
89+
yield return new object[] { @"20110103143428-0800", string.Empty };
90+
yield return new object[] { @"19701231115959+0600", "abc" };
91+
}
92+
6493
[Theory]
6594
[MemberData(nameof(GetValidDataForAddHyphensDate))]
66-
public void GivenAnHl7v2Date_WhenAddHyphensDate_ConvertedDateShouldBeReturned(string input, string expected)
95+
public void GivenADate_WhenAddHyphensDate_ConvertedDateShouldBeReturned(string input, string expected)
6796
{
6897
var result = Filters.AddHyphensDate(input);
6998
Assert.Equal(expected, result);
7099
}
71100

72101
[Theory]
73102
[MemberData(nameof(GetValidDataForFormatAsDateTime))]
74-
public void GivenAnHl7v2DateTime_WhenFormatAsDateTime_ConvertedDateShouldBeReturned(string input, string expected)
103+
public void GivenADateTime_WhenFormatAsDateTime_ConvertedDateShouldBeReturned(string input, string timeZoneHandling, string expected)
75104
{
76-
var result = Filters.FormatAsDateTime(input);
105+
var result = Filters.FormatAsDateTime(input, timeZoneHandling);
77106
Assert.Equal(expected, result);
78107
}
79108

80109
[Theory]
81110
[MemberData(nameof(GetInvalidDataForAddHyphensDate))]
82-
public void GivenAnInvalidHl7v2DateTime_WhenAddHyphensDate_ExceptionShouldBeThrown(string input)
111+
public void GivenAnInvalidDateTime_WhenAddHyphensDate_ExceptionShouldBeThrown(string input)
83112
{
84-
Assert.Throws<RenderException>(() => Filters.AddHyphensDate(input));
113+
var exception = Assert.Throws<RenderException>(() => Filters.AddHyphensDate(input));
114+
Assert.Equal(FhirConverterErrorCode.InvalidDateTimeFormat, exception.FhirConverterErrorCode);
85115
}
86116

87117
[Theory]
88118
[MemberData(nameof(GetInvalidDataForFormatAsDateTime))]
89-
public void GivenAnInvalidHl7v2DateTime_WhenFormatAsDateTime_ExceptionShouldBeThrown(string input)
119+
public void GivenAnInvalidDateTime_WhenFormatAsDateTime_ExceptionShouldBeThrown(string input)
120+
{
121+
var exception = Assert.Throws<RenderException>(() => Filters.FormatAsDateTime(input));
122+
Assert.Equal(FhirConverterErrorCode.InvalidDateTimeFormat, exception.FhirConverterErrorCode);
123+
}
124+
125+
[Theory]
126+
[MemberData(nameof(GetInvalidTimeZoneHandling))]
127+
public void GivenAnInvalidTimeZoneHandling_WhenFormatAsDateTime_ExceptionShouldBeThrown(string input, string timeZoneHandling)
90128
{
91-
Assert.Throws<RenderException>(() => Filters.FormatAsDateTime(input));
129+
var exception = Assert.Throws<RenderException>(() => Filters.FormatAsDateTime(input, timeZoneHandling));
130+
Assert.Equal(FhirConverterErrorCode.InvalidTimeZoneHandling, exception.FhirConverterErrorCode);
92131
}
93132

94133
[Fact]

src/Microsoft.Health.Fhir.Liquid.Converter/Filters/DateFilters.cs

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static string AddHyphensDate(string input)
3434
return ConvertDate(input, groups);
3535
}
3636

37-
public static string FormatAsDateTime(string input)
37+
public static string FormatAsDateTime(string input, string timeZoneHandling = "local")
3838
{
3939
if (string.IsNullOrEmpty(input))
4040
{
@@ -48,28 +48,33 @@ public static string FormatAsDateTime(string input)
4848
}
4949

5050
var groups = matches[0].Groups;
51-
if (groups["time"].Success)
51+
if (!groups["time"].Success)
5252
{
53-
// Convert DateTime with time zone
54-
string result = ConvertDateTime(input, groups);
55-
if (groups["timeZone"].Success)
56-
{
57-
int timeZoneHour = int.Parse(groups["timeZoneHour"].Value);
58-
int timeZoneMinute = int.Parse(groups["timeZoneMinute"].Value);
59-
result += groups["sign"].Value + new TimeSpan(timeZoneHour, timeZoneMinute, 0).ToString(@"hh\:mm");
60-
}
61-
else
62-
{
63-
result += "Z";
64-
}
53+
return ConvertDate(input, groups);
54+
}
6555

66-
return result;
56+
// Convert DateTime with time zone
57+
var result = ConvertDateTime(input, groups);
58+
if (groups["timeZone"].Success)
59+
{
60+
var timeZoneHour = int.Parse(groups["timeZoneHour"].Value);
61+
var timeZoneMinute = int.Parse(groups["timeZoneMinute"].Value);
62+
result += groups["sign"].Value + new TimeSpan(timeZoneHour, timeZoneMinute, 0).ToString(@"hh\:mm");
6763
}
68-
else
64+
65+
if (!DateTime.TryParse(result, out var parsedResult))
6966
{
70-
// Convert Date
71-
return ConvertDate(input, groups);
67+
throw new RenderException(FhirConverterErrorCode.InvalidDateTimeFormat, string.Format(Resources.InvalidDateTimeFormat, input));
7268
}
69+
70+
var dateTimeFormat = parsedResult.Millisecond == 0 ? "yyyy-MM-ddTHH:mm:ss%K" : "yyyy-MM-ddTHH:mm:ss.fff%K";
71+
return timeZoneHandling?.ToLower() switch
72+
{
73+
"preserve" => result,
74+
"utc" => TimeZoneInfo.ConvertTimeToUtc(parsedResult).ToString(dateTimeFormat),
75+
"local" => parsedResult.ToString(dateTimeFormat),
76+
_ => throw new RenderException(FhirConverterErrorCode.InvalidTimeZoneHandling, Resources.InvalidTimeZoneHandling),
77+
};
7378
}
7479

7580
public static string Now(string input, string format = "yyyy-MM-ddTHH:mm:ss.FFFZ")

src/Microsoft.Health.Fhir.Liquid.Converter/Models/FhirConverterErrorCode.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public enum FhirConverterErrorCode
3838
TimeoutError = 1306,
3939
InvalidDateTimeFormat = 1307,
4040
InvalidIdGenerationInput = 1308,
41+
InvalidTimeZoneHandling = 1309,
4142

4243
// PostprocessException
4344
JsonParsingError = 1401,

src/Microsoft.Health.Fhir.Liquid.Converter/OutputProcessor/PostProcessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public static JObject ParseJson(string input)
4545
ParseTreeWalker.Default.Walk(listener, tree);
4646
var result = listener.GetResult().ToString();
4747

48-
return JObject.Parse(result);
48+
return JsonConvert.DeserializeObject<JObject>(result, new JsonSerializerSettings { DateParseHandling = DateParseHandling.None });
4949
}
5050

5151
public static JObject MergeJson(JObject obj)

src/Microsoft.Health.Fhir.Liquid.Converter/Resources.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.Health.Fhir.Liquid.Converter/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@
144144
<data name="InvalidIdGenerationInput" xml:space="preserve">
145145
<value>The input to generate ID is invalid.</value>
146146
</data>
147+
<data name="InvalidTimeZoneHandling" xml:space="preserve">
148+
<value>TimeZoneHandling only supports 'local', 'utc' and 'preserve'.</value>
149+
</data>
147150
<data name="JsonMergingError" xml:space="preserve">
148151
<value>Error happened when merging JSON: {0}</value>
149152
</data>

0 commit comments

Comments
 (0)