15

I am using Razor as a view engine in ASP.NET MVC 5. I have dates coming back from a back-end service that are in UTC. I want to format those using JavaScript (e. g. moment.js) on the client. I don't want to do it on the server, because I don't want to have to worry about what time zone the client is in on the server side (after all, the server doesn't really care).

I think from Locale Date formatting with MVC, AJAX and Moment.js that there's some way to do this server-side if I have to, although there's not enough information for me to get it worked out, and that's not what I want to do anyway.

Something like ASP.NET MVC ViewModel mapping with custom formatting or Where is the best place to format Model properties in ASP.NET MVC(3)? or How to format date in asp.net mvc 5 doesn't work because that's server side (or does it in some way I don't understand?)

What's the "best" / "right" way to do this cleanly and reliably?

EDIT: To be clear, I know all about the moment.js side. I wouldn't have named it if I didn't. What I'm asking about is the ASP.NET MVC view integration - how do I get that done the "right" way?

EDIT 2: I know how to do the script. What I don't know is if I have that output, from a script variable, how do I get the contents of that variable into the information displayed to the user in the view? I don't want to have to do something like edit the HTML element contents or something, if I can help it; that's not clean at all. Perhaps I should make this much simpler to make this very very clear:

Imagine a view in Razor. That view says:

@foreach (var item in Model) {
    <!-- something to do javascript manipulation of item -->
    <p>The result of manipulating @item.startDate is <!-- WHAT DO I DO HERE? --></p>
}

The comments are what I don't know how to do right now cleanly.

Community
  • 1
  • 1
MikeBaz - MSFT
  • 2,938
  • 4
  • 28
  • 57

3 Answers3

21

You can try this (JS with razor) :

var date = moment('@YourDateTime.ToString( "s", System.Globalization.CultureInfo.InvariantCulture )');

From momentjs doc :

When creating a moment from a string, we first check if the string matches known ISO 8601 formats

(Source)

From .NET doc :

The "s" standard format specifier reflects a defined standard (ISO 8601)

(Source)

Edit

Solution to combine momentjs with Razor to display an array of DateTime

HTML/Razor :

@foreach (var item in Model) {
   <p data-utcdate="@item.startDate.ToString("s", System.Globalization.CultureInfo.InvariantCulture)"></p>
}

JS (with JQuery) :

<script type="text/javascript">
    $(function () {
        $('[data-utcdate]').each(function () {
            var d = moment($(this).attr('data-utcdate'));
            $(this).html(d.format());
        });
    });
</script>

If you want to display your date with a different format : http://momentjs.com/docs/#/displaying/format/

Guy
  • 1,434
  • 1
  • 19
  • 33
  • 2
    I'm sorry, perhaps I'm being dense, but this is still just telling me how to get a JavaScript variable. How do I get that variable value into the output of the view, e. g. in a foreach loop? – MikeBaz - MSFT Mar 04 '15 at 15:44
  • What do you mean by "_the output of the view_" ? – Guy Mar 04 '15 at 16:22
  • 1
    I mean, the data presented to the user by the view. See the second edit, perhaps that will be clearer. – MikeBaz - MSFT Mar 04 '15 at 16:43
  • 1
    Ok. I just edited my answer with an exemple. Do you have JQuery ? If not, I can edit my answer with a pure JS example. I whish it would help. – Guy Mar 04 '15 at 19:10
  • 2
    Thank you for the example JS code. Does this mean there's no way to do it without messing with the DOM? that stinks :( – MikeBaz - MSFT Mar 04 '15 at 19:29
  • 1
    Razor is executed on server side.. So, yes you need JS Code and "DOM messing" to display local date with MVC... An other solution, is to capturing the user's default time zone from the browser and doing the calculation of the correct time server-side. But, as you say : _after all, the server doesn't really care_. – Guy Mar 04 '15 at 19:42
  • I guess I was hoping for something that would allow an indication to Razor that it should use script in the template or something. But if this is the best that can be done, so be it. – MikeBaz - MSFT Mar 04 '15 at 21:08
3

If you have views that display some dates or timestamps in Razor and some in Javascript, here is a Javascript function that will translate a .NET format string like {0:MM/dd/yyyy} into a moment.js (actually moment-timezone.js) format string:

// Convert a single .NET date format to use with moment.js.
DotNetToMomentFormat = function (s0) {
    s0 = s0.replace(/\{\d+:(.*)\}/, "$1"); // remove placeholder
    var m = s0.match(/((.)\2*)/g);
    var s1 = m.reduce(function (a,s) {
            switch (s) {
                case "d": s = "MM/DD/YYYY"; break;
                case "dd": s = "DD"; break;
                case "ddd": s = "ddd"; break;
                case "dddd": s = "dddd"; break;
                case "D": s = "DD MMMM YYYY"; break;
                case "f": s = "DD MMMM YYYY HH:mm"; break;
                case "fff": s = "SSS"; break;
                case "F": s = "DD MMMM YYYY HH:mm:ss"; break;
                case "FFF": s = "SSS"; break; // no trailing 0s
                case "g": s = "DD/MM/YYYY HH:mm"; break;
                case "G": s = "DD/MM/YYYY HH:mm:ss"; break;
                case "hh": s = "hh"; break;
                case "HH": s = "HH"; break;
                case "m": s = "MMMM DD"; break;
                case "mm": s = "mm"; break;
                case "M": s = "MMMM DD"; break;
                case "MM": s = "MM"; break;
                case "MMM": s = "MMM"; break;
                case "MMMM": s = "MMMM"; break;
                case "o": s = "YYYY-MM-DD HH:mm:ssZ"; break;
                case "O": s = "YYYY-MM-DD HH:mm:ssZ"; break;
                case "r": s = "ddd, DD MMM YYYY, H:mm:ss z"; break;
                case "R": s = "ddd, DD MMM YYYY, H:mm:ss z"; break;
                case "s": s = "YYYY-MM-DDTHH:mm:ss"; break;
                case "ss": s = "ss"; break;
                case "t": s = "HH:mm"; break;
                case "tt": s = "A"; break;
                case "T": s = "HH:mm:ss"; break;
                case "u": s = "YYYY-MM-DD HH:mm:ssZ"; break;
                case "y": s = "MMMM, YYYY"; break;
                case "yy": s = "YY"; break;
                case "yyyy": s = "YYYY"; break;
                case "Y": s = "MMMM, YYYY"; break;
            }
            return a + s;
        },
        "");
    return s1;
}

Then, in Razor, some example code would be:

@{
    var today = DateTime.Today;
    string dateFormat = "{0:MM/dd/yyyy}";
}
<div>The MVC-formatted date is @(string.Format(dateFormat, today)).</div>
<div>The moment-formatted date is <span id="today"></span>.</div>

<script type="text/javascript">
    var x = window.document.getElementById("today");
    var y = moment("@today.ToString("O")");
    x.innerHTML = y.format(DotNetToMomentFormat("@dateFormat"));
</script>

As of this post, the relevant output would be:

The MVC-formatted date is 02/03/2017.
The moment-formatted date is 02/03/2017.

The other piece of information you would need is the user's local time zone and use moment-timezone's tz(timezonename) to set it before formatting.

Suncat2000
  • 966
  • 1
  • 12
  • 15
1

You need to use the moment timezone functions. http://momentjs.com/timezone/docs/#/using-timezones/converting-to-zone/

Do not forget to reference the timezone data file.

The link How to turn Razor Model into JS object in a Razor For Loop? explains how it is done.

Community
  • 1
  • 1
Praveen Paulose
  • 5,741
  • 1
  • 15
  • 19