Date parsing error in Safari - javascript

I have a date string with the following format:
2012-09-20T01:36:51.556Z
Its a date field returned from mongodb as is. Chrome, FF, and IE are able to parse this string, however, Safari fails with an error Invalid Date. I have tried DateJS, but it fails to parse this date too. Any ideas how can I easily parse this date? Or, what particular thing is causing Safari to fail?
I am using the native node driver for mongodb. And it returns date as a string in the above-mentioned format.

My DateExtensions should parse that:
http://depressedpress.com/javascript-extensions/dp_dateextensions/
After adding the library to the page you'd parse the date with the static Date.parseIso8601(date) method.
The extensions are a bit heavy for just that (although you can pull out just the data parsing stuff if you like).
I'm suprised that Safari is having such issues as the date appears, to me, to be a perfectly valid ISO 8601 date (as defined here: http://www.w3.org/TR/NOTE-datetime). This is about as plain vanilla as a date gets. Total guess, but have you tried replacing the "T" seperator with a space? It's not standard but I've seen a lot of implementations that use it that way (and my component allows it).
For what it's worth, here's my method pulled from DP_DateExtensions - I think it'll do the trick for you:
// parseIso8601
// Attempts to convert ISO8601 input to a date
Date.parseIso8601 = function(CurDate) {
// Check the input parameters
if ( typeof CurDate != "string" || CurDate == "" ) {
return null;
};
// Set the fragment expressions
var S = "[\\-/:.]";
var Yr = "((?:1[6-9]|[2-9][0-9])[0-9]{2})";
var Mo = S + "((?:1[012])|(?:0[1-9])|[1-9])";
var Dy = S + "((?:3[01])|(?:[12][0-9])|(?:0[1-9])|[1-9])";
var Hr = "(2[0-4]|[01]?[0-9])";
var Mn = S + "([0-5]?[0-9])";
var Sd = "(?:" + S + "([0-5]?[0-9])(?:[.,]([0-9]+))?)?";
var TZ = "(?:(Z)|(?:([\+\-])(1[012]|[0]?[0-9])(?::?([0-5]?[0-9]))?))?";
// RegEx the input
// First check: Just date parts (month and day are optional)
// Second check: Full date plus time (seconds, milliseconds and TimeZone info are optional)
var TF;
if ( TF = new RegExp("^" + Yr + "(?:" + Mo + "(?:" + Dy + ")?)?" + "$").exec(CurDate) ) {} else if ( TF = new RegExp("^" + Yr + Mo + Dy + "[Tt ]" + Hr + Mn + Sd + TZ + "$").exec(CurDate) ) {};
// If the date couldn't be parsed, return null
if ( !TF ) { return null };
// Default the Time Fragments if they're not present
if ( !TF[2] ) { TF[2] = 1 } else { TF[2] = TF[2] - 1 };
if ( !TF[3] ) { TF[3] = 1 };
if ( !TF[4] ) { TF[4] = 0 };
if ( !TF[5] ) { TF[5] = 0 };
if ( !TF[6] ) { TF[6] = 0 };
if ( !TF[7] ) { TF[7] = 0 };
if ( !TF[8] ) { TF[8] = null };
if ( TF[9] != "-" && TF[9] != "+" ) { TF[9] = null };
if ( !TF[10] ) { TF[10] = 0 } else { TF[10] = TF[9] + TF[10] };
if ( !TF[11] ) { TF[11] = 0 } else { TF[11] = TF[9] + TF[11] };
// If there's no timezone info the data is local time
if ( !TF[8] && !TF[9] ) {
return new Date(TF[1], TF[2], TF[3], TF[4], TF[5], TF[6], TF[7]);
};
// If the UTC indicator is set the date is UTC
if ( TF[8] == "Z" ) {
return new Date(Date.UTC(TF[1], TF[2], TF[3], TF[4], TF[5], TF[6], TF[7]));
};
// If the date has a timezone offset
if ( TF[9] == "-" || TF[9] == "+" ) {
// Get current Timezone information
var CurTZ = new Date().getTimezoneOffset();
var CurTZh = TF[10] - ((CurTZ >= 0 ? "-" : "+") + Math.floor(Math.abs(CurTZ) / 60))
var CurTZm = TF[11] - ((CurTZ >= 0 ? "-" : "+") + (Math.abs(CurTZ) % 60))
// Return the date
return new Date(TF[1], TF[2], TF[3], TF[4] - CurTZh, TF[5] - CurTZm, TF[6], TF[7]);
};
// If we've reached here we couldn't deal with the input, return null
return null;
};
I'm sure you could create a much smaller, more streamlined version (my code is written more for maintainability than compactness) - but this will manage most variations of ISO8601 date times and has never let me down. ;^)
Hope this helps!

I ended up converting the date to a TimeStamp on the server side, before sending it to client. NodeJS (which is of course, based on Chrome's JavaScript engine) parses this format just fine.

I had the same issue and this worked for me: https://github.com/csnover/js-iso8601
It overrides the Date.parse function.

Related

How can I get the timezone offset from string in javascript?

I have a string 2021-04-07T13:51:39.664+11:00 which I use to create a Date object: d = new Date('2021-04-07T13:51:39.664+03:00').
After that, d is an instance to represent the date time. Now I'd like to get the timezone offset from d, but d.getTimezoneOffset() always return local timezone offset which is -600 in my case. How can I get the timezone offset from the string? Does it get lost when I build the date instance? Do I have to parse the string to get it?
If I have a date object, how can I generate a string with the offset at the end: 2021-04-07T13:51:39.664+03:00? toISOString() only generate UTC time.
I know moment is a good date library but I don't want to add this dependency since it is too large.
function dateZ(dateString) {
var _date = dateString.substring(0, 23);
var _offset = dateString.substring(23);
var _dateValue = new Date(dateString);
function offset(value) {
if (value) {
_offset = value;
_dateValue = new Date(toString());
}
return _offset;
}
function date(value) {
if (value) {
_date = value;
_dateValue = new Date(toString());
}
return _dateValue;
}
function toString() {
return `${_date}${_offset}`
}
return { offset, date, toString };
}
var d = dateZ('2021-04-07T13:51:39.664+03:00');
console.log(d.date(), d.offset(), d.toString());
d.offset('+05:30');
console.log(d.date(), d.offset(), d.toString());
This is probably not the best possible approach, but it is portable and quite useful if you are in control of the application environment.
friendlyDate = function( x ){
var label = "Day Month Date Year Time Offset".split(" "),
d = x ? new Date(x) : new Date(),
s = ( d + "" ) .split( / / );
s.length = label.length;
for( var x in s )s[ label[ x ] ] = s[ x ];
s.length = 0;
return s
}
/**/
dte = friendlyDate();
for(var x in dte)console.log(x + ": "+ dte[x]);
p.s.: you might need to readjust\rearrange the labels for certain targeted browsers. [Not all browser vendor\version date string representations are returned equal ;)]
How to initialize a JavaScript Date to a particular time zone
There is no time zone or string format stored in the Date object itself.
Seems like either string parsing or a library (there are lighter-weight ones than moment if you're interested).

DateTime-Locale value isn't being set by javascript

I have two DateTime-Locale input in a form, and I want to set the values of them to the date and time when the view loads, and 10 minutes after that. I've been following this to do it: Setting value of datetime-local from Date , but haven't been working.
These are examples of what I've been trying (in all the cases I've tried with document.getElementById("eve_start_date_id").value=... and var date = document.getElementById("eve_start_date_id"); date.value=...:
1º, I've tried this one, without the :ss and adding '.Replace(' ', 'T') to the ToString, but this doesn't even execute.
function defaultDate() {
var date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
document.getElementById("eve_start_date_id").value = date;
}
2º, this function completes, but the value doesn't get assigned. I've tried also with toLocaleDateString() and toISOString() to no avail:
function defaultDate() {
var d = new Date();
var elem = document.getElementById("eve_start_date_id");
elem.value = d.toLocaleString();
}
3º, this one also completes but neither assign the value. I've tried this changing the order of d.getDate()& d.getMonth(), changing the date join from /to -, the separator of date and time from to T, and adding :00 at the end of localDateTime, to no avail.
function defaultDate() {
Number.prototype.AddZero = function (b, c) {
var l = (String(b || 10).length - String(this).length) + 1;
return l > 0 ? new Array(l).join(c || '0') + this : this;
}//to add zero to less than 10,
var d = new Date(),
localDateTime = [d.getDate().AddZero(),
(d.getMonth() + 1).AddZero(),
d.getFullYear()].join('/') + ' ' +
[d.getHours().AddZero(),
d.getMinutes().AddZero()].join(':');
document.getElementById("eve_start_date_id").value = localDateTime;
}
At this point I don't know what I can try or if I missed some basic stuff to set the value. Any help is welcome
Okay, I've figured this out. I was using the format that the DateTime-Local gave when submitting the form (dd/MM/yyyy HH:mm) when the correct format to set the value is yyyy-MM-ddTHH:mm, so to make it work, the case 1 should look like this:
function defaultDate() {
var date = DateTime.Now.ToString("yyyy-MM-ddTHH:mm");
document.getElementById("eve_start_date_id").value = date;
}
And this is for the case 3, though it's easier using the case 1:
function defaultDate() {
Number.prototype.AddZero = function (b, c) {
var l = (String(b || 10).length - String(this).length) + 1;
return l > 0 ? new Array(l).join(c || '0') + this : this;
}//to add zero to less than 10,
var d = new Date(),
localDateTime = [d.getFullYear(),
(d.getMonth() + 1).AddZero(),
d.getDate().AddZero()].join('-') + 'T' +
[d.getHours().AddZero(),
d.getMinutes().AddZero()].join(':');
document.getElementById("eve_start_date_id").value = localDateTime;
}

toLocaleTimeString() returning military time

I have a function that converts a SQL datetime stamp into a formatted time. It looks good on an iOS device, but its displayed as military time on an android device. How can I get this to return the toLocaleTimeString() as not military time on an android device?
function fromDateString(str) {
var res = str.match(/\/Date\((\d+)(?:([+-])(\d\d)(\d\d))?\)\//);
if (res == null)
return new Date(NaN); // or something that indicates it was not a DateString
var time = parseInt(res[1], 10);
if (res[2] && res[3] && res[4]) {
var dir = res[2] == "+" ? -1 : 1,
h = parseInt(res[3], 10),
m = parseInt(res[4], 10);
time += dir * (h*60+m) * 60000;
}
return formatdate.toLocaleTimeString();
}
The Date.toLocaleTimeString() function` is "implementation dependent" which means that if you want to guarantee a certain format on all devices then you must apply it yourself.
Here's how I would do it:
function formatTimeString(date) {
if ((typeof(date)!=='object') || (date.constructor!==Date)) {
throw new Error('argument must be a Date object');
}
function pad(s) { return ((''+s).length < 2 ? '0' : '') + s; }
function fixHour(h) { return (h==0?'12':(h>12?h-12:h)); }
var h=date.getHours(), m=date.getMinutes(), s=date.getSeconds()
, timeStr=[pad(fixHour(h)), pad(m), pad(s)].join(':');
return timeStr + ' ' + (h < 12 ? 'AM' : 'PM');
}
formatTimeString(new Date());
// => "09:19:03 AM"
formatTimeString(new Date('2012-12-19T20:09:10-0700'));
// => "08:09:10 PM"
formatTimeString(new Date('2012-12-19T00:13:14-0700'));
// => "12:13:14 AM"

Javascript Valid Date Checking does not work in IE8 (and Firefox)

I have tried two popular answers from Detecting an "invalid date" Date instance in JavaScript for checking valid dates. I tested both of them in IE8 – Unfortunately both are disappointing. See it here http://jsfiddle.net/Lijo/uzSU6/2/
Is there a better JavaScript code that will work in IE8 + Chrome + Firefox?
Note: To my surprise, it doesn't work well in Firefox too...
CONDITION
The date format is expected to be US date format with slashes (/)
CODE
isValidDateCheck2('12/33/2012') ;
isValidDateCheck1('12/12/2012') ;
function isValidDateCheck1(d)
{
alert(Object.prototype.toString.call(d));
if ( Object.prototype.toString.call(d) !== "[object Date]" )
{
alert('Not Valid');
}
if(!isNaN(d.getTime()))
{
alert(d.getTime());
}
}
function isValidDateCheck2(d)
{
var timestamp=Date.parse(d);
alert(timestamp);
if (isNaN(timestamp)==false)
{
var date=new Date(timestamp);
alert(date);
}
}
EDIT
#mplungjan approach (first suggested) is listed in http://jsfiddle.net/Lijo/uzSU6/7/. This was failed in IE8 for one scenario - http://jsfiddle.net/Lijo/uzSU6/12/.
You seem to be conflating two things here. Valid date objects and valid dates. These are not the same problem.
The question you linked to answers how to test for validity of date objects (whether a date object is an "invalid date" instance). Invalid date objects are generated when you use invalid parameters when constructing them: new Date('?')
What you want is to test if a date string conforms to a predefined date format. This is an entirely different problem that should not be solved by using only date objects.
Generally speaking, there are a couple of reasons for this; the first is that the browsers will helpfully compute overflow months/days/time to the correct date: new Date(2012,0,290) === Oct 06 2012.
Secondly because the parser may be locale dependent (mm/dd vs. dd/mm?). When the date is parsed by the browser my locale may cause it roll it to my timezone/DST thus skewing it and messing up detection (.getDate may now return next day over). Even worse, this may only occur across some timezones at certain parts of the year.
I strongly encourage using a library like date.js to handle this stuff because dates are much harder than you think! If you absolutely must validate by hand, then I recommend doing it in detail like this:
function isValidDate (str) {
// parse to numbers
const rm = str.split('/');
const m = 1 * rm[0];
const d = 1 * rm[1];
const y = 1 * rm[2];
if (isNaN(m * d * y)) {
return false;
}
// day can't be 0
if (d < 1) {
return false;
}
// month must be 1-12
if (m < 1 || m > 12) {
return false;
}
// february
if (m === 2) {
const isLeapYear = ((y % 4 === 0) && (y % 100 !== 0)) || (y % 400 === 0);
// leap year
if (isLeapYear && d > 29) {
return false;
}
// non-leap year
if (!isLeapYear && d > 28) {
return false;
}
}
// test any other month
else if (
((m === 4 || m === 6 || m === 9 || m === 11) && d > 30) ||
((m === 1 || m === 3 || m === 5 || m === 7 || m === 8 || m === 10 || m === 12) && d > 31)) {
return false;
}
return true;
}
As a jsFiddle: http://jsfiddle.net/3pMPp/1/
As a jsPerf: http://jsperf.com/silly-date-valiation
This will handle actual dates and give you the chance to find what part of the date was invalid - using the DATE OBJECT
NOTE: several browsers will happily parse what seems to be an invalid date and make a date object out of it. For example 02/29/2013 will parse as 1st of March 2013, hence my test to see if the parts entered made sense when used in an actual date.
DEMO
Tested in
Win7:
Chrome 23 (only one to give isNaN on the first date)
IE 9
Win XP:
FX 17
IE 8
Safari 5
Opera 11 and 12
function isValidDateCheck(dString) {
// test it is nn/nn/nnnn or nn/nn/nn
var dRe = /^(\d{1,2})([\-\/])(\d{1,2})\2(\d{4}|\d{2})$/
if (!dRe.exec(dString)) {
return false;
}
// make sure it parses as date
// replace this part if you do not allow dashes
dString.replace(/-/g,"/");
var date = new Date(dString); // create a date object
if (!isNaN(date)) { // it may give NaN - if not test the parts
var parts = dString.split("/"); // split on slash
var dd = parseInt(parts[1],10); // day number
var mm = parseInt(parts[0],10)-1; // month - JS months start at 0
var yyyy = parseInt(parts[2],10); // year
// return true if all parts match
return dd===date.getDate() && mm === date.getMonth() && yyyy===date.getFullYear();
}
// here the date was not parsed as a date
return false;
}
window.onload=function() {
document.getElementById("output").innerHTML+="<br/>12/33/2012: "+isValidDateCheck('12/33/2012');
document.getElementById("output").innerHTML+="<br/>12/12/2012: "+isValidDateCheck('12/12/2012') ;
document.getElementById("output").innerHTML+="<br/>02/29/2012: "+isValidDateCheck('02/29/2012') ;
document.getElementById("output").innerHTML+="<br/>02/29/2013: "+isValidDateCheck('02/29/2013') ;
document.getElementById("output").innerHTML+="<br/>01/01/2013A: "+isValidDateCheck('01/01/2013A') ;
}
Thanks to #mplungjan. I have upvoted that answer.
#mplungjan approach (first suggested) is listed in http://jsfiddle.net/Lijo/uzSU6/7/. This was failed in IE8 for one scenario - http://jsfiddle.net/Lijo/uzSU6/12/.
So I have used a slightly different approach after referring How to validate a date?. See it here http://jsfiddle.net/Lijo/uzSU6/20/
EDIT
Please refer http://jsfiddle.net/uzSU6/37/ for scenarios that handle blank spaces
Feel free to give your suggestions/ challenges with this approach.
References
Check whether white spaces exist without using trim
Which equals operator (== vs ===) should be used in JavaScript comparisons?
How to validate a date?
CODE
function isValidDate(s)
{
var bits = s.split('/');
if(s.indexOf(' ') != -1)
{
//White space exists in the original date string
return false;
}
//Javascript month starts at zero
var d = new Date(bits[2], bits[0] - 1, bits[1]);
if ( isNaN( Number(bits[2]) ) )
{
//Year is not valid number
return false;
}
if ( Number(bits[2]) < 1 )
{
//Year should be greater than zero
return false;
}
//1. Check whether the year is a Number
//2. Check whether the date parts are eqaul to original date components
//3. Check whether d is valid
return d && ( (d.getMonth() + 1) == bits[0]) && (d.getDate() == Number(bits[1]) );
}
​

Javascript Date: Ensure getMinutes(), getHours(), getSeconds() puts 0 in front if necessary

Looking for a creative way to be sure values that come from the getHours, getMinutes, and getSeconds() method for the javascript Date object return "06" instead of 6 (for example). Are there any parameters that I don't know about? Obviously I could write a function that does it by checking the length and prepending a "0" if need be, but I thought there might be something more streamlined than that.
Thanks.
Similar to #jdmichal's solution, posting because I'd prefer something a little shorter:
function pad(n) { return ("0" + n).slice(-2); }
pad(6); // -> "06"
pad(12); // -> "12"
Rather than add individual methods to Date.prototype, you could just add this method to Number.prototype:
Number.prototype.pad = function (len) {
return (new Array(len+1).join("0") + this).slice(-len);
}
// Now .pad() is callable on any number, including those returned by
var time = date.getHours().pad(2) + ":"
+ date.getMinutes().pad(2) + ":"
+ date.getSeconds().pad(2);
Update: ECMAScript 2017 (ECMA-262)
padStart has been added to pad the beginning of a string with another string, where the first value is the length it should be and the second value being what to pad it with.
For example:
let d = new Date()
let h = `${d.getHours()}`.padStart(2, '0')
let m = `${d.getMinutes()}`.padStart(2, '0')
let s = `${d.getSeconds()}`.padStart(2, '0')
let displayDate = h + ":" + m + ":" + s
// Possible Output: 09:01:34
Pre-ECMAScript 2017
As far as I know, there's not. And I do this all the time for converting dates to the XML dateTime format.
It's also important to note that those methods you list return a number, not a string.
You can, of course, add these yourself by modifying Date.prototype.
Date.prototype.getHoursTwoDigits = function()
{
var retval = this.getHours();
if (retval < 10)
{
return ("0" + retval.toString());
}
else
{
return retval.toString();
}
}
var date = new Date();
date.getHoursTwoDigits();
function pad2(number) {
return (number < 10 ? '0' : '') + number
}
document.write(pad2(2) + '<br />');
document.write(pad2(10) + '<br />');
OUTPUT:
02
10
Ok so heres my solution
function pad(n) {
return ((n <= 9 && n >= 0) ? "0":"") + n.toString();
}

Categories