Yanlış tarih biçimiyle MVC DateTime bağlama


132

Asp.net-MVC artık DateTime nesnelerinin örtük bağlanmasına izin verir. Çizgisinde bir eylemim var

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

Bu, bir ajax çağrısındaki bir dizeyi başarıyla bir DateTime'a dönüştürür. Ancak, gg / AA / yyyy tarih biçimini kullanıyoruz; MVC, AA / gg / yyyy'ye dönüştürülüyor. Örneğin, '09 / 02/2009 'dizesiyle eyleme çağrı göndermek, yerel ayarlarımızda' 02/09/2009 00:00:00 'Tarih Saatine veya 2 Eylül'e neden olur.

Tarih formatı uğruna kendi model bağlayıcımı yuvarlamak istemiyorum. Ancak, bir dizeyi kabul etmek için eylemi değiştirmek ve ardından MVC bunu benim için yapabiliyorsa DateTime.Parse kullanmak gereksiz görünüyor.

DateTime için varsayılan model bağlayıcısında kullanılan tarih biçimini değiştirmenin herhangi bir yolu var mı? Varsayılan model bağlayıcı yine de yerelleştirme ayarlarınızı kullanmamalı mı?


Hey .. Sadece sistem tarih formatınızı - GG / AA / yyyy'yi AA / GG / yyyy olarak değiştirin ve yaptım .. Ben de aynı sorunu yaşıyorum, sistem tarih formatını değiştirerek çözdüm.
banny

@banny eğer uygulama global ise ve her biri aynı tarih ve saat formatına sahip olmayabilir, bunu nasıl yaparsınız? , gidip her bir tarih saat
Ravi Mehta

Bu cevapların hiçbiri bana yardımcı olmuyor. Formun yerelleştirilmesi gerekiyor. Bazı kullanıcılar tarihe bir yönden, diğerleri diğer yöne sahip olabilir. Web.config içinde bir şey ayarlamak. veya global.asax yardımcı olmayacak. Daha iyi bir cevap aramaya devam edeceğim, ancak bir yol, c # 'a dönene kadar tarihi bir dizge olarak ele almak olabilir.
astrosteve

Yanıtlar:


164

Biraz daha kapsamlı bir Google araştırmasıyla bunun cevabını buldum:

Melvyn Harbor, MVC'nin tarihlerle neden bu şekilde çalıştığı ve gerekirse bunu nasıl geçersiz kılabileceğiniz konusunda kapsamlı bir açıklamaya sahiptir:

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

Ayrıştırılacak değeri ararken, çerçeve belirli bir sıraya bakar:

  1. RouteData (yukarıda gösterilmemiştir)
  2. URI sorgu dizesi
  3. İstek formu

Ancak bunlardan sadece sonuncusu kültürün farkında olacak. Yerelleştirme açısından bunun çok iyi bir nedeni var. Online olarak yayınladığım havayolu uçuş bilgilerini gösteren bir web uygulaması yazdığımı hayal edin. O gün için bir bağlantıya tıklayarak belirli bir tarihteki uçuşları ararım (belki http://www.melsflighttimes.com/Flights/2008-11-21 gibi bir şey ) ve ardından bu bağlantıyı iş arkadaşıma e-posta ile göndermek istiyorum Birleşik Devletler. İkimizin de aynı veri sayfasına bakacağımızı garanti etmenin tek yolu, InvariantCulture kullanılmasıdır. Aksine, uçuşumu rezerve etmek için bir form kullanıyorsam, her şey dar bir döngüde gerçekleşiyor. Veriler forma yazıldığında CurrentCulture'a saygı gösterebilir ve bu nedenle formdan geri dönerken bunlara saygı göstermesi gerekir.


Yapacağım. Bu işlev, soruyu gönderdikten sonra 48 saat süreyle devre dışı bırakılır.
Sam Wessel

43
Teknik olarak bunun doğru olduğuna kesinlikle katılmıyorum. Model bağlayıcı DAİMA POST ve GET ile aynı şekilde davranmalıdır. Değişmez kültür, GET için gitmenin yolu ise, POST için de yapın. Davranışı http fiiline bağlı olarak değiştirmek anlamsızdır.
Bart Calixto

Bir sorum var, web sitemiz başka bir ülkede barındırılıyor, MM/dd/yyyyformat gerekiyor, aksi takdirde doğrulama hatası gösteriyor The field BeginDate must be a date., dd/MM/yyyyformatı kabul etmek için nasıl sunucu yapabilirim ?
shaijut

URL parametresi, ISO standart biçimlendirmesi gibi açık olmalıdır. O zaman kültür ayarları önemli olmaz.
nforss

bu, nedeni açıklayan bir bağlantı verir, ancak aslında bu soruna en kolay çözümü sağlamaz (komut dosyasından bir ISO tarihsaat dizesi yayınlamak gibi), bu şekilde her zaman işe yarar ve sunucu ya da datetime biçiminin sunucu ve istemci arasında aynı olduğundan emin olun.
Umutsuz

36

Kültürlerinizi küresel olarak belirlerim. ModelBinder al şunu!

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

Ya da sadece bu sayfa için bunu değiştirirsiniz.
Ancak global olarak web.config'de bence daha iyi


27
Benim için yapmadı. 23/10 / 2010'u geçersem tarih hala boş olarak geliyor.
GONeale

Bence en kolay çözüm. Benim için tüm Date.ToString () biçimini değiştirir. Sanırım ciltleme ile de çalışacak, ancak kontrol etmedim, üzgünüm :-(
msa.im

1
Geliştirici sunucularımın tarih formatını gg / AA / yyyy olarak ayarladım. Model bağlayıcı, AA / gg / yyyy biçimini kullandı. Web.config'deki biçimin gg / AA / yyyy olarak ayarlanması artık model bağlayıcısını avrupa biçimini kullanmaya zorlar. Bence sunucunun tarih ayarlarını kullanmalı. Neyse sorunumu çözdün.
Karel

Bu benim için mükemmel çalıştı ... uygulama sunucumun Birleşik Krallık'ta bir Birleşik Krallık işletim sistemi çalıştırdığını bildiğim için bir şekilde garip hissettirdi ...: /
Tallmaris

Azure Web Sitelerinde çalışan MVC4'te mükemmel bir şekilde çalıştı
Matty Bear

31

DateTime model özelliklerine kısa tarih biçimi bağlama ile aynı sorunu yaşıyorum. Pek çok farklı örneğe baktıktan sonra (yalnızca DateTime ile ilgili değil) aşağıdaki kanadı bir araya getirdim:

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

Rotaların vb. Global ASAX dosyasında kaydedilme şeklini korumak için, ayrıca CustomModelBinderConfig adlı MVC4 projemin App_Start klasörüne yeni bir sistematik sınıf ekledim:

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

Daha sonra Global ASASX Application_Start'ımdan statik RegisterCustomModelBinders'ı şöyle çağırıyorum:

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

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

Buradaki önemli bir not şudur: Gizli bir alana böyle bir DateTime değeri yazarsanız:

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

Bunu yaptım ve sayfadaki gerçek değer istediğim gibi "gg / AA / yyyy ss: dd: ss tt" yerine "AA / gg / yyyy ss: dd: ss tt" biçimindeydi. Bu, model doğrulamamın ya başarısız olmasına ya da yanlış tarihi döndürmesine neden oldu (açıkçası gün ve ay değerlerini değiştiriyor).

Birçok kafa karıştırmadan ve başarısız denemelerden sonra çözüm, Global.ASAX'ta bunu yaparak her istek için kültür bilgisini ayarlamaktı:

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

Bunu Application_Start'a yapıştırırsanız veya hatta Session_Start'a yapıştırırsanız çalışmaz, çünkü bu onu oturumun mevcut iş parçacığına atar. Sizin de bildiğiniz gibi, web uygulamaları devletsizdir, bu nedenle talebinize daha önce hizmet veren iş parçacığı, mevcut isteğinize hizmet eden aynı iş parçacığıdır, bu nedenle kültür bilgileriniz dijital gökyüzündeki harika GC'ye gitmiştir.

Teşekkürler şuraya gidin: 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

Dmitry - https://stackoverflow.com/a/11903896/578208


13

MVC 3'te biraz farklı olacak.

Bir denetleyicimiz ve Get yöntemiyle bir görünümümüz olduğunu varsayalım

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

ModelBinder eklemeliyiz

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
}

ve Global.asax'ın Application_Start () içindeki komut

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

Bu iyi bir başlangıç ​​noktasıdır, ancak IModelBinderözellikle doğrulama konusunda doğru bir şekilde uygulanmaz . Adını Ayrıca, sadece çalışır DateTimeolduğunu dateTime .
Sam

2
Ayrıca, bunun DateTime?yalnızca ModelBinders.Binders.Addile başka bir çağrı eklerseniz işe yaradığını buldum typeof(DateTime?).
Sam

8

Ayrıca, kendi model bağlayıcınızı oluşturmadan bile birden çok farklı biçimin ayrıştırılabileceğini belirtmek gerekir.

Örneğin ABD'de aşağıdaki dizelerin tümü eşdeğerdir ve otomatik olarak aynı DateTime değerine bağlanır :

/ Şirket / basın / mayıs% 2001% 202.008

/ Şirket / basın / 2008-05-01

/ Şirket / basın / 05-01-2008

Yyyy-aa-gg kullanmanızı şiddetle öneririm çünkü çok daha taşınabilir. Gerçekten birden çok yerelleştirilmiş biçimle uğraşmak istemezsiniz. Birisi 5 Ocak yerine 1 Mayıs'ta bir uçuş rezervasyonu yaparsa, büyük sorunlar yaşarsınız!

Not: yyyy-aa-gg tüm kültürlerde evrensel olarak ayrıştırılırsa, tam olarak net değilim, bu yüzden belki bilen biri yorum ekleyebilir.


3
Kimse yyyy-MM-dd'nin evrensel olmadığını söylemediğinden, sanırım öyle.
deerchao

bu bence model bağlayıcı kullanmaktan daha iyidir. .datepicker ("seçenek", "tarihFormat", "yy-aa-gg") veya sadece varsayılanlara ayarlayın.
Bart Calixto

6

ToISOString () kullanmayı deneyin. ISO8601 biçiminde dize döndürür.

GET yöntemi

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 yöntemi

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

            }
        }
    }
}

Bu çalışmıyor. gg-MM-yyyy hala tanınmıyor
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
Bazı açıklamalar ekleyerek cevabınızı daha zengin hale getirmelisiniz.
Alexandre Lavoie

Bellekten bu, yerleşik model bağlayıcıların çalışma biçimiyle tutarlı değildir, bu nedenle, doğrulama için yazılan değeri korumak gibi aynı ikincil davranışa sahip olmayabilir.
Sam

1

Ben ayarlamak CurrentCultureve CurrentUICulturebenim özel taban kontrolörü

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

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.