날짜 형식이 잘못된 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로 변경하고 .. 또한 같은 문제가 있습니다. 시스템 날짜 형식을 변경하여 문제를 해결했습니다.
— banny

@banny 응용 프로그램이 전역 적이며 모두 날짜 시간 형식이 같지 않은 경우 어떻게 할 수 있습니까? , 당신은 가서 모든 날짜 날짜 형식을 변경한다고 가정하지 않습니다 ..
— Ravi Mehta

이 답변들 중 어느 것도 나를 도와주지 않습니다. 양식을 현지화해야합니다. 어떤 사용자는 날짜를 다른 방법으로 가질 수 있습니다. web.config에서 무언가 설정하기. 또는 global.asax는 도움이되지 않습니다. 더 나은 답변을 계속 찾으려고하지만 한 가지 방법은 c #으로 돌아갈 때까지 날짜를 문자열로 처리하는 것입니다.
— astrosteve

답변:


164

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

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를 존중할 수 있으므로 양식에서 돌아올 때이를 존중해야합니다.


할 것이다. 해당 기능은 질문을 게시 한 후 48 시간 동안 비활성화됩니다.
— Sam Wessel

43
기술적으로 이것이 정확하다는 데는 동의하지 않습니다. 모델 바인더는 항상 POST 및 GET과 동일하게 작동해야합니다. 변하지 않는 문화가 GET으로가는 길이라면 POST 도하십시오. http 동사에 따라 동작을 변경하는 것은 의미가 없습니다.
— Bart Calixto

질문이 있습니다. 다른 국가에서 호스팅되는 웹 사이트에 다른 MM/dd/yyyy형식이 필요 합니다. 다른 The field BeginDate must be a date.방법으로 유효성 검사 오류가 표시 dd/MM/yyyy됩니다. 형식 을 수락하려면 어떻게합니까?
— shaijut

URL 표준은 ISO 표준 형식을 사용하는 것과 같이 명확해야합니다. 문화 설정은 중요하지 않습니다.
— nforss

이것은 원인을 설명하는 링크를 제공하지만 실제로이 문제에 대한 가장 쉬운 해결책을 제공하지는 않습니다 (예 : 스크립트에서 ISO 날짜 시간 문자열을 게시하는 것과 같이).이 방법은 항상 작동하며 서버 또는 클라이언트와 날짜 시간 형식이 동일한 지 확인하십시오.
— 희망없는

36

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

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

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


27
나를 위해하지 않았다. 2010 년 10 월 23 일을 통과하면 날짜가 여전히 null로 표시됩니다.
— GONeale

가장 쉬운 해결책이라고 생각합니다. 나를 위해 모든 Date.ToString ()의 형식이 변경됩니다. 나는 그것이 바인딩과 함께 작동 할 것이라고 생각하지만 확인하지 않았다. :-(
— msa.im

1
dev 서버 날짜 형식을 dd / MM / yyyy로 설정했습니다. 모델 바인더가 MM / dd / yyyy 형식을 사용했습니다. web.config의 형식을 dd / MM / yyyy로 설정하면 이제 모델 바인더가 유럽 형식을 사용해야합니다. 제 생각에는 서버의 날짜 설정을 사용해야합니다. 어쨌든 내 문제를 해결했다.
— Karel

그것은 나를 위해 완벽하게 작동했습니다 ... 어쨌든 내 응용 프로그램 서버가 영국 OS를 실행하는 영국에 있다는 것을 알기 때문에 이상한 느낌이 들었습니다 ... : /
— Tallmaris

Azure 웹 사이트에서 실행되는 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;
            }
        }
    }
}

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


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특히 유효성 검사와 관련하여 올바르게 구현하지 않습니다 . 또한의 이름 DateTime이 dateTime 인 경우에만 작동합니다 .
— Sam

2
또한 with으로 DateTime?다른 호출을 추가해야 작동하는 것으로 나타났습니다 . ModelBinders.Binders.Addtypeof(DateTime?)
— Sam

8

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

예를 들어 미국에서는 다음 문자열이 모두 동일하며 자동으로 동일한 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가 모든 문화권에서 보편적으로 파싱되어 있다면 아는 사람이 의견을 추가 할 수 있는지 여부는 확실하지 않습니다.


3
아무도 yyyy-MM-dd가 보편적이지 않다고 말하지 않기 때문에 나는 그렇게 생각합니다.
— deerchao

이것은 제 생각에 모델 바인더를 사용하는 것보다 낫습니다. .datepicker ( "option", "dateFormat", "yy-mm-dd") 또는 기본값으로 설정하십시오.
— Bart Calixto

6

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


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

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

메모리에서 이는 기본 제공 모델 바인더의 작동 방식과 일치하지 않으므로 유효성 검증을 위해 입력 한 값을 유지하는 것과 동일한 2 차 동작이 없을 수 있습니다.
— 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.