Piotr
Piotr

Reputation: 592

ASP.NET MVC: Html.Action / Html.ActionLink and culture specific DateTime

I've been fighting with culture specific DateTime in ASP.NET MVC for few days now, to no avail. The scenario is as follows: I have a list which I want to filter against DateTime submitted by user. Users should handle dates in a localized form, in my case culture and cultureUI is set to "da-DK", so date format is "dd-mm-yy". So far I've managed to make the form submit date in the required format, but Action and ActionLink methods generate URLs that interpret dates in en-US ("mm/dd/yy").

Here's a sample project displaying the issue. The culture in Web.config is hardcoded to "da-DK":

<globalization culture="da-DK" uiCulture="da-DK" />

First the controller with two actions:

using System;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace DateTimeTest.Controllers
{
    public class HomeController : Controller
    {
        public ViewResult Index(DateModel model)
        {
            return View(model);
        }

        public ViewResult Dummy(DateTime? date)
        {
            return View(date);
        }
    }

    public class DateModel
    {
        [DataType(DataType.Date)]
        public DateTime? Date { get; set; }
    }
}

Index is an action displaying a form, where user can enter date in "dd-mm-yy" format. Form is posted to the same GET method (as I mentioned it's suppose to be filtering functionality), which will display the received date. Here's the Index.cshtml view:

@model DateTimeTest.Controllers.DateModel

<div>
    @if (Model.Date != null)
    {
        <p>Received @Model.Date</p>
        @Html.ActionLink("Dummy", "Dummy", new {Model.Date})
    }

    @using (Html.BeginForm("Index", "Home", FormMethod.Get))
    {
        @Html.EditorForModel()
    }
</div>

To make this work, I had to use a custom model binder, processing the DateTime. A simplified version looks as follows:

using System;
using System.Web.Mvc;

namespace DateTimeTest.Infrastructure
{
    public class DateTimeBinder: IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            string date = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;

            DateTime? result = DateTime.Parse(date);

            return result;
        }
    }
}

This binder has been registered in Global.asax.cs with

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

Now, when in the form I enter date "30-12-2013" and submit, Index view displays message "Received 30-12-2013 00:00:00" which is correct in my meaning. However the link to Dummy action looks like this:

http://localhost:53143/Home/Dummy?Date=12%2F30%2F2013%2000%3A00%3A00

which means the query string for Date is actually "12/30/2013 00:00:00". Clicking the link gives a FormatException in BindModel of the DateTimeBinder, because there are no 30 months in the calendar (it tries to process the date in da-DK format while what it gets is date in en-US). I know how to deal with exception itself but what I don't know is how to either force the system to use uniform DateTime format, or detect in the binder which format it comes in and act accordingly. I guess it boils down to question how URLs are generated by Html.Action and Html.ActionLink methods, though I might be wrong.

Note that I understand the need to treat GET URLs in en-US format (the post about fly tickets circulating around the web), though I would be willing to ignore this as the website I'm working on is an Intranet site where dates will only be used in da-DK format. Can anyone help me fix the issue? Thanks in advance.

PS. I'm using ASP.NET MVC 5.

Upvotes: 2

Views: 1629

Answers (2)

Chris
Chris

Reputation: 27609

The problem seems to be that having dug deep into the code for ActionLink (and following lots of method calls through lots of classes) it has this line:

builder2.Append(UrlEncode(Convert.ToString(obj3, CultureInfo.InvariantCulture)));

Now the code is very complicated so I can't be 100% sure that this is the line that is causing you problems but I'd be relatively confident that if not there is another similar line elsewhere.

So the reason for your problem is that your ActionLink is generating in a Invariant culture context rather than the one you want. This is not going to be fixable.

You could probably fix it by just using a ToString and specifying your culture (or presumably allowing it to use the default).

N.B. The line of code quoted above was found via Reflector in System.Web.Routing.ParsedRoute.Bind(...). ParsedRoute is an internal class. Its in the System.Web.dll assembly.

Upvotes: 2

Kaido
Kaido

Reputation: 3931

I would hard-code the culture and uiculture to da-DK

<globalization uiCulture="da-DK" culture="da-DK" />

Upvotes: 0

Related Questions