Forey
Forey

Reputation: 81

Strange error with converting from UTC to TimeZone

I have a problem with converting from UTC time to the respective local time.

What I have is UTC Time and Location Time Zone in string (Like "Eastern Standard Time").

My code:

private static DateTime? LocalTimeConvert(string locationTimeZone, DateTime? dateTimeUtc)
{

    var dateTimeUnspec = DateTime.SpecifyKind(dateTimeUtc.Value, DateTimeKind.Unspecified);
    DateTime utcDateTime = TimeZoneInfo.ConvertTime(dateTimeUnspec, TimeZoneInfo.FindSystemTimeZoneById(locationTimeZone));

    return utcDateTime;
}

public static T LocalTime<T>(T value, string locationTimeZone)
{
    if (value.GetType().IsGenericType && value.GetType().GetGenericTypeDefinition() == typeof(List<>))
    {
        IList collection = (IList)value;

        foreach (var element in collection)
        {
            VariableConvertion(element, locationTimeZone);
        }
    }
    else
    {
        VariableConvertion(value, locationTimeZone);
    }

    return value;
}


private static T VariableConvertion<T>(T value, string locationTimeZone)
{
    PropertyInfo[] props = value.GetType().GetProperties();

    foreach (var property in props)
    {
        if (property.PropertyType == typeof(DateTime?) || property.PropertyType == typeof(DateTime))
        {
            if (property.GetValue(value) != null)
            {
                var localTime = LocalTimeConvert(locationTimeZone, DateTime.Parse(property.GetValue(value).ToString()));
                property.SetValue(value, localTime);
            }
        }
    }

    return value;
}

The assumptions were that any value that enters the method is to search for variables that are date and change them with the appropriate local time.

Unfortunately, I have a strange bug where it converts correctly at one time and wrong at the other.

Data from LocalTimeConvert method:

Eastern Standard Time - correct

    System.TimeZoneInfo.FindSystemTimeZoneById returned {(UTC-05:00) Stany Zjednoczone i Kanada (czas wschodni)}    System.TimeZoneInfo
    locationTimeZone    "Eastern Standard Time" string
    dateTimeUtc {19.03.2021 15:43:07}   System.DateTime?
    dateTimeUnspec  {19.03.2021 15:43:07}   System.DateTime
    utcDateTime {19.03.2021 10:43:07}   System.DateTime

Central European Standard Time - incorrect

    System.TimeZoneInfo.FindSystemTimeZoneById returned {(UTC+01:00) Sarajewo, Skopie, Warszawa, Zagrzeb}   System.TimeZoneInfo
    locationTimeZone    "Central European Standard Time"    string
    dateTimeUtc {22.03.2021 12:58:09}   System.DateTime?
    dateTimeUnspec  {22.03.2021 12:58:09}   System.DateTime
    utcDateTime {22.03.2021 12:58:09}   System.DateTime

I am also wondering why I get different time on the server in the USA and different time on the server in Poland. I use pipe in angular for date: 'short': 'UTC' so I supposed to get the exact date from the database without local convertion right? Or the DateTime in C# is also converting the date to server date?

Upvotes: 0

Views: 904

Answers (1)

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241420

Refer to the documentation of the TimeZoneInfo.ConvertTime(DateTime, TimeZoneInfo) method.

In the Remarks section, it says that if the Kind property of the source DateTime is DateTimeKind.Unspecified, that it is assumed to be local. Since you explicitly set the kind to Unspecified in your code, you are not converting from UTC to the named time zone, but you are instead converting from local time to the named time zone.

In this context, "local time" means local to the time zone setting of the computer where the code is executing. So if your server in Poland is set for Poland's time zone, then the conversion to Poland's time will be a no-op.

If your intention is to convert UTC to a specific time zone, then either the source value's Kind should be DateTimeKind.Utc, or you should use ConvertTimeFromUtc instead of ConvertTime (the difference being, Unspecified kind is assumed to be Utc rather than Local).

As an aside - you've got a potential bug with DateTime.Parse(property.GetValue(value).ToString(). Don't ever create a string from an object just to parse it back to an object again. Instead, cast the object to unbox it to the desired type. The tostring/parse approach often is slower and often introduces bugs related to date formats of the current culture.

Upvotes: 1

Related Questions