我正在向MVC控制器发布一个对象。该对象包含一个名为StartDt的字段,在客户端上它是本地时间的javascript Date对象。
当我在对象上调用JSON.stringify并使用jQuery的ajax方法将其POST到服务器时,我可以在Firebug中看到发送到服务器的是一个ISO字符串,如“1900-12-31T13:00:00.000Z” “我认为应该是UTC格式的当地时间。
当我查看控制器中的DateTime字段时,它看起来像是回到本地时间而不是UTC。我该如何解决这个问题?
我想存储来自客户端的日期的UTC版本。
答案 0 :(得分:19)
我在谷歌上发现了一个关于符合ISO 8601 DateTime Model Binder的代码的要点,然后将其修改为:
public class DateTimeBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var name = bindingContext.ModelName;
var value = bindingContext.ValueProvider.GetValue(name);
if (value == null)
return null;
DateTime date;
if (DateTime.TryParse(value.AttemptedValue, null, DateTimeStyles.RoundtripKind, out date))
return date;
else
return base.BindModel(controllerContext, bindingContext);
}
}
我相信gist代码限制太多 - 它在秒上需要6个小数位,否则它将不接受时间戳。这使用TryParse而不是TryParseExact,因此它在技术上会接受大量的时间戳类型。重要的是它使用DateTimeStyles.RoundtripKind来尊重Z隐含的时区。所以这在技术上不再是ISO 8601特定的实现。
然后,您可以使用模型绑定器属性或使用App_Start中的此代码段将其挂接到MVC管道中:
var dateTimeBinder = new DateTimeBinder();
ModelBinders.Binders.Add(typeof(DateTime), dateTimeBinder);
ModelBinders.Binders.Add(typeof(DateTime?), dateTimeBinder);
答案 1 :(得分:11)
此问题在ASP.NET Core 2.0中仍然存在。以下代码将解析它,支持ISO 8601基本和扩展格式,正确保留值并正确设置DateTimeKind
。这与JSON.Net的解析的默认行为一致,因此它使您的模型绑定行为与系统的其余部分保持一致。
首先,添加以下模型绑定器:
public class DateTimeModelBinder : IModelBinder
{
private static readonly string[] DateTimeFormats = { "yyyyMMdd'T'HHmmss.FFFFFFFK", "yyyy-MM-dd'T'HH:mm:ss.FFFFFFFK" };
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var stringValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).FirstValue;
if (bindingContext.ModelType == typeof(DateTime?) && string.IsNullOrEmpty(stringValue))
{
bindingContext.Result = ModelBindingResult.Success(null);
return Task.CompletedTask;
}
bindingContext.Result = DateTime.TryParseExact(stringValue, DateTimeFormats,
CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result)
? ModelBindingResult.Success(result)
: ModelBindingResult.Failed();
return Task.CompletedTask;
}
}
然后添加以下模型绑定提供程序:
public class DateTimeModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (context.Metadata.ModelType != typeof(DateTime) &&
context.Metadata.ModelType != typeof(DateTime?))
return null;
return new BinderTypeModelBinder(typeof(DateTimeModelBinder));
}
}
然后在Startup.cs
文件中注册提供商:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
...
options.ModelBinderProviders.Insert(0, new DateTimeModelBinderProvider());
...
}
}
答案 2 :(得分:8)
您可能必须使用DateTime.ToUniversalTime()方法来取回UTC时间。
答案 3 :(得分:8)
我创建了这个小属性。
public class ConvertDateToUTCAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var dateArgs =
filterContext.ActionParameters.Where(
x => x.Value != null && x.Value.GetType().IsAssignableFrom(typeof(DateTime))).ToList();
foreach (var keyValuePair in dateArgs)
{
var date = (DateTime) keyValuePair.Value;
if (date.Kind == DateTimeKind.Local)
filterContext.ActionParameters[keyValuePair.Key] = date.ToUniversalTime();
}
base.OnActionExecuting(filterContext);
}
}
因此,这将留下未指定或已经单独使用Utc的日期。您可以将它应用于整个控制器。
答案 4 :(得分:4)
或者,您可以将绑定对象指定为DateTimeOffset
而不是DateTime
,并且不会进行自动转换。只要输入字符串的后缀为Z
,您就应该获得原始日期,其偏移量为+00:00
。
答案 5 :(得分:1)
我认为这已在 ASP.NET Core 5.0 中解决。