MVC DateTime mengikat dengan format tanggal yang salah

132

Asp.net-MVC sekarang memungkinkan untuk mengikat objek DateTime secara implisit. Saya memiliki tindakan di sepanjang baris

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

Ini berhasil mengubah string dari panggilan ajax menjadi DateTime. Namun, kami menggunakan format tanggal dd / MM / yyyy; MVC dikonversi ke MM / hh / tttt. Misalnya, mengirimkan panggilan ke tindakan dengan string '09 / 02/2009 'menghasilkan DateTime dari '02 / 09/2009 00:00:00', atau 2 September di pengaturan lokal kami.

Saya tidak ingin menggulung binder model saya sendiri demi format tanggal. Tapi sepertinya tidak perlu harus mengubah tindakan untuk menerima string dan kemudian gunakan DateTime.Parse jika MVC mampu melakukan ini untuk saya.

Apakah ada cara untuk mengubah format tanggal yang digunakan dalam binder model default untuk DateTime? Bukankah seharusnya pengikat model default menggunakan pengaturan lokalisasi Anda?

Sam Wessel
sumber
Hai .. Ubah saja format tanggal sistem Anda - DD / MM / yyyy ke MM / DD / yyyy dan lakukan itu .. Saya juga memiliki masalah yang sama, saya menyelesaikannya dengan mengubah format tanggal sistem.
banny
@banny jika aplikasi bersifat global dan mungkin setiap orang tidak memiliki format waktu tanggal yang sama, bagaimana Anda bisa melakukan ini? , Anda tidak perlu pergi dan mengubah format tanggal setiap waktu ..
Ravi Mehta
Tidak satu pun dari jawaban ini yang membantu saya. Formulir perlu dilokalkan. Beberapa pengguna mungkin memiliki kencan satu arah, yang lain dengan cara lain. Menyiapkan sesuatu di web.config. atau global.asax tidak akan membantu. Saya akan terus mencari jawaban yang lebih baik, tetapi salah satu caranya adalah dengan memperlakukan tanggal sebagai string sampai saya kembali ke c #.
astrosteve

Jawaban:

164

Saya baru saja menemukan jawaban untuk ini dengan beberapa googling yang lebih lengkap:

Melvyn Harbor memiliki penjelasan menyeluruh tentang mengapa MVC bekerja dengan tanggal seperti yang dilakukannya, dan bagaimana Anda dapat menimpanya jika diperlukan:

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

Saat mencari nilai untuk diuraikan, kerangka kerja terlihat dalam urutan tertentu yaitu:

  1. RouteData (tidak ditampilkan di atas)
  2. String kueri URI
  3. Form Permintaan

Namun hanya yang terakhir yang akan sadar budaya. Ada alasan yang sangat bagus untuk ini, dari perspektif lokalisasi. Bayangkan saya telah menulis aplikasi web yang menampilkan informasi penerbangan maskapai yang saya terbitkan online. Saya mencari penerbangan pada tanggal tertentu dengan mengklik tautan untuk hari itu (mungkin sesuatu seperti http://www.melsflighttimes.com/Flights/2008-11-21 ), dan kemudian ingin mengirim email tautan itu ke kolega saya di Amerika Serikat. Satu-satunya cara kami dapat menjamin bahwa kami berdua akan melihat halaman data yang sama adalah jika InvariantCulture digunakan. Sebaliknya, jika saya menggunakan formulir untuk memesan penerbangan saya, semuanya terjadi dalam siklus yang ketat. Data dapat menghormati CurrentCulture saat ditulis ke formulir, dan karenanya perlu menghormatinya ketika kembali dari formulir.

Sam Wessel
sumber
Akan melakukan. Fungsionalitas itu dinonaktifkan selama 48 jam setelah memposting pertanyaan.
Sam Wessel
43
Saya akan sangat tidak setuju bahwa secara teknis ini benar. Pengikat model harus SELALU berperilaku sama dengan POST dan GET. Jika budaya invarian adalah cara untuk mendapatkan GET, buat juga POST. Mengubah perilaku tergantung pada kata kerja http tidak masuk akal.
Bart Calixto
saya punya pertanyaan, situs web kami di-host di negara lain, perlu MM/dd/yyyyformat lain yang menunjukkan kesalahan validasi The field BeginDate must be a date., bagaimana saya bisa memutuskan untuk menerima dd/MM/yyyyformat?
shaijut
Parameter URL harus tidak ambigu, seperti menggunakan pemformatan standar ISO. Maka pengaturan budaya tidak masalah.
nforss
ini memberikan tautan yang menjelaskan penyebabnya tetapi sebenarnya tidak memberikan solusi termudah untuk masalah ini (seperti dengan memposting string datetime ISO dari skrip), cara itu selalu berhasil dan kami tidak perlu peduli tentang pengaturan budaya tertentu pada server atau memastikan bahwa format datetime identik antara server dan klien.
Putus asa
36

Saya akan mengatur budaya Anda secara global. ModelBinder ambil itu!

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

Atau Anda hanya mengubah ini untuk halaman ini.
Tetapi secara global di web.config saya pikir lebih baik

Peter Gfader
sumber
27
Bukan untuk saya. Tanggal masih dinyatakan sebagai nol jika saya lulus 23/10/2010.
GONeale
Saya pikir ini solusi termudah. Bagi saya itu mengubah format di semua Date.ToString (). Saya pikir itu akan bekerja dengan mengikat juga, tetapi tidak memeriksa, maaf :-(
msa.im
1
Saya memiliki server dev dateformat saya diatur ke dd / MM / yyyy Modelbinder menggunakan format MM / dd / yyyy. Mengatur format di web.config ke dd / MM / yyyy sekarang memaksa pengikat model untuk menggunakan format eropa. Menurut saya itu harus menggunakan pengaturan tanggal server sekalipun. Pokoknya Anda memecahkan masalah saya.
Karel
Itu bekerja dengan sempurna bagi saya ... entah bagaimana rasanya aneh meskipun karena saya tahu bahwa server aplikasi saya di Inggris menjalankan OS Inggris ...: /
Tallmaris
Bekerja dengan sempurna di MVC4 yang berjalan di Situs Web Azure
Matty Bear
31

Saya telah mengalami masalah yang sama dengan format tanggal pendek yang mengikat ke properti model DateTime. Setelah melihat banyak contoh yang berbeda (tidak hanya mengenai DateTime) saya mengumpulkan follwing:

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

Agar tetap seperti rute dll adalah regiseterd dalam file ASAX Global saya juga menambahkan kelas sytatic baru ke folder App_Start proyek MVC4 saya bernama CustomModelBinderConfig:

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

Saya kemudian panggil RegisterCustomModelBinders statis dari Global ASASX Application_Start saya seperti ini:

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

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

Catatan penting di sini adalah jika Anda menulis nilai DateTime ke bidang tersembunyi seperti ini:

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

Saya melakukan itu dan nilai aktual pada halaman itu dalam format "MM / hh / tttt hh: mm: ss tt" bukan "dd / MM / tttt hh: mm: ss tt" seperti yang saya inginkan. Ini menyebabkan validasi model saya gagal atau mengembalikan tanggal yang salah (jelas menukar nilai hari dan bulan sekitar).

Setelah banyak yang menggaruk-garuk kepala dan upaya yang gagal solusinya adalah mengatur info budaya untuk setiap permintaan dengan melakukan ini di 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;
}

Ini tidak akan berfungsi jika Anda menempelkannya di Application_Start atau bahkan Session_Start karena itu menetapkannya pada utas saat ini untuk sesi. Seperti yang Anda ketahui, aplikasi web tidak memiliki kewarganegaraan sehingga utas yang melayani permintaan Anda sebelumnya adalah utas yang sama untuk melayani permintaan Anda saat ini, maka info kultur Anda telah mencapai GC hebat di langit digital.

Terima kasih buka: 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

WernerVA
sumber
13

Ini akan sedikit berbeda di MVC 3.

Misalkan kita memiliki pengontrol dan tampilan dengan metode Get

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

Kita harus menambahkan 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
}

dan perintah di Application_Start () dari Global.asax

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
Dmitry
sumber
Ini adalah titik awal yang layak, tetapi tidak diimplementasikan dengan benar IModelBinder, terutama mengenai validasi. Juga, ini hanya berfungsi jika nama dateTimeDateTime adalah .
Sam
2
Juga, saya telah menemukan bahwa DateTime?s hanya bekerja jika Anda menambahkan panggilan lain untuk ModelBinders.Binders.Adddengan typeof(DateTime?).
Sam
8

Perlu dicatat bahwa bahkan tanpa membuat model Anda sendiri, banyak format yang berbeda dapat diuraikan.

Misalnya di AS semua string berikut ini setara dan secara otomatis terikat dengan nilai DateTime yang sama :

/ perusahaan / pers / dapat% 2001% 202008

/ perusahaan / pers / 2008-05-01

/ perusahaan / pers / 05-01-2008

Saya sangat menyarankan menggunakan yyyy-mm-dd karena jauh lebih portabel. Anda benar-benar tidak ingin berurusan dengan menangani beberapa format terlokalisasi. Jika seseorang memesan penerbangan pada 1 Mei alih-alih 5 Januari, Anda akan memiliki masalah besar!

NB: Saya tidak jelas apakah yyyy-mm-dd secara universal diuraikan dalam semua budaya jadi mungkin seseorang yang tahu bisa menambahkan komentar.

Simon_Weaver
sumber
3
Karena tidak ada yang mengatakan yyyy-MM-dd itu tidak universal, saya kira begitu.
deerchao
ini menurut saya lebih baik daripada menggunakan pengikat model. .datepicker ("option", "dateFormat", "yy-mm-dd") atau cukup atur secara default.
Bart Calixto
6

Coba gunakan toISOString (). Ini mengembalikan string dalam format ISO8601.

DAPATKAN metode

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

Metode POST

javascript

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

c #

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

Saya mengatur konfigurasi di bawah ini pada MVC4 saya dan itu berfungsi seperti pesona

<globalization uiCulture="auto" culture="auto" />
JeeShen Lee
sumber
3
Ini juga bekerja untuk saya. Perhatikan bahwa elemen globalisasi berada di bawah Configuration> System.Web. msdn.microsoft.com/en-us/library/hy4kkhe0%28v=vs.85%29.aspx
Jowen
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);
                    }
                }

            }
        }
    }
}
tobias
sumber
Ini tidak berhasil. dd-MM-yyyy masih belum dikenali
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;
}
Teth
sumber
1
Anda harus membuat jawaban Anda lebih kaya dengan menambahkan beberapa penjelasan.
Alexandre Lavoie
Dari memori, ini tidak konsisten dengan cara pengikat model bawaan bekerja, jadi mungkin tidak memiliki perilaku sekunder yang sama seperti mempertahankan nilai yang diketik untuk validasi.
Sam
1

Saya mengatur CurrentCulturedan CurrentUICulturepengontrol basis kustom saya

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

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
    }
Korayem
sumber