Search code examples
.netdatetimetimezoneglobalizationazure-cloud-services

ValueProviderResult.ConvertTo returns different dates locally and in the cloud


I have next custom model binder:

public class WebApiModelBinderDateTime : IModelBinder
{
    public bool BindModel(HttpActionContext executionContext, ModelBindingContext bindingContext)
    {
        ValueProviderResult value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (value == null)
        {
            return false;
        }

        DateTime date = (DateTime)value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
        date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
        bindingContext.Model = date;

        return true;
    }
}

Culture is setup in web.config like this:

<globalization culture="en-GB" enableClientBasedCulture="false" uiCulture="en-GB" />

and in global.asax like this:

CultureInfo culture = CultureInfo.CreateSpecificCulture("en-GB");
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;

The problem is that this binder returns different dates locally and remotely.

E.g. 2019-06-03T09:53:26.651Z will be converted to 2019-06-03 10:53:26 locally but to 2019-06-03 09:53:26 in the cloud(it's an old system which is deployed to Azure Cloud Services).

I tried to debug both locally and remotely but has not found any differences between CultureInfo.CurrentCulture, ModelBindingContext, ValueProvider or ValueProviderResult.

What else can cause different timezones?


Solution

  • A few things:

    • Culture settings and time zones are completely orthogonal concepts. Culture can affect the formatting of a string, or which calendar system is applied, but it does not influence time zones. They have nothing to do with each other.

    • You're specifying DateTimeKind.Utc, thus everywhere you use this the resulting value will be serialized with a trailing Z indicating UTC. If indeed the values are UTC based, then everything is fine and there's nothing left to do. However if you are applying these to arbitrary values that may represent time in a different time zone, setting UTC kind is not going to help. Instead, you might consider using a DateTimeOffset type rather than a DateTime.

    • You didn't show us how you apply this model binder or how you consume the API. Assuming that your API is correctly delivering the UTC-based result (with the Z), then your concern about time zones is entirely based on the interpretation by the client-side code.

    • To answer directly "What else can cause different time zones?"

      • If you call DateTime.Now or DateTimeOffset.Now or TimeZoneInfo.Local or use DateTimeKind.Local, .ToLocalTime(), etc., as well as several APIs that convert from local time like .ToUniversalTime(), then the server's local time zone setting (from the date and time control panel or settings page) is what controls that time zone. It is a server-wide setting for all applications on the server. You can also view or control it with tzutil.exe on the command line.
      • If you follow best practices of not relying on the server's time zone setting, (by using only UTC based APIs or APIs that use a specific time zone), then you don't have this concern.
      • Don't forget that the world is not all in one time zone. Thus, if your server emits time in one time zone (such as UTC) and your client code is either explicitly or implicitly converting it to another time zone (such as its local time), then it's perfectly normal for the values to differ.