I have a controller action that effectively simply returns a JsonResult of my model. So, in my method I have something like the following:
return new JsonResult(myModel);
This works well, except for one problem. There is a date property in the model and this appears to be returned in the Json result like so:
"\/Date(1239018869048)\/"
How should I be dealing with dates so they are returned in the format I require? Or how do I handle this format above in script?
Source: Tips4all, CCNA FINAL EXAM
Just to expand on CasperOne's answer.
ReplyDeleteThe JSON spec does not account for Date values. MS had to make a call, and the path they chose was to exploit a little trick in the javascript representation of strings: the string literal "/" is the same as "\/", and a string literal will never get serialized to "\/" (even "\/" must be mapped to "\\/").
See http://msdn.microsoft.com/en-us/library/bb299886.aspx#intro_to_json_topic2 for a better explanation (scroll down to "From JavaScript Literals to JSON")
One of the sore points of JSON is the
lack of a date/time literal. Many
people are surprised and disappointed
to learn this when they first
encounter JSON. The simple explanation
(consoling or not) for the absence of
a date/time literal is that JavaScript
never had one either: The support for
date and time values in JavaScript is
entirely provided through the Date
object. Most applications using JSON
as a data format, therefore, generally
tend to use either a string or a
number to express date and time
values. If a string is used, you can
generally expect it to be in the ISO
8601 format. If a number is used,
instead, then the value is usually
taken to mean the number of
milliseconds in Universal Coordinated
Time (UTC) since epoch, where epoch is
defined as midnight January 1, 1970
(UTC). Again, this is a mere
convention and not part of the JSON
standard. If you are exchanging data
with another application, you will
need to check its documentation to see
how it encodes date and time values
within a JSON literal. For example,
Microsoft's ASP.NET AJAX uses neither
of the described conventions. Rather,
it encodes .NET DateTime values as a
JSON string, where the content of the
string is \/Date(ticks)\/ and where
ticks represents milliseconds since
epoch (UTC). So November 29, 1989,
4:55:30 AM, in UTC is encoded as
"\/Date(628318530718)\/".
A solution would be to just parse it out:
value = new Date(parseInt(value.replace("/Date(", "").replace(")/",""), 10));
However I've heard that there is a setting somewhere to get the serializer to output DateTime objects with the new Date(xxx) syntax. I'll try to dig that out.
Here's my solution in Javascript - very much like JPots', but shorter (and possibly a tiny bit faster):
ReplyDeletevalue = new Date(parseInt(value.substr(6)));
"value.substr(6)" takes out the "/Date(" part, and the parseInt function ignores the non-number characters that occur at the end.
Using jQuery to auto-convert dates with $.parseJSON
ReplyDeleteSince you're using Asp.net MVC I suspect you're using jQuery on the client side. I suggest you read this blog post that has code how to use $.parseJSON to automatically convert dates for you.
Code supports Asp.net formatted dates like the ones you mentioned as well as ISO formatted dates. All dates will be automatically formatted for you by using $.parseJSON().
There are quite a bit of answers to handle it client side, but you can change the output server side if you desired.
ReplyDeleteThere are a few ways to approach this, I'll start with the basics. You'll have to subclass the JsonResult class and override the ExecuteResult method. From there you can take a few different approaches to change the serialization.
Approach 1:
The default implementation uses the JsonScriptSerializer. If you take a look at the documentation, you can use the RegisterConverters method to add custom JavaScriptConverters. There are a few problems with this though: The JavaScriptConverter serializes to a dictionary, that is it takes an object and serializes to a Json dictionary. In order to make the object serialize to a string it requires a bit of hackery, see post. This particular hack will also escape the string.
public class CustomJsonResult : JsonResult
{
private const string _dateFormat = "yyyy-MM-dd hh:mm:ss";
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType = "application/json";
}
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
// Use your custom JavaScriptConverter subclass here.
serializer.RegisterConverters(new JavascriptConverter[] { new CustomConverter });
response.Write(serializer.Serialize(Data));
}
}
}
Approach 2 (recommended):
The second approach is to start with the overridden JsonResult and go with another Json serializer, in my case the Json.NET serializer. This doesn't require the hackery of approach 1. Here is my implementation of the JsonResult subclass:
public class CustomJsonResult : JsonResult
{
private const string _dateFormat = "yyyy-MM-dd hh:mm:ss";
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType = "application/json";
}
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
// Using Json.NET serializer
var isoConvert = new IsoDateTimeConverter();
isoConvert.DateTimeFormat = _dateFormat;
response.Write(JsonConvert.SerializeObject(Data, isoConvert));
}
}
}
Additional credits:
James Newton-King
I have been working on a solution to this issue as none of the above answers really helped me. I am working with the jquery week calendar and needed my dates to have time zone information on the server and locally on the page. After quite a bit of digging around, I figured out a solution that may help others.
ReplyDeleteI am using asp.net 3.5, vs 2008, asp.net MVC 2, and jquery week calendar,
First, I am using a library written by Steven Levithan that helps with dealing with dates on the client side, Steven Levithan's date library. The isoUtcDateTime format is perfect for what I needed. In my jquery AJAX call I use the format function provided with the library with the isoUtcDateTime format and when the ajax call hits my action method, the datetime Kind is set to local and reflects the server time.
When I send dates to my page via AJAX, I send them as text strings by formatting the dates using "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'zzzz". This format is easily converted client side using
var myDate = new Date(myReceivedDate);
Here is my complete solution minus Steve Levithan's source, which you can download:
Controller:
public class HomeController : Controller
{
public const string DATE_FORMAT = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'zzzz";
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
public JsonResult GetData()
{
DateTime myDate = DateTime.Now.ToLocalTime();
return new JsonResult { Data = new { myDate = myDate.ToString(DATE_FORMAT) } };
}
public JsonResult ReceiveData(DateTime myDate)
{
return new JsonResult { Data = new { myDate = myDate.ToString(DATE_FORMAT) } };
}
}
Javascript:
<script type="text/javascript">
function getData() {
$.ajax({
url: "/Home/GetData",
type: "POST",
cache: "false",
dataType: "json",
success: function(data) {
alert(data.myDate);
var newDate = cleanDate(data.myDate);
alert(newDate);
sendData(newDate);
}
});
}
function cleanDate(d) {
if (typeof d == 'string') {
return new Date(d) || Date.parse(d) || new Date(parseInt(d));
}
if (typeof d == 'number') {
return new Date(d);
}
return d;
}
function sendData(newDate) {
$.ajax({
url: "/Home/ReceiveData",
type: "POST",
cache: "false",
dataType: "json",
data:
{
myDate: newDate.format("isoUtcDateTime")
},
success: function(data) {
alert(data.myDate);
var newDate = cleanDate(data.myDate);
alert(newDate);
}
});
}
// bind myButton click event to call getData
$(document).ready(function() {
$('input#myButton').bind('click', getData);
});
</script>
I hope this quick example helps out others in the same situation I was in. At this time it seems to work very well with the Microsoft JSON Serialization and keeps my dates correct across timezones.
If you doing eval("(" + jsonResult + ")") on the result, it should work. You may be best to, as mentioned, do a cusom serialization of the date/time to a client specific GMT/UTC string. Dates aren't defined within the JSON specification, but using Date(TIME_T) will work unless you are doing client-side validation of JSON responses.
ReplyDeleteEdit:example
var o = eval("({ dtm: new Date('1990/01/05') })");
alert(o.dtm);
//milliseconds from 1970-01-01 to 1990-01-05 aka TIME_T * 1000
o = eval("({ dtm: new Date(631522800000) })");
alert(o.dtm);
both work...
Not the most elegant way but this worked for me:
ReplyDeletevar ms = date.substring(6, date.length - 2);
var newDate = formatDate(ms);
function formatDate(ms) {
var date = new Date(parseInt(ms));
var hour = date.getHours();
var mins = date.getMinutes() + '';
var time = "AM";
// find time
if (hour >= 12) {
time = "PM";
}
// fix hours format
if (hour > 12) {
hour -= 12;
}
else if (hour == 0) {
hour = 12;
}
// fix minutes format
if (mins.length == 1) {
mins = "0" + mins;
}
// return formatted date time string
return date.getMonth() + 1 + "/" + date.getDate() + "/" + date.getFullYear() + " " + hour + ":" + mins + " " + time;
}
I had the same problem and instead of returning the actual date value I just used ToString("dd MMM yyyy") on it. Then in my javascript I used new Date(datevalue), where datevalue may be "01 Jan 2009".
ReplyDeleteFormat the date within the query.
ReplyDeletevar _myModel = from _m in model.ModelSearch(word)
select new { date = ((DateTime)_m.Date).ToShortDateString() };
The only problem with this solution is that you won't get any results if ANY of the date values are null. To get around this you could either put conditional statements in your query BEFORE you select the date that ignores date nulls or you could set up a query to get all the results and then loop through all of that info using a foreach loop and assign a value to all dates that are null BEFORE you do your SELECT new.
Example of both:
var _test = from _t in adc.ItemSearchTest(word)
where _t.Date != null
select new { date = ((DateTime)_t.Date).ToShortDateString() };
The second option requires another query entirely so you can assign values to all nulls. This and the foreach loop would have to be BEFORE your query that selects the values.
var _testA = from _t in adc.ItemSearchTest(word)
select _i;
foreach (var detail in _testA)
{
if (detail.Date== null)
{
detail.Date= Convert.ToDateTime("1/1/0001");
}
}
Just an idea which I found easier than all of the javascript examples.