[asp.net-mvc] 날짜 형식이 잘못된 MVC DateTime 바인딩

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의 기본 모델 바인더에 사용되는 날짜 형식을 변경하는 방법이 있습니까? 기본 모델 바인더가 현지화 설정을 사용해서는 안됩니까?



답변

방금 더 철저한 인터넷 검색으로 이에 대한 답을 찾았습니다.

Melvyn Harbor는 MVC가 날짜와 작동하는 방식과 필요한 경우이를 무시할 수있는 방법에 대한 철저한 설명을 제공합니다.

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

구문 분석 할 값을 찾을 때 프레임 워크는 특정 순서 즉, 다음을 찾습니다.

  1. RouteData (위에 표시되지 않음)
  2. URI 쿼리 문자열
  3. 요청 양식

그러나 이들 중 마지막 만이 문화를 인식 할 것입니다. 현지화 관점에서 볼 때 매우 좋은 이유가 있습니다. 온라인으로 발행 한 항공사 항공편 정보를 보여주는 웹 응용 프로그램을 작성했다고 가정하십시오. 해당 날짜의 링크 (예 : http://www.melsflighttimes.com/Flights/2008-11-21 ) 를 클릭하여 특정 날짜의 항공편을 찾은 다음 해당 동료에게 해당 링크를 이메일로 보내려고합니다. 미국. InvariantCulture를 사용하는 경우 동일한 데이터 페이지를 모두 볼 수있는 유일한 방법입니다. 반대로, 항공편을 예약하기 위해 양식을 사용하는 경우 모든 과정이 빡빡하게 진행됩니다. 데이터는 양식에 기록 될 때 CurrentCulture를 존중할 수 있으므로 양식에서 돌아올 때이를 존중해야합니다.


답변

전 세계적으로 문화를 설정하겠습니다. ModelBinder가 그것을 픽업!

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

또는이 페이지에 대해 이것을 변경하십시오.
그러나 전 세계적으로 web.config에서는 더 나은 것으로 생각합니다.


답변

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

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

그런 다음 Global 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


답변

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


답변

자신 만의 모델 바인더를 만들지 않아도 여러 다른 형식을 구문 분석 할 수 있다는 점도 주목할 가치가 있습니다.

예를 들어 미국에서는 다음 문자열이 모두 동일하며 자동으로 동일한 DateTime 값에 바인딩됩니다 .

/ company / press / may % 2001 % 202008

/ company / press / 2008-05-01

/ company / press / 05-01-2008

이식성이 훨씬 뛰어나 yyyy-mm-dd를 사용하는 것이 좋습니다. 현지화 된 여러 형식을 처리하는 것을 정말로 원하지 않습니다. 누군가 1 월 5 일 대신 5 월 1 일에 항공편을 예약하면 큰 문제가 생길 것입니다!

NB : yyyy-mm-dd가 모든 문화권에서 보편적으로 파싱되어 있다면 아는 사람이 의견을 추가 할 수 있는지 여부는 확실하지 않습니다.


답변

toISOString ()을 사용해보십시오. ISO8601 형식의 문자열을 반환합니다.

GET 방법

자바 스크립트

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

씨#

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

POST 방법

자바 스크립트

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

씨#

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


답변

MVC4에서 아래 구성을 설정했으며 매력처럼 작동합니다.

<globalization uiCulture="auto" culture="auto" />