Convert a local date time string to UTC as is (using C#)

Let's say you have a bunch of date time strings which doesn't contain any time zone information but you know the time zone some how. Let's say you are in a web browser context.

You fetch the time zone using moment.js something like moment.tz.guess() and passes the time zone representation to the server together with your date time strings.

30 - 60 minutes later you will probably be pretty frustrated working with the DateTime structure, string parsing and time zones. There is actually pretty hard to get the DateTime structure to not take the current computer's time zone into consideration when parsing a string into a DateTime object.

This method might help you. The return value (string) is pretty specific to my scenario but you get the deal and are probably smart enough to change it to whatever you like.

public class DateTimeStringConverter  
{
    /// <summary>
    /// Parses given date time string based on given time zone and returns the UTC time, the computer's time zone isn't taken into consideration.
    /// </summary>
    /// <param name="localDateTimeString">String representation of a local data time string without any information about time zone</param>
    /// <param name="timeZoneName">The time zone name, can be an IANA time zone or a Windows time zone ID.</param>
    /// <returns>Given local date time string converted to UTC based on given time zone</returns>
    public string ToUtcString(string localDateTimeString, string timeZoneName)
    {
        if (string.IsNullOrWhiteSpace(localDateTimeString))
        {
            throw new ArgumentException("Can't be null or white space", nameof(localDateTimeString));
        }

        if (string.IsNullOrWhiteSpace(timeZoneName))
        {
            throw new ArgumentException("Can't be null or white space", nameof(timeZoneName));
        }

        // The idea here is to parse the date time string as it is without applying any time zone conversion during parsing time.
        if (!DateTimeOffset.TryParse(localDateTimeString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var dateTimeOffset))
        {
            throw new ArgumentException($"'{localDateTimeString}' isn't a valid date time string", nameof(localDateTimeString));
        }

        var parsed = dateTimeOffset.LocalDateTime;

        var timeZone = TZConvert.GetTimeZoneInfo(timeZoneName);

        // Ensure that the kind is set to 'Unspecified' since the parsed time isn't representing a time zone adjusted time.
        parsed = DateTime.SpecifyKind(parsed, DateTimeKind.Unspecified);

        // Convert the unspecified date time to a UTC time within the given time zone.
        var utc = TimeZoneInfo.ConvertTimeToUtc(parsed, timeZone);

        // Patterns.Date.FullUtc = @"yyyy-MM-dd'T'HH:mm:ss.fff'Z'"
        return utc.ToString(Patterns.Date.FullUtc);
    }
}

The method can be used something like this (XUnit test).

public class DateTimeStringConverterTests  
{
    [Theory]
    [InlineData("1986-06-01 13:37", "W. Europe Standard Time", "1986-06-01T11:37:00.000Z")] // Daylight Saving Time (DST) (UTC+2h)
    [InlineData("1986-04-01 13:37", "Europe/Stockholm", "1986-04-01T11:37:00.000Z")] // Daylight Saving Time (DST) (UTC+2h)
    [InlineData("1986-11-01 13:37", "W. Europe Standard Time", "1986-11-01T12:37:00.000Z")] // (UTC+1h)
    [InlineData("1986-01-01 13:37", "Europe/Stockholm", "1986-01-01T12:37:00.000Z")] // (UTC+1h)
    [InlineData("1986-01-01 13:37", "America/Toronto", "1986-01-01T18:37:00.000Z")] // (UTC-5h)
    [InlineData("2018-04-01 13:37:12", "America/Toronto", "2018-04-01T17:37:12.000Z")] // Daylight Saving Time (DST) (UTC-4h)
    public void Can_Convert_Local_Date_Time_String_To_ISO8601_Utc_String(string givenLocalTime, string givenTimeZoneName, string expectedUtcTime)
    {
        var subject = new DateTimeStringConverter();

        var result = subject.ToUtcString(givenLocalTime, givenTimeZoneName);

        result.Should().Be(expectedUtcTime);
    }
}

Thanks to TimeZoneConverter.

comments powered by Disqus