2012-04-24 18 views
13

MVCコントローラにオブジェクトをポストしています。オブジェクトにはStartDtというフィールドがあり、クライアントには現地時間でのjavascriptのDateオブジェクトです。ASP.Net MVCモデルバインダーを作成する方法は、受信日をUTCとして扱いますか?

オブジェクトのJSON.stringifyを呼び出してjQueryのajaxメソッドを使用してサーバーにPOSTすると、サーバーに送信される内容が "1900-12-31T13:00:00.000"のようなISO文字列であることがFirebugでわかります私が信じるZはUTC形式の現地時間でなければなりません。

私のコントローラのDateTimeフィールドを見ると、UTCではなくローカルタイムに戻るように見えます。これをどうすれば解決できますか?

クライアントからの日付のUTCバージョンを保存します。

答えて

3

UTC時刻を取得するには、DateTime.ToUniversalTime()メソッドを使用する必要があります。

+7

この私たちはそれについて考えなくてもいいように、インフラストラクチャーによってうまく処理されるようなものです*毎回* DateTimeを処理します。 –

+0

@DavidBoike:http://www.martin-brennan.com/custom-utc-datetime-model-binding-mvc/ –

4

私はこの小さな属性を作成しました。

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だけの日付が残されます。それをコントローラ全体に適用することができます。

+0

DateTimeプロパティを内部に持つカスタムクラスを渡すとどうなりますか? IsAssignableFrom(typeof(DateTime))は動作しません。 –

+1

Yehこれは、アクションの直接のパラメータである日付に対してのみ機能します。もっと堅牢な選択肢はおそらくモデルバインドを使うことでしょう。以下のバインダーと私のコメントを参照してください。 – Sam

+0

なぜdownvotes?これは、モデルバインダーの作成に代わる有効な方法です。 – Sam

16

Iに準拠DateTime Model BinderISO 8601ためのコードをGoogleで要点を見つけ、その後、このようにそれを修正:私は要点コードが厳しすぎると考えてい

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); 
    } 
} 

- それは秒に小数点以下6桁を望んでいますか、タイムスタンプを受け入れません。これはTryParseExactの代わりにTryParseを使用するため、技術的には多くのタイムスタンプ型を受け入れます。重要なのは、DateTimeStyles.RoundtripKindを使用してZが意味するタイムゾーンを尊重することです。これは、技術的にはISO 8601固有の実装ではなくなりました。

その後、モデルバインダー属性を持つかApp_Startこのスニペットを使ってMVCパイプラインにこれ​​をフックすることができます:

var dateTimeBinder = new DateTimeBinder(); 

ModelBinders.Binders.Add(typeof(DateTime), dateTimeBinder); 
ModelBinders.Binders.Add(typeof(DateTime?), dateTimeBinder); 
+4

datetime値自体を解析する必要はありません。質問に記載されているように、問題は日付の解析ではなく、解析されたutcデータをサーバのローカルdatetimeに変換するデフォルトのモデルバインダの動作です。私はモデルのバインダーはおそらくアクションフィルターよりも良いフィット感だと思っています。しかし、すべてがbase.BindModelコールから値を取得する必要があります。値がKind = Localであるかどうかをチェックし、それをUtcに変換して返します。 – Sam

+1

いいえ、.NETがISO Dateを「DateTimeStyles.RoundtripKind」でデフォルトで解析しないのはなぜかと思います。 –

+0

完全な画像が分からないと、これがデフォルトになるはずです。 ISO 8601で文字列を送信すると、正しく解析され、サーバーの現地時間への変換も正しく行われます。 – beruic

2

この問題は、ASP.NETコア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()); 

     ... 
    } 
} 
+0

これは素晴らしいことです。 'ConfigureServices'を実行すると、' ModelBinderProviders'の問題の順番が、コレクションに追加するのではなく、挿入を行う理由と思われます。 –

関連する問題