I have a utility method that attempts to parse a datetime string into a datetime. A portion of it is below.
if (dateTime is { Kind: DateTimeKind.Unspecified, TimeOfDay.Ticks: > 0 })
{
var cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
dateTime = TimeZoneInfo.ConvertTimeToUtc(dateTime, cst);
}
return dateTime.ToUniversalTime();
sometimes the strings were trying to parse do not have a timezone offset specified. But they are implicitly Central Standard Times. For example this string value "20241010091157" should be 10/10/2024 09:11:57 CST. My utility method attempts to set the timezone for datetimes that don't have a kind specified but sill have time values.
The following unit test passes when I run locally on a machine in CST time zone but fails when I run on the build agent.
message.PV1.AdmitDateTime.Time.Value = "20241010091157";
var hl7DateTime = hl7Utils.GetHl7DateTimeUtc(message.PV1.AdmitDateTime);
Assert.IsNotNull(hl7DateTime);
Assert.AreEqual(2024, hl7DateTime.Value.Year);
Assert.AreEqual(10, hl7DateTime.Value.Month);
Assert.AreEqual(10, hl7DateTime.Value.Day);
Assert.AreEqual(14, hl7DateTime.Value.Hour);
Assert.AreEqual(11, hl7DateTime.Value.Minute);
Assert.AreEqual(57, hl7DateTime.Value.Second);
Assert.AreEqual(hl7DateTime.Value.Kind, DateTimeKind.Utc);
This assertion fails on the build agent:
Assert.AreEqual(14, hl7DateTime.Value.Hour);
Assert.AreEqual failed. Expected:<14>. Actual:<4>
what is the right way to parse a string into a datetime and default the timezone if its not provided in the parsed string?
I have a utility method that attempts to parse a datetime string into a datetime. A portion of it is below.
if (dateTime is { Kind: DateTimeKind.Unspecified, TimeOfDay.Ticks: > 0 })
{
var cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
dateTime = TimeZoneInfo.ConvertTimeToUtc(dateTime, cst);
}
return dateTime.ToUniversalTime();
sometimes the strings were trying to parse do not have a timezone offset specified. But they are implicitly Central Standard Times. For example this string value "20241010091157" should be 10/10/2024 09:11:57 CST. My utility method attempts to set the timezone for datetimes that don't have a kind specified but sill have time values.
The following unit test passes when I run locally on a machine in CST time zone but fails when I run on the build agent.
message.PV1.AdmitDateTime.Time.Value = "20241010091157";
var hl7DateTime = hl7Utils.GetHl7DateTimeUtc(message.PV1.AdmitDateTime);
Assert.IsNotNull(hl7DateTime);
Assert.AreEqual(2024, hl7DateTime.Value.Year);
Assert.AreEqual(10, hl7DateTime.Value.Month);
Assert.AreEqual(10, hl7DateTime.Value.Day);
Assert.AreEqual(14, hl7DateTime.Value.Hour);
Assert.AreEqual(11, hl7DateTime.Value.Minute);
Assert.AreEqual(57, hl7DateTime.Value.Second);
Assert.AreEqual(hl7DateTime.Value.Kind, DateTimeKind.Utc);
This assertion fails on the build agent:
Assert.AreEqual(14, hl7DateTime.Value.Hour);
Assert.AreEqual failed. Expected:<14>. Actual:<4>
what is the right way to parse a string into a datetime and default the timezone if its not provided in the parsed string?
So the issue turns out to be that we were using a library to parse the string into a date time. Its built into NHapi which is used for hl7 processing.
This is the libraries parse method.
public virtual DateTime GetAsDate()
{
try
{
var dateFormats = new string[] { LongDateTimeFormat, ShortDateTimeFormat, LongDateTimeFormatWithSecond, LongDateTimeFormatWithOffset, LongDateTimeFormatWithFractionOfSecond };
var val = DateTime.MinValue;
var culture = Thread.CurrentThread.CurrentCulture;
if (Value != null && Value.Length > 0)
{
val = DateTime.ParseExact(Value, dateFormats, culture, DateTimeStyles.NoCurrentDateDefault);
}
return val;
}
catch (Exception)
{
throw new HL7Exception("Could not get field as dateTime");
}
}
I left out my utility methods invocation of the method above, my util method looked like this:
public DateTime? GetHl7DateTimeUtc(TS? ts)
{
if (ts is not { Time: not null } || ts.Time.GetAsDate() == DateTime.MinValue)
{
return null;
}
var dateTime = ts.Time.GetAsDate();
if (dateTime is { Kind: DateTimeKind.Unspecified, TimeOfDay.Ticks: > 0 })
{
var cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
dateTime = TimeZoneInfo.ConvertTimeToUtc(dateTime, cst);
}
return dateTime.ToUniversalTime();
}
So the library was using Thread.CurrentThread.CurrentCulture;
My solution was to avoid using the library method to parse the string into a dateTime and just roll that code myself. Here is my utility method that works:
public DateTime? GetHl7DateTimeUtc(TS? ts)
{
if (ts is not { Time: not null } || ts.Time.GetAsDate() == DateTime.MinValue)
{
return null;
}
var dateFormats = new[]
{
"yyyyMMddHHmmss.FFFF", "yyyyMMddHHmmsszzz", "yyyyMMddHHmmss", "yyyyMMddHHmm", "yyyyMMdd",
"yyyyMMddHHmmss-zzzz"
};
var dateTime = DateTime.ParseExact(ts.Time.Value, dateFormats, CultureInfo.InvariantCulture,
DateTimeStyles.NoCurrentDateDefault);
if (dateTime is { Kind: DateTimeKind.Unspecified, TimeOfDay.Ticks: > 0 })
{
var cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
dateTime = TimeZoneInfo.ConvertTimeToUtc(dateTime, cst);
}
return dateTime.ToUniversalTime();
}
This method correctly parses string dateTimes both on my machine and on ADO agents. It also defaults the timezone to Central Standard Time
if its not provided. Hamid was on the right track I'll accept his answer.
if (...) {...}
isn't correctly run, and.ToUniversalTime();
actually just tried to use local machine time instead. You can use debugger to find out. But the full parser as asked by Michael Liu is needed too. – Peko Miko Commented Jan 3 at 1:58