날짜 형식이 잘못된 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특히 유효성 검사와 관련하여 올바르게 구현하지 않습니다 . 또한의 이름 DateTimedateTime 인 경우에만 작동합니다 .
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

설정 CurrentCultureCurrentUICulture사용자 정의 기본 컨트롤러

    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.