Передача 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.
Ответы
Ответ 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
Вот специальные инструкции для этого случая:
В своем классе "WebApiConfig" добавьте следующее:
var provider = new SimpleModelBinderProvider(typeof(DateTime),new UtcDateTimeModelBinder());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
Создайте новый класс с именем 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);