Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How do I convert a string+offset value to a DateTimeOffset without adding-then-subtracting the offset?

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

  • You say your yyyyMMdd HH:mm input strings represent a "local" (or rather: non-UTC) instant, but…
    • You’re using DateTimeOffset.ParseExact even though the input string does not represent a Date+Time+Offset tuple: the string only represents a Date+Time+Undefined-Offset tuple.
      • So use DateTime.ParseExact instead, and ensure it has DateTimeKind.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).
    • 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."

I’d do it like so:

  • As the dateTimeString value only represents a date+time value without an offset I use DateTime.ParseExact instead of DateTimeOffset.ParseExact.
  • Use DateTimeStyles.None as AssumeUniversal is incorrect here.
  • Then I adjust the DateTime value to a DateTimeOffset via 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 ) );
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading