Our app interacts with an API that returns local datetime strings in the format yyyyMMdd HH:mm with a secondary number field indicating the UTC offset in hours.
We’re successfully handling this with the following code.
DateTimeOffset ParseWeirdDateTime(string dateTimeString, double utcOffset)
{
DateTimeOffset dateTimeOffset = DateTimeOffset.ParseExact(
dateTimeString,
"yyyyMMdd HH:mm",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal
);
return dateTimeOffset.AddHours(-utcOffset).ToOffset(TimeSpan.FromHours(utcOffset));
}
string localDateTimeString = "20231108 14:30";
double utcOffset = -4;
DateTimeOffset dateTimeParsed = ParseWeirdDateTime(localDateTimeString, utcOffset);
// comes out to 11/8/2023 2:30:00 PM -04:00
My question is, is there a better way to handle this? Though this works, it feels clunky to parse to a UTC DateTimeOffset, only to apply the inverse of the offset and finally convert to the offset.
>Solution :
- You say your
yyyyMMdd HH:mminput strings represent a "local" (or rather: non-UTC) instant, but…- You’re using
DateTimeOffset.ParseExacteven though the input string does not represent aDate+Time+Offsettuple: the string only represents aDate+Time+Undefined-Offsettuple.- So use
DateTime.ParseExactinstead, and ensure it hasDateTimeKind.Unspecified(because no offset data is specified at-this-point). - (btw, never use
DateTimeKind.Local: it specifically refers to the local computer’s time, which is almost always useless in business applications because it isn’t invariant like real UTC is: consider local timezones, political timezone changes, daylight savings, machine misconfiguration, etc).
- So use
- Also, you’re incorrectly using
DateTimeStyles.AssumeUniversal, which doesn’t apply here because the string you’re passing-in does not represent a UTC value. As the documentation states "If no time zone is specified in the parsed string, the string is assumed to denote a UTC."
- You’re using
I’d do it like so:
- As the
dateTimeStringvalue only represents a date+time value without an offset I useDateTime.ParseExactinstead ofDateTimeOffset.ParseExact. - Use
DateTimeStyles.NoneasAssumeUniversalis incorrect here. - Then I adjust the
DateTimevalue to aDateTimeOffsetvia the ctor.
/// <summary><see href="https://stackoverflow.com/a/77449580/159145" /></summary>
static DateTimeOffset ParseWeirdDateTime( String dateTimeString, Double utcOffsetInHours /* Always specify units in names when using dimensionless types */ )
{
DateTime instantInTimeWithUndefinedOffset = DateTime.ParseExact(
s : dateTimeString,
format : "yyyyMMdd HH:mm",
provider: CultureInfo.InvariantCulture,
style : DateTimeStyles.None
);
Debug.Assert( instantInTimeWithUndefinedOffset.Kind == DateTimeKind.Unspecified );
return new DateTimeOffset( instantInTimeWithUndefinedOffset, offset: TimeSpan.FromHours( utcOffsetInHours ) );
}