MVC DateTime ràng buộc với định dạng ngày không chính xác


132

Asp.net-MVC hiện cho phép liên kết ngầm các đối tượng DateTime. Tôi có một hành động dọc theo dòng

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

Điều này chuyển đổi thành công một chuỗi từ một cuộc gọi ajax thành DateTime. Tuy nhiên, chúng tôi sử dụng định dạng ngày dd / MM / yyyy; MVC đang chuyển đổi sang MM / dd / yyyy. Ví dụ: gửi một cuộc gọi đến hành động với chuỗi '09 / 02/2009 'sẽ dẫn đến DateTime là '02 / 09/2009 00:00:00', hoặc ngày 2 tháng 9 trong cài đặt cục bộ của chúng tôi.

Tôi không muốn cuộn chất kết dính mô hình của riêng mình vì định dạng ngày. Nhưng có vẻ như không cần phải thay đổi hành động để chấp nhận một chuỗi và sau đó sử dụng DateTime.Pude nếu MVC có khả năng làm điều này cho tôi.

Có cách nào để thay đổi định dạng ngày được sử dụng trong liên kết mô hình mặc định cho DateTime không? Không phải trình kết dính mô hình mặc định không nên sử dụng cài đặt nội địa hóa của bạn chứ?


Xin chào .. Chỉ cần thay đổi định dạng ngày hệ thống của bạn - DD / MM / yyyy thành MM / DD / yyyy và thực hiện nó .. Tôi cũng gặp vấn đề tương tự, tôi đã giải quyết nó bằng cách thay đổi định dạng ngày hệ thống.
banny

@banny nếu ứng dụng là toàn cầu và có thể mọi người không có định dạng ngày giờ giống nhau, làm thế nào bạn có thể làm điều này? , bạn không nên đi và thay đổi định dạng thời gian mỗi ngày ..
Ravi Mehta

Không có câu trả lời nào trong số này giúp tôi. Các hình thức cần phải được địa phương hóa. Một số người dùng có thể có ngày một cách, những người khác theo cách khác. Thiết lập một cái gì đó trong web.config. hoặc global.asax sẽ không giúp đỡ. Tôi sẽ tiếp tục tìm kiếm một câu trả lời tốt hơn, nhưng một cách sẽ chỉ là xử lý ngày dưới dạng chuỗi cho đến khi tôi quay lại c #.
astrosteve

Câu trả lời:


164

Tôi vừa tìm thấy câu trả lời cho điều này với một số thông tin đầy đủ hơn:

Melvyn Harbor có một lời giải thích kỹ lưỡng về lý do tại sao MVC hoạt động với ngày theo cách của nó và cách bạn có thể ghi đè lên điều này nếu cần thiết:

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

Khi tìm kiếm giá trị để phân tích cú pháp, khung tìm kiếm theo một thứ tự cụ thể là:

  1. RouteData (không hiển thị ở trên)
  2. Chuỗi truy vấn URI
  3. Mẫu yêu cầu

Tuy nhiên, chỉ cuối cùng trong số này sẽ được nhận thức văn hóa. Có một lý do rất tốt cho việc này, từ góc độ nội địa hóa. Hãy tưởng tượng rằng tôi đã viết một ứng dụng web hiển thị thông tin chuyến bay của hãng hàng không mà tôi xuất bản trực tuyến. Tôi tìm kiếm các chuyến bay vào một ngày nhất định bằng cách nhấp vào một liên kết cho ngày hôm đó (có lẽ là một cái gì đó như http://www.melsflighttimes.com/Flight/2008-11-21 ), và sau đó muốn gửi email liên kết đó đến đồng nghiệp của tôi trong Mỹ. Cách duy nhất mà chúng tôi có thể đảm bảo rằng cả hai chúng tôi sẽ xem xét cùng một trang dữ liệu là nếu InvariantCARM được sử dụng. Ngược lại, nếu tôi đang sử dụng một hình thức để đặt chuyến bay của mình, mọi thứ sẽ diễn ra theo một chu kỳ chặt chẽ. Dữ liệu có thể tôn trọng Hiện tại khi nó được ghi vào biểu mẫu và vì vậy cần phải tôn trọng dữ liệu đó khi quay lại từ biểu mẫu.


Sẽ làm. Chức năng đó bị vô hiệu hóa trong 48 giờ sau khi đăng câu hỏi.
Tàu Sam

43
Tôi sẽ không đồng ý rằng về mặt kỹ thuật điều này là chính xác. Chất kết dính mô hình LUÔN LUÔN hành xử tương tự với POST và GET. Nếu văn hóa bất biến là cách để NHẬN thì hãy tạo nó cho POST. Thay đổi hành vi tùy thuộc vào động từ http là vô nghĩa.
Bart Calixto

Tôi có một câu hỏi, trang web của chúng tôi được lưu trữ ở một quốc gia khác, nó cần MM/dd/yyyyđịnh dạng khác, nó hiển thị lỗi xác thực The field BeginDate must be a date., làm thế nào tôi có thể thực hiện sever để chấp nhận dd/MM/yyyyđịnh dạng?
shaijut

Tham số URL phải rõ ràng, như sử dụng định dạng tiêu chuẩn ISO. Sau đó, cài đặt văn hóa sẽ không quan trọng.
nforss

điều này đưa ra liên kết giải thích nguyên nhân nhưng thực tế không cung cấp bất kỳ giải pháp đơn giản nào cho vấn đề này (chẳng hạn như bằng cách đăng chuỗi thời gian ISO từ tập lệnh), theo cách đó luôn hoạt động và chúng tôi không phải quan tâm đến việc thiết lập văn hóa cụ thể trên máy chủ hoặc đảm bảo rằng định dạng datetime giống hệt nhau giữa máy chủ và máy khách.
Vô vọng

36

Tôi sẽ thiết lập toàn cầu nền văn hóa của bạn. ModelBinder chọn điều đó!

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

Hoặc bạn chỉ cần thay đổi điều này cho trang này.
Nhưng trên toàn cầu trong web.config tôi nghĩ là tốt hơn


27
Không cho tôi. Ngày vẫn trôi qua là null nếu tôi vượt qua 23/10/2010.
GONeale

Tôi nghĩ đó là giải pháp dễ nhất. Đối với tôi, nó thay đổi định dạng trong tất cả Date.ToString (). Tôi nghĩ rằng nó cũng sẽ hoạt động với ràng buộc, nhưng không kiểm tra, xin lỗi :-(
msa.im

1
Tôi có định dạng máy chủ dev của tôi được đặt thành dd / MM / yyyy Modelbinder sử dụng định dạng MM / dd / yyyy. Đặt định dạng trong web.config thành dd / MM / yyyy bây giờ buộc modelbinder sử dụng định dạng châu Âu. Theo tôi thì nên sử dụng cài đặt ngày của máy chủ. Dù sao bạn đã giải quyết vấn đề của tôi.
Karel

Điều đó làm việc hoàn hảo đối với tôi ... bằng cách nào đó tôi cảm thấy kỳ lạ mặc dù tôi biết rằng máy chủ ứng dụng của tôi ở Anh đang chạy hệ điều hành Vương quốc Anh ...: /
Tallmaris

Hoạt động hoàn hảo trong MVC4 chạy trên trang web Azure
Matty Bear

31

Tôi đã gặp vấn đề tương tự với định dạng ngày ngắn ràng buộc với các thuộc tính mô hình DateTime. Sau khi xem xét nhiều ví dụ khác nhau (không chỉ liên quan đến DateTime), tôi kết hợp các 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;
            }
        }
    }
}

Để theo cách các tuyến đường v.v. được ghi lại trong tệp ASAX toàn cầu, tôi cũng đã thêm một lớp sytatic mới vào thư mục App_Start của dự án MVC4 của tôi có tên 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());
        }
    }
}

Sau đó, tôi chỉ cần gọi RegisterCustomModelBinder tĩnh từ Ứng dụng ASASX toàn cầu của mình_Start như thế này:

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

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

Một lưu ý quan trọng ở đây là nếu bạn viết giá trị DateTime vào một trường ẩn như thế này:

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

Tôi đã làm điều đó và giá trị thực tế trên trang là ở định dạng "MM / dd / yyyy hh: mm: ss tt" thay vì "dd / MM / yyyy hh: mm: ss tt" như tôi muốn. Điều này khiến xác thực mô hình của tôi không thành công hoặc trả về ngày sai (rõ ràng là hoán đổi giá trị ngày và tháng xung quanh).

Sau nhiều nỗ lực gãi đầu và thất bại, giải pháp là đặt thông tin văn hóa cho mọi yêu cầu bằng cách thực hiện điều này trong 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;
}

Nó sẽ không hoạt động nếu bạn dán nó vào Application_Start hoặc thậm chí Session_Start vì điều đó gán nó cho luồng hiện tại cho phiên. Như bạn đã biết, các ứng dụng web là không trạng thái, do đó, luồng phục vụ yêu cầu của bạn trước đó là cùng một luồng phục vụ yêu cầu hiện tại của bạn do đó thông tin văn hóa của bạn đã chuyển sang GC tuyệt vời trên bầu trời kỹ thuật số.

Cảm ơn, hãy truy cập: Ivan Zlatev - http://ivanz.com/2010/11/03/custom-model-binding-USE-imodelbinder-in-asp-net-mvc-two-gotchas/

garik - https://stackoverflow.com/a/2468447/578208

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


13

Nó sẽ hơi khác nhau trong MVC 3.

Giả sử chúng ta có một bộ điều khiển và một khung nhìn với phương thức Get

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

Chúng ta nên thêm 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
}

và lệnh trong Application_Start () của Global.asax

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

Đây là một điểm khởi đầu tốt, nhưng nó không thực hiện chính xác IModelBinder, đặc biệt là về xác nhận. Ngoài ra, nó chỉ hoạt động nếu tên của DateTimedatetime .
Sam

2
Ngoài ra, tôi đã thấy rằng DateTime?nó chỉ hoạt động nếu bạn thêm một cuộc gọi khác ModelBinders.Binders.Addvới typeof(DateTime?).
Sam

8

Cũng cần lưu ý rằng ngay cả khi không tạo mô hình của riêng bạn, nhiều định dạng khác nhau có thể được phân tích cú pháp.

Chẳng hạn ở Mỹ, tất cả các chuỗi sau đều tương đương và tự động bị ràng buộc với cùng một giá trị DateTime:

/ công ty / báo chí / có thể% 2001% 202008

/ công ty / báo chí / 2008-05-01

/ công ty / báo chí / 05-01-2008

Tôi thực sự khuyên bạn nên sử dụng yyyy-mm-dd vì nó dễ mang theo hơn nhiều. Bạn thực sự không muốn đối phó với việc xử lý nhiều định dạng cục bộ. Nếu ai đó đặt chuyến bay vào ngày 1 tháng 5 thay vì ngày 5 tháng 1, bạn sẽ gặp vấn đề lớn!

Lưu ý: Tôi không rõ exaclty nếu yyyy-mm-dd được phân tích cú pháp phổ biến trong tất cả các nền văn hóa để có thể ai đó biết có thể thêm nhận xét.


3
Vì không ai nói yyyy-MM-dd không phổ biến, tôi đoán là vậy.
Deerchao

theo ý kiến ​​của tôi thì tốt hơn là sử dụng chất kết dính mô hình. .datepicker ("tùy chọn", "dateFormat", "yy-mm-dd") hoặc chỉ đặt nó trong mặc định.
Bart Calixto

6

Hãy thử sử dụng toISOString (). Nó trả về chuỗi ở định dạng ISO8601.

Phương thức GET

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

Phương thức POST

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

            }
        }
    }
}

Điều này không hoạt động. dd-MM-yyyy vẫn chưa được công nhận
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
Bạn nên làm cho câu trả lời của bạn phong phú hơn bằng cách thêm một số giải thích.
Alexandre Lavoie

Từ bộ nhớ, điều này không phù hợp với cách thức hoạt động của các chất kết dính mô hình tích hợp, do đó nó có thể không có hành vi thứ cấp giống như giữ lại giá trị đã nhập để xác thực.
Sam

1

Tôi đặt CurrentCultureCurrentUICulturebộ điều khiển cơ sở tùy chỉnh của tôi

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

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.