I'm taking my first crack at Ajax with jQuery. I'm getting my data onto my page, but I'm having some trouble with the JSON data that is returned for Date data types. Basically, I'm getting a string back that looks like this:
/Date(1224043200000)/
From someone totally new to JSON - How do I format this to a short date format? Should this be handled somewhere in the jQuery code? I've tried the jQuery.UI.datepicker plugin using $.datepicker.formatDate() without any success.
FYI: Here's the solution I came up with using a combination of the answers here:
function getMismatch(id) {
$.getJSON("Main.aspx?Callback=GetMismatch",
{ MismatchId: id },
function (result) {
$("#AuthMerchId").text(result.AuthorizationMerchantId);
$("#SttlMerchId").text(result.SettlementMerchantId);
$("#CreateDate").text(formatJSONDate(Date(result.AppendDts)));
$("#ExpireDate").text(formatJSONDate(Date(result.ExpiresDts)));
$("#LastUpdate").text(formatJSONDate(Date(result.LastUpdateDts)));
$("#LastUpdatedBy").text(result.LastUpdateNt);
$("#ProcessIn").text(result.ProcessIn);
}
);
return false;
}
function formatJSONDate(jsonDate) {
var newDate = dateFormat(jsonDate, "mm/dd/yyyy");
return newDate;
}
This solution got my object from the callback method and displayed the dates on the page properly using the date format library.
eval() is not necessary. This will work fine:
var date = new Date(parseInt(jsonDate.substr(6)));
The substr() function takes out the /Date( part, and the parseInt() function gets the integer and ignores the )/ at the end. The resulting number is passed into the Date constructor.
I have intentionally left out the radix (the 2nd argument to parseInt); see my comment below.
Also, I completely agree with Rory's comment: ISO-8601 dates are preferred over this old format - so this format generally shouldn't be used for new development.
For ISO-8601 formatted JSON dates, just pass the string into the Date constructor:
var date = new Date(jsonDate); //no ugly parsing needed; full timezone support
You can use this to get a date from JSON:
var date = eval(jsonDate.replace(/\/Date\((\d+)\)\//gi, "new Date($1)"));
And then you can use a JavaScript Date Format script (1.2 KB when minified and gzipped) to display it as you want.
For those using Newtonsoft Json.NET, read up on how to do it via Native JSON in IE8, Firefox 3.5 plus Json.NET.
Also the documentation on changing the format of dates written by Json.NET is useful:
Serializing Dates with Json.NET
For those that are too lazy, here are the quick steps. As JSON has a loose DateTime implementation, you need to use the IsoDateTimeConverter(). Note that since Json.NET 4.5 the default date format is ISO so the code below isn't needed.
string jsonText = JsonConvert.SerializeObject(p, new IsoDateTimeConverter());
The JSON will come through as
"fieldName": "2009-04-12T20:44:55"
Finally, some JavaScript to convert the ISO date to a JavaScript date:
function isoDateReviver(value) {
if (typeof value === 'string') {
var a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)(?:([\+-])(\d{2})\:(\d{2}))?Z?$/.exec(value);
if (a) {
var utcMilliseconds = Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]);
return new Date(utcMilliseconds);
}
}
return value;
}
I used it like this
$("<span />").text(isoDateReviver(item.fieldName).toLocaleString()).appendTo("#" + divName);
The original example:
/Date(1224043200000)/
does not reflect the formatting used by WCF when sending dates via WCF REST using the built-in JSON serialization. (at least on .NET 3.5, SP1)
I found the answer here helpful, but a slight edit to the regex is required, as it appears that the timezone GMT offset is being appended onto the number returned (since 1970) in WCF JSON.
In a WCF service I have:
[OperationContract]
[WebInvoke(
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.WrappedRequest
)]
ApptVisitLinkInfo GetCurrentLinkInfo( int appointmentsId );
ApptVisitLinkInfo is defined simply:
public class ApptVisitLinkInfo {
string Field1 { get; set; }
DateTime Field2 { get; set; }
...
}
When "Field2" is returned as Json from the service the value is:
/Date(1224043200000-0600)/
Notice the timezone offset included as part of the value.
The modified regex:
/\/Date\((.*?)\)\//gi
It's slightly more eager and grabs everything between the parens, not just the first number. The resulting time sinze 1970, plus timezone offset can all be fed into the eval to get a date object.
The resulting line of JavaScript for the replace is:
replace(/\/Date\((.*?)\)\//gi, "new Date($1)");
Don't repeat yourself - automate date conversion using $.parseJSON()
Answers to your post provide manual date conversion to JavaScript dates. I've extended jQuery's $.parseJSON() just a little bit, so it's able to automatically parse dates when you instruct it to. It processes ASP.NET formatted dates (/Date(12348721342)/) as well as ISO formatted dates (2010-01-01T12.34.56.789Z) that are supported by native JSON functions in browsers (and libraries like json2.js).
Anyway. If you don't want to repeat your date conversion code over and over again I suggest you read this blog post and get the code that will make your life a little easier.
Click here to check the Demo
JavaScript/jQuery
var = MyDate_String_Value = "/Date(1224043200000)/"
var value = new Date
(
parseInt(MyDate_String_Value.replace(/(^.*\()|([+-].*$)/g, ''))
);
var dat = value.getMonth() +
1 +
"/" +
value.getDate() +
"/" +
value.getFullYear();
Result - "10/15/2008"
If you say in JavaScript,
var thedate = new Date(1224043200000);
alert(thedate);
you will see that it's the correct date, and you can use that anywhere in JavaScript code with any framework.
Updated
We have an internal UI library that has to cope with both Microsoft's ASP.NET built-in JSON format, like /Date(msecs)/, asked about here originally, and most JSON's date format including JSON.NET's, like 2014-06-22T00:00:00.0. In addition we need to cope with oldIE's inability to cope with anything but 3 decimal places.
We first detect what kind of date we're consuming, parse it into a normal JavaScript Date object, then format that out.
1) Detect Microsoft Date format
// Handling of Microsoft AJAX Dates, formatted like '/Date(01238329348239)/'
function looksLikeMSDate(s) {
return /^\/Date\(/.test(s);
}
2) Detect ISO date format
var isoDateRegex = /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(\.\d\d?\d?)?([\+-]\d\d:\d\d|Z)?$/;
function looksLikeIsoDate(s) {
return isoDateRegex.test(s);
}
3) Parse MS date format:
function parseMSDate(s) {
// Jump forward past the /Date(, parseInt handles the rest
return new Date(parseInt(s.substr(6)));
}
4) Parse ISO date format.
We do at least have a way to be sure that we're dealing with standard ISO dates or ISO dates modified to always have three millisecond places (see above), so the code is different depending on the environment.
4a) Parse standard ISO Date format, cope with oldIE's issues:
function parseIsoDate(s) {
var m = isoDateRegex.exec(s);
// Is this UTC, offset, or undefined? Treat undefined as UTC.
if (m.length == 7 || // Just the y-m-dTh:m:s, no ms, no tz offset - assume UTC
(m.length > 7 && (
!m[7] || // Array came back length 9 with undefined for 7 and 8
m[7].charAt(0) != '.' || // ms portion, no tz offset, or no ms portion, Z
!m[8] || // ms portion, no tz offset
m[8] == 'Z'))) { // ms portion and Z
// JavaScript's weirdo date handling expects just the months to be 0-based, as in 0-11, not 1-12 - the rest are as you expect in dates.
var d = new Date(Date.UTC(m[1], m[2]-1, m[3], m[4], m[5], m[6]));
} else {
// local
var d = new Date(m[1], m[2]-1, m[3], m[4], m[5], m[6]);
}
return d;
}
4b) Parse ISO format with a fixed three millisecond decimal places - much easier:
function parseIsoDate(s) {
return new Date(s);
}
5) Format it:
function hasTime(d) {
return !!(d.getUTCHours() || d.getUTCMinutes() || d.getUTCSeconds());
}
function zeroFill(n) {
if ((n + '').length == 1)
return '0' + n;
return n;
}
function formatDate(d) {
if (hasTime(d)) {
var s = (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear();
s += ' ' + d.getHours() + ':' + zeroFill(d.getMinutes()) + ':' + zeroFill(d.getSeconds());
} else {
var s = (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear();
}
return s;
}
6) Tie it all together:
function parseDate(s) {
var d;
if (looksLikeMSDate(s))
d = parseMSDate(s);
else if (looksLikeIsoDate(s))
d = parseIsoDate(s);
else
return null;
return formatDate(d);
}
The below old answer is useful for tying this date formatting into jQuery's own JSON parsing so you get Date objects instead of strings, or if you're still stuck in jQuery <1.5 somehow.
Old Answer
If you're using jQuery 1.4's Ajax function with ASP.NET MVC, you can turn all DateTime properties into Date objects with:
// Once
jQuery.parseJSON = function(d) {return eval('(' + d + ')');};
$.ajax({
...
dataFilter: function(d) {
return d.replace(/"\\\/(Date\(-?\d+\))\\\/"/g, 'new $1');
},
...
});
In jQuery 1.5 you can avoid overriding the parseJSON method globally by using the converters option in the Ajax call.
http://api.jquery.com/jQuery.ajax/
Unfortunately you have to switch to the older eval route in order to get Dates to parse globally in-place - otherwise you need to convert them on a more case-by-case basis post-parse.
There is no built in date type in JSON. This looks like the number of seconds / milliseconds from some epoch. If you know the epoch you can create the date by adding on the right amount of time.
I also had to search for a solution to this problem and eventually I came across moment.js which is a nice library that can parse this date format and many more.
var d = moment(yourdatestring)
It saved some headache for me so I thought I'd share it with you. :)
You can find some more info about it here: http://momentjs.com/
I ended up adding the "characters into Panos's regular expression to get rid of the ones generated by the Microsoft serializer for when writing objects into an inline script:
So if you have a property in your C# code-behind that's something like
protected string JsonObject { get { return jsSerialiser.Serialize(_myObject); }}
And in your aspx you have
<script type="text/javascript">
var myObject = '<%= JsonObject %>';
</script>
You'd get something like
var myObject = '{"StartDate":"\/Date(1255131630400)\/"}';
Notice the double quotes.
To get this into a form that eval will correctly deserialize, I used:
myObject = myObject.replace(/"\/Date\((\d+)\)\/"/g, 'new Date($1)');
I use Prototype and to use it I added
String.prototype.evalJSONWithDates = function() {
var jsonWithDates = this.replace(/"\/Date\((\d+)\)\/"/g, 'new Date($1)');
return jsonWithDates.evalJSON(true);
}
In jQuery 1.5, as long as you have json2.js to cover for older browsers, you can deserialize all dates coming from Ajax as follows:
(function () {
var DATE_START = "/Date(";
var DATE_START_LENGTH = DATE_START.length;
function isDateString(x) {
return typeof x === "string" && x.startsWith(DATE_START);
}
function deserializeDateString(dateString) {
var dateOffsetByLocalTime = new Date(parseInt(dateString.substr(DATE_START_LENGTH)));
var utcDate = new Date(dateOffsetByLocalTime.getTime() - dateOffsetByLocalTime.getTimezoneOffset() * 60 * 1000);
return utcDate;
}
function convertJSONDates(key, value) {
if (isDateString(value)) {
return deserializeDateString(value);
}
return value;
}
window.jQuery.ajaxSetup({
converters: {
"text json": function(data) {
return window.JSON.parse(data, convertJSONDates);
}
}
});
}());
I included logic that assumes you send all dates from the server as UTC (which you should); the consumer then gets a JavaScript Date object that has the proper ticks value to reflect this. That is, calling getUTCHours(), etc. on the date will return the same value as it did on the server, and calling getHours() will return the value in the user's local timezone as determined by their browser.
This does not take into account WCF format with timezone offsets, though that would be relatively easy to add.
Using the jQuery UI datepicker - really only makes sense if you're already including jQuery UI:
$.datepicker.formatDate('MM d, yy', new Date(parseInt('/Date(1224043200000)/'.substr(6))));
output:
October 15, 2008
Don't over-think this. Like we've done for decades, pass a numeric offset from the de-facto standard epoch of 1 Jan 1970 midnight GMT/UTC/&c in number of seconds (or milliseconds) since this epoch. JavaScript likes it, Java likes it, C likes it, and the Internet likes it.
Everyone of these answers has one thing in common: they all store dates as a single value (usually a string).
Another option is to take advantage of the inherent structure of JSON, and represent a date as list of numbers:
{ "name":"Nick",
"birthdate":[1968,6,9] }
Of course, you would have to make sure both ends of the conversation agree on the format (year, month, day), and which fields are meant to be dates,... but it has the advantage of completely avoiding the issue of date-to-string conversion. It's all numbers -- no strings at all. Also, using the order: year, month, day also allows proper sorting by date.
Just thinking outside the box here -- a JSON date doesn't have to be stored as a string.
Another bonus to doing it this way is that you can easily (and efficiently) select all records for a given year or month by leveraging the way CouchDB handles queries on array values.
Posting in awesome thread:
var d = new Date(parseInt('/Date(1224043200000)/'.slice(6, -2)));
alert('' + (1 + d.getMonth()) + '/' + d.getDate() + '/' + d.getFullYear().toString().slice(-2));
Just to add another approach here, the "ticks approach" that WCF takes is prone to problems with timezones if you're not extremely careful such as described here and in other places. So I'm now using the ISO 8601 format that both .NET & JavaScript duly support that includes timezone offsets. Below are the details:
In WCF/.NET:
Where CreationDate is a System.DateTime; ToString("o") is using .NET's Round-trip format specifier that generates an ISO 8601-compliant date string
new MyInfo {
CreationDate = r.CreationDate.ToString("o"),
};
In JavaScript
Just after retrieving the JSON I go fixup the dates to be JavaSript Date objects using the Date constructor which accepts an ISO 8601 date string...
$.getJSON(
"MyRestService.svc/myinfo",
function (data) {
$.each(data.myinfos, function (r) {
this.CreatedOn = new Date(this.CreationDate);
});
// Now each myinfo object in the myinfos collection has a CreatedOn field that is a real JavaScript date (with timezone intact).
alert(data.myinfos[0].CreationDate.toLocaleString());
}
)
Once you have a JavaScript date you can use all the convenient and reliable Date methods like toDateString, toLocaleString, etc.
var newDate = dateFormat(jsonDate, "mm/dd/yyyy");
Is there another option without using the jQuery library?
This may can also help you.
function ToJavaScriptDate(value) { //To Parse Date from the Returned Parsed Date
var pattern = /Date\(([^)]+)\)/;
var results = pattern.exec(value);
var dt = new Date(parseFloat(results[1]));
return (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear();
}
I get the date like this:
"/Date(1276290000000+0300)/"
In some examples the date is in slightly different formats:
"/Date(12762900000000300)/"
"Date(1276290000000-0300)"
etc.
So I came up with the following RegExp:
/\/+Date\(([\d+]+)\)\/+/
and the final code is:
var myDate = new Date(parseInt(jsonWcfDate.replace(/\/+Date\(([\d+-]+)\)\/+/, '$1')));
Hope it helps.
Update:
I found this link from Microsoft:
How do I Serialize Dates with JSON?
This seems like the one we are all looking for.
Below is a pretty simple solution for parsing JSON dates. Use the below functions as per your requirement. You just need to pass the JSON format Date fetched as a parameter to the functions below:
function JSONDate(dateStr) {
var m, day;
jsonDate = dateStr;
var d = new Date(parseInt(jsonDate.substr(6)));
m = d.getMonth() + 1;
if (m < 10)
m = '0' + m
if (d.getDate() < 10)
day = '0' + d.getDate()
else
day = d.getDate();
return (m + '/' + day + '/' + d.getFullYear())
}
function JSONDateWithTime(dateStr) {
jsonDate = dateStr;
var d = new Date(parseInt(jsonDate.substr(6)));
var m, day;
m = d.getMonth() + 1;
if (m < 10)
m = '0' + m
if (d.getDate() < 10)
day = '0' + d.getDate()
else
day = d.getDate();
var formattedDate = m + "/" + day + "/" + d.getFullYear();
var hours = (d.getHours() < 10) ? "0" + d.getHours() : d.getHours();
var minutes = (d.getMinutes() < 10) ? "0" + d.getMinutes() : d.getMinutes();
var formattedTime = hours + ":" + minutes + ":" + d.getSeconds();
formattedDate = formattedDate + " " + formattedTime;
return formattedDate;
}
You also can use the JavaScript library moment.js, which comes in handy when you plan do deal with different localized formats and perform other operations with dates values:
function getMismatch(id) {
$.getJSON("Main.aspx?Callback=GetMismatch",
{ MismatchId: id },
function (result) {
$("#AuthMerchId").text(result.AuthorizationMerchantId);
$("#SttlMerchId").text(result.SettlementMerchantId);
$("#CreateDate").text(moment(result.AppendDts).format("L"));
$("#ExpireDate").text(moment(result.ExpiresDts).format("L"));
$("#LastUpdate").text(moment(result.LastUpdateDts).format("L"));
$("#LastUpdatedBy").text(result.LastUpdateNt);
$("#ProcessIn").text(result.ProcessIn);
}
);
return false;
}
Setting up localization is as easy as adding configuration files (you get them at momentjs.com) to your project and configuring the language:
moment.lang('de');
Check up the date ISO standard; kind of like this:
yyyy.MM.ddThh:mm
It becomes 2008.11.20T22:18.
This is frustrating. My solution was to parse out the "/ and /" from the value generated by ASP.NET's JavaScriptSerializer so that, though JSON may not have a date literal, it still gets interpreted by the browser as a date, which is what all I really want:{"myDate":Date(123456789)}
Custom JavaScriptConverter for DateTime?
I must emphasize the accuracy of Roy Tinker's comment. This is not legal JSON. It's a dirty, dirty hack on the server to remove the issue before it becomes a problem for JavaScript. It will choke a JSON parser. I used it for getting off the ground, but I do not use this any more. However, I still feel the best answer lies with changing how the server formats the date, for example, ISO as mentioned elsewhere.
A late post, but for those who searched this post.
Imagine this:
[Authorize(Roles = "Administrator")]
[Authorize(Roles = "Director")]
[Authorize(Roles = "Human Resources")]
[HttpGet]
public ActionResult GetUserData(string UserIdGuidKey)
{
if (UserIdGuidKey!= null)
{
var guidUserId = new Guid(UserIdGuidKey);
var memuser = Membership.GetUser(guidUserId);
var profileuser = Profile.GetUserProfile(memuser.UserName);
var list = new {
UserName = memuser.UserName,
Email = memuser.Email ,
IsApproved = memuser.IsApproved.ToString() ,
IsLockedOut = memuser.IsLockedOut.ToString() ,
LastLockoutDate = memuser.LastLockoutDate.ToString() ,
CreationDate = memuser.CreationDate.ToString() ,
LastLoginDate = memuser.LastLoginDate.ToString() ,
LastActivityDate = memuser.LastActivityDate.ToString() ,
LastPasswordChangedDate = memuser.LastPasswordChangedDate.ToString() ,
IsOnline = memuser.IsOnline.ToString() ,
FirstName = profileuser.FirstName ,
LastName = profileuser.LastName ,
NickName = profileuser.NickName ,
BirthDate = profileuser.BirthDate.ToString() ,
};
return Json(list, JsonRequestBehavior.AllowGet);
}
return Redirect("Index");
}
As you can see, I'm utilizing C# 3.0's feature for creating the "Auto" Generics. It's a bit lazy, but I like it and it works.
Just a note: Profile is a custom class I've created for my web application project.
FYI, for anyone using Python on the server side: datetime.datetime().ctime() returns a string that is natively parsable by "new Date()". That is, if you create a new datetime.datetime instance (such as with datetime.datetime.now), the string can be included in the JSON string, and then that string can be passed as the first argument to the Date constructor. I haven't yet found any exceptions, but I haven't tested it too rigorously, either.
Mootools solution:
new Date(Date(result.AppendDts)).format('%x')
Requires mootools-more. Tested using mootools-1.2.3.1-more on Firefox 3.6.3 and IE 7.0.5730.13
var obj = eval('(' + "{Date: \/Date(1278903921551)\/}".replace(/\/Date\((\d+)\)\//gi, "new Date($1)") + ')');
var dateValue = obj["Date"];
Add the jQuery UI plugin in your page:
function DateFormate(dateConvert) {
return $.datepicker.formatDate("dd/MM/yyyy", eval('new ' + dateConvert.slice(1, -1)));
};
What if .NET returns...
return DateTime.Now.ToString("u"); //"2013-09-17 15:18:53Z"
And then in JavaScript...
var x = new Date("2013-09-17 15:18:53Z");
I failed to parse an ISO-8601 date "2011-04-26T13:16:50Z" on IE8 and Safari 5, but it worked on Chrome 10, FF4. Support seems to be quite mixed?
Does anyone know the actual status of which browsers can parse this format? I assume IE6, and 7 will fail too.
var d = Date.parse("2011-04-26T13:16:50Z");
I say shim it only if needed via a few tests,
here is one I already written:
(function() {
var d = window.Date,
regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})\.(\d{1,3})(?:Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/;
if (d.parse('2011-11-29T15:52:30.5') !== 1322581950500 ||
d.parse('2011-11-29T15:52:30.52') !== 1322581950520 ||
d.parse('2011-11-29T15:52:18.867') !== 1322581938867 ||
d.parse('2011-11-29T15:52:18.867Z') !== 1322581938867 ||
d.parse('2011-11-29T15:52:18.867-03:30') !== 1322594538867 ||
d.parse('2011-11-29') !== 1322524800000 ||
d.parse('2011-11') !== 1320105600000 ||
d.parse('2011') !== 1293840000000) {
d.__parse = d.parse;
d.parse = function(v) {
var m = regexIso8601.exec(v);
if (m) {
return Date.UTC(
m[1],
(m[2] || 1) - 1,
m[3] || 1,
m[4] - (m[8] ? m[8] + m[9] : 0) || 0,
m[5] - (m[8] ? m[8] + m[10] : 0) || 0,
m[6] || 0,
((m[7] || 0) + '00').substr(0, 3)
);
}
return d.__parse.apply(this, arguments);
};
}
d.__fromString = d.fromString;
d.fromString = function(v) {
if (!d.__fromString || regexIso8601.test(v)) {
return new d(d.parse(v));
}
return d.__fromString.apply(this, arguments);
};
})();
and in your code just always use Date.fromString(...) instead of new Date(...)
test a browser to see if the shim will be used:
http://jsbin.com/efivib/1/edit
works in all major browsers, used these references:
http://dev.w3.org/html5/spec/common-microsyntaxes.html
http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
http://msdn.microsoft.com/en-us/library/windows/apps/ff743760(v=vs.94).aspx
http://msdn.microsoft.com/en-us/library/windows/apps/wz6stk2z(v=vs.94).aspx
http://msdn.microsoft.com/en-us/library/windows/apps/k4w173wk(v=vs.94).aspx
!- microsoft connect requires a log in to view:
IE9 was failing on milliseconds with digit counts other than 3: (fixed in IE10)
https://connect.microsoft.com/IE/feedback/details/723740/date-parse-and-new-date-fail-on-valid-formats
IE10 is still (as of 1/17/2013) failing when the time zone is omitted (according to ECMA, this should defalt to Z or UTC, not local):
https://connect.microsoft.com/IE/feedback/details/776783/date-parse-and-new-date-fail-on-valid-formats
-- Read this if you care about where the standard is now / going in the future and why I can't get the IE team to recognize that their IE10 implementation is technically incorrect:
ECMAScript-262 v6.0 is going to move to the slightly more iso8601 compliant version of "if time zone indicator is omitted, assume local time"... so now there is a discrepancy, this implementation, chrome, mobile safari and opera all follow ECMAScript-262 v5.1, whereas IE10, firefox, desktop safari all seem to be following the more iso8601 compliant ECMAScript-262 v6.0 specification... this is confusing to say the least. When chrome or mobile safari pull the trigger and move to the ES6 implementation, I think this implementation should go with it leaving ES5.1 in the minority. I've read that this is listed in the "errata" of version 5.1 though I haven't found it. I'm more of the opinion that it's a little early to be pulling the trigger on ES6 just yet, but I'm also of the opinion that code needs to be practical, not ideal and move to where the browser makers move to. That said, it seems to be a 50/50 decision right now, so below is the "future" version of this code...
I should also mention that either version of the code will normalize "non-compliant" browsers to match the behavior of the other one, since that's what shims do ;)
HERE IS AN ADAPTED VERSION COMPATIBLE WITH ECMAScript-262 v6.0 (JavaScript Future)
see relevant sections here: (this is the only online html version of the spec I could find) http://people.mozilla.org/~jorendorff/es6-draft.html#sec-15.9.1.15
(function() {
var d = window.Date,
regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})\.(\d{1,})(Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/,
lOff, lHrs, lMin;
if (d.parse('2011-11-29T15:52:30.5') !== 1322599950500 ||
d.parse('2011-11-29T15:52:30.52') !== 1322599950520 ||
d.parse('2011-11-29T15:52:18.867') !== 1322599938867 ||
d.parse('2011-11-29T15:52:18.867Z') !== 1322581938867 ||
d.parse('2011-11-29T15:52:18.867-03:30') !== 1322594538867 ||
d.parse('2011-11-29') !== 1322524800000 ||
d.parse('2011-11') !== 1320105600000 ||
d.parse('2011') !== 1293840000000) {
d.__parse = d.parse;
lOff = -(new Date().getTimezoneOffset());
lHrs = Math.floor(lOff / 60);
lMin = lOff % 60;
d.parse = function(v) {
var m = regexIso8601.exec(v);
if (m) {
return Date.UTC(
m[1],
(m[2] || 1) - 1,
m[3] || 1,
m[4] - (m[8] ? m[9] ? m[9] + m[10] : 0 : lHrs) || 0,
m[5] - (m[8] ? m[9] ? m[9] + m[11] : 0 : lMin) || 0,
m[6] || 0,
((m[7] || 0) + '00').substr(0, 3)
);
}
return d.__parse.apply(this, arguments);
};
}
d.__fromString = d.fromString;
d.fromString = function(v) {
if (!d.__fromString || regexIso8601.test(v)) {
return new d(d.parse(v));
}
return d.__fromString.apply(this, arguments);
};
})();
hope this helps -ck
I had this issue today. I found momentjs was a good way of parsing ISO 8601 dates in a cross browser manor.
momentjs can also be used to output the date in a different format.
Simple function to parse ISO8601 date format in any browser:
function dateFromISO8601(isoDateString) {
var parts = isoDateString.match(/\d+/g);
var isoTime = Date.UTC(parts[0], parts[1] - 1, parts[2], parts[3], parts[4], parts[5]);
var isoDate = new Date(isoTime);
return isoDate;
}
Yes, Date.parse is not consistent for different browsers. You could:
Use Date.UTC instead, which breaks up the date-string into separate inputs
Use a wrapper library like jQuery's parseDate
Some older browsers return the wrong date (and not NaN)if you parse an ISO date string.
You can use your own method across all browsers, or use Date.parse if it is implemented correctly-
check a known timestamp.
Date.fromISO= (function(){
var diso= Date.parse('2011-04-26T13:16:50Z');
if(diso=== 1303823810000) return function(s){
return new Date(Date.parse(s));
}
else return function(s){
var day, tz,
rx= /^(\d{4}\-\d\d\-\d\d([tT][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/,
p= rx.exec(s) || [];
if(p[1]){
day= p[1].split(/\D/).map(function(itm){
return parseInt(itm, 10) || 0;
});
day[1]-= 1;
day= new Date(Date.UTC.apply(Date, day));
if(!day.getDate()) return NaN;
if(p[5]){
tz= parseInt(p[5], 10)*60;
if(p[6]) tz += parseInt(p[6], 10);
if(p[4]== "+") tz*= -1;
if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
}
return day;
}
return NaN;
}
})()
The ES5 spec deviates from the ISO8601 spec, especially when it comes to the treatment of dates without a timezone indicator / offset. There is a bug ticket at https://bugs.ecmascript.org/show_bug.cgi?id=112 describing the problem and it looks like it will be fixed in ES6.
For now, I recommend looking at https://github.com/csnover/js-iso8601 for a cross-browser implementation. I use https://github.com/csnover/js-iso8601/tree/lax which doesn't conform to the ES5 spec but has better interoperability with other JSON serialisation libraries such as JSON.NET.
I found ckozl answer really useful and interesting, but the regexp is not perfect and it didn't work in my case.
Apart from the fact that dates without minutes, secs or milisecs are not parsed, ISO 8501 spec says that the '-' and ':' separators are optional, so "2013-12-27" and "20131227" are both valid. In my case, this is important because I'm setting the server date and time in a JavaScript variable from PHP:
var serverDate = new Date(Date.parse("<?php date(DateTime::ISO8601); ?>"));
This code generates something like this:
<script>
var serverDate = new Date(Date.parse("2013-12-27T15:27:34+0100"));
</script>
The important part is the time zone designator "+0100" where the ':' is missing. Although Firefox parses that string correctly, IE (11) fails (if the ':' is added, then IE also works). The headache about the zonetime and the ECMAScript specifications described by ckozl is unimportant in my case, because PHP always add the time zone designator.
The RegExp I'm using, instead of that one from ckozl is:
var regexIso8601 = /^(\d{4}|\+\d{6})(?:-?(\d{2})(?:-?(\d{2})(?:T(\d{2})(?::?(\d{2})(?::?(\d{2})(?:(?:\.|,)(\d{1,}))?)?)?(Z|([\-+])(\d{2})(?::?(\d{2}))?)?)?)?)?$/;
Keep in mind that this regexp is not perfect either. ISO 8501 allows week specification (2007-W01-1 for monday, 1 Jan 2007), or decimal fractions in hours and minutes (18.50 for 18:30:00, or 18:30.25 for 18:30:15). But they are quite unusual.
P.D. This answer should be, I imagine, a comment to the original chozl answer, but I do not have reputation enough :(
As mentioned previously ISO 8601 style dates were added in ECMAScript version 5, where implementation is not consistent, and not available in all browsers. There are a number of script stubs available, but you may wish to simply add your own Date.parse* method.
(function() {
//ISO-8601 Date Matching
var reIsoDate = /^(\d{4})-(\d{2})-(\d{2})((T)(\d{2}):(\d{2})(:(\d{2})(\.\d*)?)?)?(Z|[+-]00(\:00)?)?$/;
Date.parseISO = function(val) {
var m;
m = typeof val === 'string' && val.match(reIsoDate);
if (m) return new Date(Date.UTC(+m[1], +m[2] - 1, +m[3], +m[6] || 0, +m[7] || 0, +m[9] || 0, parseInt((+m[10]) * 1000) || 0));
return null;
}
//MS-Ajax Date Matching
var reMsAjaxDate = /^\\?\/Date\((\-?\d+)\)\\?\/$/;
Date.parseAjax = function(val) {
var m;
m = typeof val === 'string' && val.match(reMsAjaxDate);
if (m) return new Date(+m[1]);
return null;
}
}();
I use the above method for JSON.parse hydration of dates...
JSON.parse(text, function(key, val) {
return Date.parseISO(val) || Date.parseAjax(val) || val;
});
ISO 8601 date formats were added with ECMAScript-262 v5. So if a browser is not v5 compatible, you simply cannot expect to be capable of handling ISO 8601 formats.
Browsers not being v5 compatible may use any implementation specific date formats they want. Most of them do at least support RFC822/RFC1123 date formats, though. Example:
var d = Date.parse("Wed, 26 Apr 2011 13:16:50 GMT+0200");
Microsoft Sharepoint 2013 is ALSO using a different notation eg "2013-04-30T22:00:00Z"
If you want to use the REST services from sharepoint 2013 in combination with Internet Explorer 8 ( IE8 ) , then the solution from ckozl is NOT working.
Youll get the NaN
change the regex line TO:
regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})(\.(\d{1,3}))?(?:Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/;
this will make the microseconds bit optional !
cheerio, Leo
Works in all browsers except Firefox, any ideas?
(function($){
$.relativetime = function(options) {
var defaults = {
time:new Date(),
suffix:'ago',
prefix:''
};
options = $.extend(true, defaults, options);
//Fixes NaN in some browsers by removing dashes...
_dateStandardizer = function(dateString){
modded_date = options.time.toString().replace(/-/g,' ');
return new Date(modded_date)
}
//Time object with all the times that can be used throughout
//the plugin and for later extensions.
time = {
unmodified:options.time, //the original time put in
original:_dateStandardizer(options.time).getTime(), //time that was given in UNIX time
current:new Date().getTime(), //time right now
displayed:'' //what will be shown
}
//The difference in the unix timestamps
time.diff = time.current-time.original;
//Here we save a JSON object with all the different measurements
//of time. "week" is not yet in use.
time.segments = {
second:time.diff/1000,
minute:time.diff/1000/60,
hour:time.diff/1000/60/60,
day:time.diff/1000/60/60/24,
week:time.diff/1000/60/60/24/7,
month:time.diff/1000/60/60/24/30,
year:time.diff/1000/60/60/24/365
}
//Takes a string and adds the prefix and suffix options around it
_uffixWrap = function(str){
return options.prefix+' '+str+' '+options.suffix;
}
//Converts the time to a rounded int and adds an "s" if it's plural
_niceDisplayDate = function(str,date){
_roundedDate = Math.round(date);
s='';
if(_roundedDate !== 1){ s='s'; }
return _uffixWrap(_roundedDate+' '+str+s)
}
//Now we loop through all the times and find out which one is
//the right one. The time "days", "minutes", etc that gets
//shown is based on the JSON time.segments object's keys
for(x in time.segments){
if(time.segments[x] >= 1){
time.displayed = _niceDisplayDate(x,time.segments[x])
}
else{
break;
}
}
//If time.displayed is still blank (a bad date, future date, etc)
//just return the original, unmodified date.
if(time.displayed == ''){time.displayed = time.unmodified;}
//Give it to em!
return time.displayed;
};
})(jQuery);
In Safari, this code returns the given date which my plugin date if it fails. This could happen due to a future date or an invalid date. However, I'm not sure as the time that is given is standard YY MM DD HH:mm:ss
Demo:
http://jsfiddle.net/8azeT/
I think the string used is wrong and then stripped of '-' very wrong:
'010111' - interpreted by FF as Jan 1 1911 (US FF)
Correct format is '01/01/2011' (US FF)
I wouldn't use this format at all as each country has it's own way of showing/ parsing dates.
The safest way to parse a string is probably to use:
'January 1, 2011 1:30:11 pm GMT'
but I would use a date object instead in the options and skip the string parsing to make sure the date is correct.
http://jsfiddle.net/8azeT/4/
Question is about Safari but content FF?
I am new to javascript and am trying to compare two date values ,I am getting two time value strings in the format
06:30:47 AM
01:10:47 PM
I need to compare these to find out if the first one is less than the other.I couldn't figure out how to do this in javascript.Can someone help?
o.h
I do not think that the standard implementation can parse this. I would do something like this:
function toDate(dateString) {
var timeComponents = dateString.replace(/\s.*$/, '').split(':');
if (dateString.indexOf("PM") > -1) {
timeComponents[0] += 12;
}
var date = new Date();
date.setHours(timeComponents[0]);
date.setMinutes(timeComponents[1]);
date.setSeconds(timeComponents[2]);
return date;
}
if (toDate('06:30:47 AM') > toDate('01:10:47 PM')) {
// ...
}
JavaScript's specified date/time parsing, what you can rely upon cross-browser, is surprisingly limited. For a long time, there was no single string date format that was mandated in the spec, and as of the recent 5th edition spec, the only mandated format is ISO-8601 (and some subsets). You can't yet rely on browsers having implemented that part of the 5th edition spec.
So you have a couple of choices:
Parse the string yourself and use the Date constructor that takes the individual parts of the date as numbers, e.g. new Date(year, month, day, hour, minute, second, ...). (You need only specify as many of those as you want, so for instance new Date(2010, 9, 14) is September 14th, 2010.)
Use a library like Moment that's already done the work for you. Moment lets you specify the format to parse.
Use the Date object. Check this: http://www.w3schools.com/jsref/jsref_obj_date.asp
Try putting the two values in Date variables and do this:
if(var1.valueOf() > var2.valueOf())
{
//Do Something
}
If your times are always in the format 00:00:00 AM then
var a="06:30:47 AM";
var b="01:10:47 PM";
var at=parseInt(a.substring(0,8).replace(/(^0+|:)/g,""));
var bt=parseInt(b.substring(0,8).replace(/(^0+|:)/g,""));
if (a.charAt(9)=="P") {at=at+120000};
if (b.charAt(9)=="P") {bt=bt+120000};
if (at<bt) {
// a is smaller
}
else
{
// a is not smaller
};
..should be cross-browser and time/format safe.
I tried something like this
var ts1="06:30:47 AM";
var ts2="01:10:47 PM";
var ds=new Date().toDateString();
var d1=new Date(ds+" "+ts1);
var d2=new Date(ds+" "+ts2);
if (!(d2>d1)){
alert("d1 should be less than d2");
}
Is there something wrong with this?
// specific formatter for the time format ##:##:## #M
var formatToMiliseconds = function(t){
t = t.split(/[:\s]/);
t = ((t[0] * 3600000) + (t[1] * 60000) * (t[2] * 1000)); // To ms
t = t + (/PM/i.test(t[3]) ? 43200000 : 0); // adjust for AM/PM
return t;
}
var time01 = formatToMiliseconds('06:30:47 AM');
var time02 = formatToMiliseconds('01:10:47 PM');
alert(time01 > time02); // false
allert(time01 < time02); // true
As a bonus, your time is now more compatible with the Date object and other time calculations.