Recently, I was listening to the .Net Rocks! Podcast and Miguel Castro was on talking about MVVM for the web. I only mildly care about the topic, but I like to listen to things even outside of my normal sphere of interest to broaden my range of knowledge. Another side benefit is that sometimes you get unexpected gold nuggets thrown your way. This was one of those times.
Miguel was talking about some JavaScript libraries that were popular with his circle of friends/peers and he mentioned Moment.js. I had heard the name come up in the list of all of the {{noun}}.js libraries that were out there, but never really paid it any mind. All the while, I was suffering JavaScript date pain and I didn’t have to! 😉
To get started using it, you can either download it from the link above, or you can use npm, NuGet, or bower to install it.
bower install moment --save # bower
npm install moment --save # npm
Install-Package Moment.js # NuGet
From there, you can just reference it into your web page. Alternatively, you can just reference it from a CD and never do the install (//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment.js is one).
I’m starting out with this shell of a page. I made a div with the id of playground here I can write out output from Moment.js without using alerts all over the place. I’m going to wrap all of the calls to moment in the writeToPlayground method to get the output.
<html>
<head>
<title>Testing Moment.js</title>
</head>
<body>
<div id="playground"></div>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment.js"></script>
<script type="text/javascript">
function writeToPlayground(val) {
document.getElementById('playground').innerHTML += val + '<br />';
}
</script>
</body>
</html>
I have this shell – including all of the code in this post – as a jsfiddle that you can access at http://jsfiddle.net/PeteOnSoftware/pe4n383h/ to help make it all as clear as possible.
To get started with moment, you just have to create a moment object and then call the format or calendar methods.
writeToPlayground("moment().format() returns " + moment().format());
writeToPlayground("moment().calendar() returns " + moment().calendar());
This gives us:
moment().format() returns 2014-11-15T08:32:47-05:00
moment().calendar() returns Today at 8:32 AM
The format method takes a format string and gives us a lot of power
writeToPlayground("moment().format('dddd') returns " + moment().format('dddd'));
writeToPlayground("moment().format('MMMM') returns " + moment().format('MMMM'));
writeToPlayground("moment().format('MM/DD/YYYY, hh:mm:ss') returns " + moment().format('MM/DD/YYYY, hh:mm:ss'));
This outputs:
moment().format(‘dddd’) returns Saturday
moment().format(‘MMMM’) returns November
moment().format(‘MM/DD/YYYY, hh:mm:ss’) returns 11/15/2014, 08:32:47
But, this isn’t just about date formatting. It also does a lot of date manipulation for you. If you need to subtract time from a date, you can write
writeToPlayground("moment().subtract(10, 'minutes').format('MM/DD/YYYY, hh:mm:ss') returns " + moment().subtract(10, 'minutes').format('MM/DD/YYYY, hh:mm:ss'));
And that outputs: moment().subtract(10, ‘minutes’).format(‘MM/DD/YYYY, hh:mm:ss’) returns 11/15/2014, 08:22:47
But, better than ALL of those things is this next little beauty. If you use .Net as a REST API, whether through WCF or through WebApi, you are probably going to be used to seeing dates returned like this: /Date(1198908717056-0700)/. This is some tainted version of a JS date that doesn’t quite conform to standards that other people are using. If you try to deserialize it in JS, it fails, with or without the -0700 part. The issue is that JSON doesn’t specify a date format, so Microsoft returns *kind of* what you’d need to parse the date. The 1198908717056 part is the number of milliseconds since the Epoch and the -0700 part is supposed to be the timezone offset. Here are some examples of what would happen if you tried to just naively parse what you got back, or if you tried to parse it after doing some manipulation.
writeToPlayground("Trying to parse that as a new Date('/Date(1198908717056-0700)/') gives back " + new Date('/Date(1198908717056-0700)/'));
writeToPlayground("Trying to parse that as a new Date(1198908717056-0700) gives back " + new Date(1198908717056-0700));
writeToPlayground("Trying to parse that as a Date without the -700 as Date(1198908717056) gives back " + new Date(1198908717056));
Gives us:
Trying to parse that as a new Date(‘/Date(1198908717056-0700)/’) gives back Invalid Date
Trying to parse that as a new Date(1198908717056-0700) gives back Sat Dec 29 2007 01:11:56 GMT-0500 (EST)
Trying to parse that as a Date without the -700 as Date(1198908717056) gives back Sat Dec 29 2007 01:11:57 GMT-0500
I’ve run into these same problems trying to parse those kinds of values in Android and iOS applications, too. Here, in the second example, JavaScript just does the subtraction first, which is why the second result is about a second behind the third result (700 milliseconds).
However, Moment.js just takes that .Net value returned and handles it like a champ.
writeToPlayground("moment(/Date(1198908717056-0700)/).format('LLLL') returns " + moment(/Date(1198908717056-0700)/).format('LLLL'));
Brings back: moment(/Date(1198908717056-0700)/).format(‘LLLL’) returns Saturday, December 29, 2007 1:11 AM
That just makes my day! If you want to know more about Moment.js, go check out its page and documentation here. If you just want to play around, go check out my JSFiddle and mess with my examples or just enter sample dates into the textbox and click the PARSE button to see what Moment.js will do with your input.