不正な日付形式のMVC DateTimeバインディング


132

Asp.net-MVCでは、DateTimeオブジェクトの暗黙的なバインディングが可能になりました。私は次のように行動します

public ActionResult DoSomething(DateTime startDate) 
{ 
... 
}

これは、ajax呼び出しからの文字列をDateTimeに正常に変換します。ただし、日付形式はdd / MM / yyyyを使用しています。MVCはMM / dd / yyyyに変換しています。たとえば、文字列「09/02/2009」を含むアクションの呼び出しを送信すると、DateTimeは「02/09/2009 00:00:00」、またはローカル設定では9月2日になります。

日付形式のために自分のモデルバインダーをロールバックしたくありません。しかし、MVCがこれを行うことができる場合は、文字列を受け入れるようにアクションを変更してからDateTime.Parseを使用する必要がないようです。

DateTimeのデフォルトモデルバインダーで使用される日付形式を変更する方法はありますか?とにかく、デフォルトのモデルバインダーがローカリゼーション設定を使用するべきではありませんか?


ちょっと..ちょうどあなたのシステムの日付形式を変更してください-DD / MM / yyyyをMM / DD / yyyyにしてそれを行ってください。私も同じ問題を抱えています。システムの日付形式を変更することで解決しました。
バニー2015

@bannyアプリケーションがグローバルであり、すべてが同じ日付時刻形式ではない可能性がある場合、どのようにこれを行うことができますか?、あなたは...行くと、すべてのものの日時書式を変更すると仮定していない
ラヴィメータに

これらの答えのどれも私を助けていません。フォームはローカライズする必要があります。一部のユーザーは、一方の日付を使用している場合と、もう一方の方法を使用している場合があります。web.configで何かを設定する。またはglobal.asaxは役に立ちません。私はより良い答えを探し続けますが、1つの方法は、c#に戻るまで日付を文字列として処理することです。
astrosteve 2016年

回答:


164

私はちょうどより完全なグーグルでこれに対する答えを見つけました:

Melvyn Harbourは、MVCが日付と同じように機能する理由と、必要に応じてこれをどのようにオーバーライドできるかについて、徹底的に説明しています。

http://weblogs.asp.net/melvynharbour/archive/2008/11/21/mvc-modelbinder-and-localization.aspx

解析する値を探すとき、フレームワークは特定の順序、つまり次のように調べます。

  1. RouteData(上記には表示されていません)
  2. URIクエリ文字列
  3. リクエストフォーム

ただし、これらの最後のものだけが文化を認識します。ローカリゼーションの観点から、これには非常に正当な理由があります。私がオンラインで公開する航空会社のフライト情報を表示するWebアプリケーションを作成したと想像してください。その日のリンク(おそらくhttp://www.melsflighttimes.com/Flights/2008-11-21のようなもの)をクリックして特定の日付のフライトを検索し、そのリンクを同僚にメールで送信したいアメリカ。両方が同じページのデータを表示することを保証できる唯一の方法は、InvariantCultureが使用されている場合です。対照的に、フォームを使用してフライトを予約している場合、すべてがタイトなサイクルで発生しています。データは、フォームに書き込まれるときにCurrentCultureを尊重することができるため、フォームから戻ってくるときにそれを尊重する必要があります。


しましょう。質問を投稿してから48時間、その機能は無効になります。
サムウェッセル

43
技術的にはこれが正しいことに強く反対します。モデルバインダーは、常にPOSTおよびGETと同じように動作する必要があります。不変のカルチャーがGETの方法である場合、POSTでもそれを行います。http動詞に応じて動作を変更することは意味がありません。
Bart Calixto 2013

質問があります。別の国でホストされているウェブサイトMM/dd/yyyyです。フォーマットが必要です。それ以外の場合は検証エラーが表示されますThe field BeginDate must be a date.。どうすればサーバーはdd/MM/yyyyフォーマットを受け入れることができますか?
shaijut 2015

URLパラメータは、ISO標準のフォーマットを使用する場合のように、明確でなければなりません。その後、文化の設定は重要ではありません。
nforss

これは原因を説明するリンクを提供しますが、実際にはこの問題に対する最も簡単な解決策を提供しません(スクリプトからISO日時文字列を投稿するなど)。この方法は常に機能し、特定のカルチャを設定する必要はありませんサーバーまたはサーバーとクライアントの間で日時の形式が同じであることを確認します。
絶望的な

36

私はあなたの文化をグローバルに設定します。ModelBinderがそれを手に入れました!

  <system.web>
    <globalization uiCulture="en-AU" culture="en-AU" />

または、このページでこれを変更するだけです。
しかし、グローバルにweb.configで私はより良いと思います


27
私のためではなかった。2010年10月23日を過ぎると、日付はnullのままです。
GONeale 2010年

私はそれが最も簡単な解決策だと思います。私にとっては、すべてのDate.ToString()でフォーマットを変更します。バインディングでも機能すると思いますが、チェックしませんでした。申し訳ありません:-(
msa.im

1
開発サーバーのdateformatをdd / MM / yyyyに設定していますモデルバインダーはMM / dd / yyyy形式を使用しています。web.configの形式をdd / MM / yyyyに設定すると、modelbinderが強制的にヨーロッパ形式を使用するようになりました。私の意見では、サーバーの日付設定を使用する必要があります。とにかくあなたは私の問題を解決しました。
カレル

私のアプリケーションサーバーはUK OSを実行しているUKにあることがわかっているので、どういうわけかそれは奇妙に感じられました...:/
Tallmaris

Azure Webサイトで実行されているMVC4で完全に動作しました
Matty Bear

31

DateTimeモデルプロパティへの短い日付形式のバインドで同じ問題が発生しています。(DateTimeだけでなく)さまざまな例を見てから、次のようにまとめました。

using System;
using System.Globalization;
using System.Web.Mvc;

namespace YourNamespaceHere
{
    public class CustomDateBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (controllerContext == null)
                throw new ArgumentNullException("controllerContext", "controllerContext is null.");
            if (bindingContext == null)
                throw new ArgumentNullException("bindingContext", "bindingContext is null.");

            var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            if (value == null)
                throw new ArgumentNullException(bindingContext.ModelName);

            CultureInfo cultureInf = (CultureInfo)CultureInfo.CurrentCulture.Clone();
            cultureInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";

            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

            try
            {
                var date = value.ConvertTo(typeof(DateTime), cultureInf);

                return date;
            }
            catch (Exception ex)
            {
                bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
                return null;
            }
        }
    }

    public class NullableCustomDateBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (controllerContext == null)
                throw new ArgumentNullException("controllerContext", "controllerContext is null.");
            if (bindingContext == null)
                throw new ArgumentNullException("bindingContext", "bindingContext is null.");

            var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            if (value == null) return null;

            CultureInfo cultureInf = (CultureInfo)CultureInfo.CurrentCulture.Clone();
            cultureInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";

            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

            try
            {
                var date = value.ConvertTo(typeof(DateTime), cultureInf);

                return date;
            }
            catch (Exception ex)
            {
                bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
                return null;
            }
        }
    }
}

ルートなどがグローバルASAXファイルで登録される方法を維持するために、CustomModelBinderConfigという名前のMVC4プロジェクトのApp_Startフォルダーに新しいsytaticクラスも追加しました。

using System;
using System.Web.Mvc;

namespace YourNamespaceHere
{
    public static class CustomModelBindersConfig
    {
        public static void RegisterCustomModelBinders()
        {
            ModelBinders.Binders.Add(typeof(DateTime), new CustomModelBinders.CustomDateBinder());
            ModelBinders.Binders.Add(typeof(DateTime?), new CustomModelBinders.NullableCustomDateBinder());
        }
    }
}

次に、次のように、グローバルASASX Application_Startから静的RegisterCustomModelBindersを呼び出すだけです。

protected void Application_Start()
{
    /* bla blah bla the usual stuff and then */

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

ここで重要なのは、次のような隠しフィールドにDateTime値を書き込む場合です。

@Html.HiddenFor(model => model.SomeDate) // a DateTime property
@Html.Hiddenfor(model => model) // a model that is of type DateTime

私はそれを行い、ページの実際の値は、「dd / MM / yyyy hh:mm:ss tt」ではなく「MM / dd / yyyy hh:mm:ss tt」の形式でした。これにより、モデルの検証が失敗するか、間違った日付が返されました(明らかに、日と月の値を入れ替えています)。

多くの頭を悩ませて失敗した後の解決策は、Global.ASAXでこれを行うことにより、すべてのリクエストのカルチャー情報を設定することでした。

protected void Application_BeginRequest()
{
    CultureInfo cInf = new CultureInfo("en-ZA", false);  
    // NOTE: change the culture name en-ZA to whatever culture suits your needs

    cInf.DateTimeFormat.DateSeparator = "/";
    cInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
    cInf.DateTimeFormat.LongDatePattern = "dd/MM/yyyy hh:mm:ss tt";

    System.Threading.Thread.CurrentThread.CurrentCulture = cInf;
    System.Threading.Thread.CurrentThread.CurrentUICulture = cInf;
}

Application_StartまたはSession_Startに貼り付けても、セッションの現在のスレッドに割り当てられるため、機能しません。ご存知のように、ウェブアプリケーションはステートレスであるため、以前にリクエストを処理したスレッドは現在のリクエストを処理するスレッドと同じではないため、カルチャー情報はデジタルスカイのすばらしいGCに送られます。

ありがとうございました:Ivan Zlatev- http ://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/

garik- https: //stackoverflow.com/a/2468447/578208

ドミトリー-https://stackoverflow.com/a/11903896/578208


13

MVC 3では少し異なります。

Getメソッドを備えたコントローラーとビューがあるとします。

public ActionResult DoSomething(DateTime dateTime)
{
    return View();
}

ModelBinderを追加する必要があります

public class DateTimeBinder : IModelBinder
{
    #region IModelBinder Members
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        DateTime dateTime;
        if (DateTime.TryParse(controllerContext.HttpContext.Request.QueryString["dateTime"], CultureInfo.GetCultureInfo("en-GB"), DateTimeStyles.None, out dateTime))
            return dateTime;
        //else
        return new DateTime();//or another appropriate default ;
    }
    #endregion
}

Global.asaxのApplication_Start()のコマンド

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());

これはまともな出発点ですがIModelBinder、特に検証に関しては、を正しく実装していません。また、の名前DateTimedateTimeの場合にのみ機能します。
Sam

2
また、私は見つけたDateTime?あなたに別の呼び出しを追加した場合のみ、仕事だModelBinders.Binders.Addtypeof(DateTime?)
2014

8

独自のモデルバインダーを作成しなくても、複数の異なる形式を解析できる場合があることにも注意してください。

たとえば米国では、次のすべての文字列は同等であり、自動的に同じ DateTime値にバインドされます。

/ company / press / may%2001%202008

/ company / press / 2008-05-01

/ company / press / 05-01-2008

yyyy-mm-ddの方が移植性が高いため、使用することを強くお勧めします。複数のローカライズされたフォーマットの処理に対処したくない場合。1月5日ではなく5月1日にフライトを予約すると、大きな問題が発生します。

注意:yyyy-mm-ddがすべてのカルチャで普遍的に解析されるので、知っている誰かがコメントを追加できるかどうかは明確ではありません。


3
誰もyyyy-MM-ddは普遍的ではないと言っているので、私はそうだと思います。
deerchao

私の意見では、モデルバインダーを使用するよりも優れています。.datepicker( "option"、 "dateFormat"、 "yy-mm-dd")または単にデフォルトに設定します。
Bart Calixto 2013

6

toISOString()を使用してみてください。ISO8601形式で文字列を返します。

GETメソッド

JavaScript

$.get('/example/doGet?date=' + new Date().toISOString(), function (result) {
    console.log(result);
});

c#

[HttpGet]
public JsonResult DoGet(DateTime date)
{
    return Json(date.ToString(), JsonRequestBehavior.AllowGet);
}

POSTメソッド

JavaScript

$.post('/example/do', { date: date.toISOString() }, function (result) {
    console.log(result);
});

c#

[HttpPost]
public JsonResult Do(DateTime date)
{
     return Json(date.ToString());
}


1
  public class DateTimeFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.RequestType == "GET")
        {

            foreach (var parameter in filterContext.ActionParameters)
            {
                var properties = parameter.Value.GetType().GetProperties();

                foreach (var property in properties)
                {
                    Type type = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;

                    if (property.PropertyType == typeof(System.DateTime) || property.PropertyType == typeof(DateTime?))
                    {
                        DateTime dateTime;

                        if (DateTime.TryParse(filterContext.HttpContext.Request.QueryString[property.Name], CultureInfo.CurrentUICulture, DateTimeStyles.None, out dateTime))
                            property.SetValue(parameter.Value, dateTime,null);
                    }
                }

            }
        }
    }
}

これは機能しません。dd-MM-yyyyはまだ認識されていません
jao 2014年

1
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    var str = controllerContext.HttpContext.Request.QueryString[bindingContext.ModelName];
    if (string.IsNullOrEmpty(str)) return null;
    var date = DateTime.ParseExact(str, "dd.MM.yyyy", null);
    return date;
}

1
いくつかの説明を追加して、回答をより豊かにする必要があります。
Alexandre Lavoie 2013年

メモリからは、これは組み込みモデルバインダーの動作と一致しないため、検証のために型指定された値を保持するなど、同じ二次的な動作をしない場合があります。
Sam

1

私は自分のカスタムベースコントローラーを設定CurrentCultureしましたCurrentUICulture

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.