Scr.at
Scr.at

Reputation: 71

Asp.net Web API DateTimeOffset set offset from http header

In my project I'm using the DateTimeOffset datatype for parameters. I realised the offset is always "0". But I need the local time on serverside. To fix this I send the timezoneoffset in http header and will read this on creating of the DateTimeOffset parameter. Therefore I created a ModelBinder to convert the request into a DateTimeOffset and get the timezoneoffset from the header. That works, but if any action has a complex object with a DateTimeOffset property (sending via http POST to web api in JSON format), so the modelbinder wasn't executed and the offset in the DateTimeOffset property is "0". Is there any way to customize the behaviour of creating objects from JSON, so that I can get the timezoneoffset from the http header?

public class DateTimeOffsetModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof(DateTimeOffset))
        {
            return false;
        }

        var timezoneOffsetMinutes = actionContext.Request.Headers
            .Where(h => String.Equals(h.Key, "TimezoneOffsetMinutes", StringComparison.InvariantCultureIgnoreCase))
            .SelectMany(h => h.Value)
            .FirstOrDefault()
            .ParseAsNullableInteger();

        if (!timezoneOffsetMinutes.HasValue)
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Http header dosn't contains 'timezoneoffsetminutes'.");

            return false;
        }

        TimeSpan timezoneOffset = TimeSpan.FromMinutes(timezoneOffsetMinutes.Value);

        ValueProviderResult value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        DateTime date;

        if (!DateTime.TryParse(value.AttemptedValue, out date))
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Can't parse parameter '" + bindingContext.ModelName + "' to DateTime.");

            return false;
        }

        date = date.ToUniversalTime();

        bindingContext.Model = new DateTimeOffset(date).ToOffset(timezoneOffset);

        return true;
    }
}

Upvotes: 3

Views: 2212

Answers (1)

Scr.at
Scr.at

Reputation: 71

Now I have a solution: I implemented a own JsonConverter based of the IsoDateTimeConverter wich loads the timezoneoffset from the http header an set it into DateTimeOffset correctly.

public class DateTimeOffsetConverter : IsoDateTimeConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object baseResult = base.ReadJson(reader, objectType, existingValue, serializer);
        DateTimeOffset? date = baseResult as DateTimeOffset?;

        if (date.HasValue)
        {
            //timezone offset
            int? timezoneOffsetMinutes = HttpContext.Current.Request.Headers["TimezoneOffsetMinutes"].ParseAsNullableInteger();
            TimeSpan? timezoneOffset = timezoneOffsetMinutes.HasValue
                ? TimeSpan.FromMinutes(timezoneOffsetMinutes.Value)
                : (TimeSpan?)null;

            return timezoneOffset.HasValue
                ? date.Value.ToOffset(timezoneOffset.Value)
                : date.Value;
        }

        return baseResult;
    }
}

Upvotes: 4

Related Questions