Подтвердить что ты не робот

Передача UTC DateTime в API API HttpGet приводит к местному времени

Я пытаюсь передать дату UTC в качестве параметра строки запроса методу веб-API. URL-адрес выглядит как

/api/order?endDate=2014-04-01T00:00:00Z&zoneId=4

Подпись метода выглядит как

[HttpGet]
public object Index(int zoneId, DateTime? endDate = null)

Дата приходит как 31/03/2014 8:00:00 PM, но я бы хотел, чтобы она появилась как 01/04/2014 12:00:00 AM

Мой JsonFormatter.SerializerSettings выглядит следующим образом

new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    DateTimeZoneHandling = DateTimeZoneHandling.Utc,
    DateFormatHandling = DateFormatHandling.IsoDateFormat
};

ИЗМЕНИТЬ № 1: Я заметил, что когда я отправляю POST 2014-04-01T00:00:00Z, он будет сериализоваться в формате UTC DateTime в С#. Однако я нашел работу над endDate.Value.ToUniversalTime(), чтобы преобразовать ее, хотя мне показалось странным, как она работает для POST, но не GET.

4b9b3361

Ответ 1

Значение параметра строки запроса, которое вы отправляете 2014-04-01T00:00:00Z, - это время UTC. Таким образом, то же самое переводится на время, основанное на ваших локальных часах, и если вы вызываете ToUniversalTime(), он преобразуется обратно в UTC.

Итак, в чем именно вопрос? Если возникает вопрос, почему это происходит, если он отправлен как строка запроса, но не когда отправлен в тело запроса, ответ на этот вопрос заключается в том, что ASP.NET Web API связывает путь URI, строку запроса и т.д., Используя привязку модели и использование тела привязка параметров. Для последнего используется медиаформат. Если вы отправляете JSON, используется форматирование носителей JSON, и оно основано на JSON.NET.

Поскольку вы указали DateTimeZoneHandling.Utc, он использует этот параметр, и вы получите нужный тип даты. BTW, если вы измените этот параметр на DateTimeZoneHandling.Local, тогда вы увидите то же поведение, что и привязка к модели.

Ответ 2

Если вы хотите, чтобы преобразование было прозрачным, вы можете использовать пользовательский TypeConverter:

public sealed class UtcDateTimeConverter : DateTimeConverter
{
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return ((DateTime)base.ConvertFrom(context, culture, value)).ToUniversalTime();
    }
}

и подключите его, используя:

TypeDescriptor.AddAttributes(typeof(DateTime), new TypeConverterAttribute(typeof(UtcDateTimeConverter)));

Затем параметр строки запроса будет создан как DateTimeKind.Utc.

Ответ 3

В результате я использовал метод ToUniversalTime() при входе параметров.

Ответ 4

Таким образом, для тех из вас, кто не хочет переопределять преобразование строки в дату во всем приложении, а также не хочет помнить о необходимости изменять каждый метод, который принимает параметр даты, вот как вы это делаете для Проект веб-API.

В конечном счете, общие инструкции приходят отсюда:

https://docs.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api#model-binders

Вот специальные инструкции для этого случая:

  1. В своем классе "WebApiConfig" добавьте следующее:

        var provider = new SimpleModelBinderProvider(typeof(DateTime),new UtcDateTimeModelBinder());
        config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
    
  2. Создайте новый класс с именем UtcDateTimeModelBinder:

    public class UtcDateTimeModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext,
            ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType != typeof(DateTime)) return false;
    
            var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (val == null)
            {
                return false;
            }
    
            var key = val.RawValue as string;
            if (key == null)
            {
                bindingContext.ModelState.AddModelError(bindingContext.ModelName,
                    "Wrong value type");
                return false;
            }
    
            DateTime result;
            if (DateTime.TryParse(key, out result))
            {
                bindingContext.Model = result.ToUniversalTime();
                return true;
            }
    
            bindingContext.ModelState.AddModelError(bindingContext.ModelName,
                "Cannot convert value to Utc DateTime");
            return false;
        }
    }
    

Ответ 5

Я наконец нашел этот код, он не является основным ответом, но в некоторых случаях его можно использовать:

var dateUtc = TimeZoneInfo.ConvertTimeToUtc(date);