Both the above new date formats give different results.
new Date(2015,2,30) : Date 2015-03-29T18:30:00.000Z
new Date('2015-2-30') : Invalid Date
Why is this different ?
EDITS
Points :
1. Month index in the first format start with 0.
2. The first format handles the overflow of the dates and hence is not a prefered way to test for invalid dates.
For eg : new Date(2015, 1, 30) {when the user is looking for 30,Feb,2015} will be converted to 01,March,2015. That is the extra date is carry forwarded to the month. Pretty indecent a convert according to me. However, if you write new Date("2015-2-30"){when the user is looking for 30,Feb,2015} , this will be an invalid date.
You're calling the Date constructor with different types of parameters :
in the first case you're providing integers which it uses to populate its fields (the first three being the year, the month and the day of the month) ; note that the monthes are 0-indexed : a value of 1 in that field will correspond to February rather than January
in the second case you're providing a String which will be parsed as if passed to Date.parse, that is as an ISO 8601 Extended Format date (YYYY‐MM‐DDTHH:mm:ss.sssZ)
Check docs.
It's because by first example you pass arguments
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);
And by second one you pass date string that is not valid. Missing 0 before 2
new Date(dateString);
You can create new Date in these ways:
var d = new Date();
var d = new Date(milliseconds);
var d = new Date(dateString);
var d = new Date(year, month, day, hours, minutes, seconds, milliseconds);
For dateString it must be provided by one of these input:
ISO Date "2015-03-25" (The International Standard)
Short Date "03/25/2015" or "2015/03/25"
Long Date "Mar 25 2015" or "25 Mar 2015"
Full Date "Wednesday March 25 2015"
There is a fundamental rule with ECMAScript dates: never use the Date constructor or Date.parse (they are equivalent for parsing) to parse strings.
Given new Date(2015,2,30) the values are treated as "local" for 30 March 2015, so the UTC time value is adjusted for the timezone offset on the host machine for that date. The outcome will be identical in all implementations consistent with all versions of ECMAScript from ed 3 onward at least.
Given the string new Date('2015-3-30'), the browser parsing algorithm is used. That string it might be treated as:
An invalid date, which is the result in Safari since it's ISO–like but not the correct format and the day is invalid for February
ISO 8601 and hence UTC for 30 March 2015, which is the result in Firefox and is also consistent with the standard since it's not valid ISO 8601 and hence can be parsed any way the implementation wishes to
A local date for 30 March 2015 which is the result in Chrome.
Also, given new Date('2015-2-30'):
Safari and Firefox give an invalid date
Chrome gives 2 March 2015 as a local date (i.e. 30 February rolls over to 2 March, which is the same behaviour as new Date(2015,1,30))
All the above outcomes are consistent with ECMAScript 2016. Hence the opening statement.
Edit
The quickest way to validate the values of a date while parsing is to test the month, in the simplest case of parsing a date in d/m/y format:
function parseDMY(s) {
var b = s.split(/\D/);
var d = new Date(b[2], --b[1], b[0]);
// If month is wrong, return invalid date
return d && d.getMonth() == b[1]? d : new Date(NaN);
}
// Dates in d/m/y format
['23/10/2015','32/10/2015','12/13/2015'].forEach(function(s){
var d = parseDMY(s);
console.log(s + ' : ' + (isNaN(d)? 'Invalid' : 'Valid'));
});
The output will be
console.log(new Date(2015,2,30)); // here 2 represents the march, ,month starts from 0 where 0 represents first month
console.log(new Date('2015-3-30'));
Mon Mar 30 2015 00:00:00 GMT+0530 (India Standard Time)
Mon Mar 30 2015 00:00:00 GMT+0530 (India Standard Time)
new Date('2015-2-30') // it means 30th day in February; which will convert it to second march
Related
let's say I have this date as input :
var _dateA = 2018-11-15T11:13:26.687Z
If I'm doing, whatever,
var _dateB = new Date(_date)
or
var _dateB = moment(_date)
I get this as result ==>
_dateB = Thu Nov 15 2018 12:13:26 GMT+0100 (heure normale d’Europe centrale)
I understood that there's timezone trouble, but how can I get a Date object or Moment object, without having this one hour more?
Wanted result => Thu Nov 15 2018 11:13:26 GMT+0100
Current result => Thu Nov 15 2018 12:13:26 GMT+0100
You need to use Date.toUTCString() that converts date to string, using the UTC time zone
var _dateA = '2018-11-15T11:13:26.687Z';
var _dateB = new Date(_dateA);
console.log(_dateB.toUTCString());
When you "output" a Date object via console.log(), alert(), etc the toString() method is used by default, converting the date object to a local timezone string for display (which is why you are seeing your date in your local time).
Parsing date strings with the Date constructor is not recommended (although, I suspect that most browsers probably handle ISO 8601 dates, like the one in your question, fairly well) - see the dateString parameter note here. So, if you need to construct a date object as well as output a date string, then you could parse the ISO 8601 string with split() using a regex character set for multiple delimiters, then construct a UTC date object with new Date(Date.UTC(...)). You could also do this with moment.js but the below should illustrate what is happening in a bit more detail.
For example:
const text = '2018-11-15T11:13:26.687Z'
const [y, m, d, hh, mm, ss, ms] = text.split(/[-T:.Z]/);
const date = new Date(Date.UTC(y, m - 1, d, hh, mm, ss, ms));
console.log(date.toLocaleString()); // date string in local timezone
console.log(date.toUTCString()); // UTC date string
console.log(JSON.stringify(date)); // ISO 8601 date string in most browsers
I'm making a business day calculator that takes a date and an offset and returns a date. So if you pass it Monday, Feb 27, 2017 and want to know the date 2 business days prior it returns Thursday, Feb 23, 2017. It also takes into account holidays, so if in the previous example, Feb 23, 2017 was a holiday it would return Feb 22, 2017.
I have a list of holidays in SQL Server in a date field in the format yyyy-mm-dd. The problem is that when they're converted to javascript Date objects it appends a timezone offset and in the case of Eastern Standard Time subtracts 5 hours from the date, rolling it back to the previous day.
new Date('2017-02-20');
becomes
Sun Feb 19 2017 19:00:00 GMT-0500 (Eastern Standard Time)
or
Sun Feb 19 2017 20:00:00 GMT-0400 (Eastern Daylight Time)
It seems to flip back and forth between standard time and daylight time randomly.
How do I prevent javascript from messing up my dates regardless of where the user lives? I only care about dates, not times.
Parsing strings with the Date constructor (or Date.parse) is not recommended as it's largely implementation dependent. In recent browsers, ISO 8601 format dates are parsed as UTC, hence the reason they are offset by the host timezone offset. Older browsers will not parse ISO 8601 format dates at all, and some parse them as local.
Parsing a date requires a 2 line function, validation requires one more:
/* Parse an ISO 8601 format date string as local
** #param {string} s - date informat yyyy-mm-dd
** #returns {Date}
*/
function parseLocal(s) {
var b = s.split(/\D/);
var d = new Date(b[0], --b[1], b[2]);
return d && d.getMonth() == b[1]? d : new Date(NaN);
}
console.log(parseLocal('2017-02-25').toString()); // 25 Feb 2017
console.log(parseLocal('2017-02-30').toString()); // Invalid date
Why does javaScript allow creation of dates such as
invalidDate = new Date(2015,3,31);
Here I am trying to create a date with April 31st. Instead JavaScript creates a date as Fri May 01 2015 00:00:00. Should we validate the date before creating it?
There are scenarios when we try to parse invalid dates and it does the same thing. How should one parse the date correctly when the given date may not be valid?
var invalidDate = new Date(2015, 3, 31);
alert(invalidDate);
You can use the Date API to create the date and check the result:
function makeDate(year, month, day) {
var d = new Date(year, month, day);
if (d.getFullYear() !== year || d.getMonth() !== month)
throw new Error("Invalid date");
return d;
}
If the day is not a valid day for the given month, then the month will be adjusted; that's a feature of the Date API that allows easy "date math". If the above code notices that the input values have been corrected, it throws an exception.
Because there is no 31st April, so it's giving you the nearest valid date...
For clarity, as below, any integer above a valid date will roll over to the following month.
i.e. Date(2016,0,1) is 1st Jan 2016.
Date(2016,0,61) adds 60 days on to that date, which rolls past the 29th Feb 2016 and into March, thus..
Date(2016,0,61) = 1st March 2016
As a result, Date(2015,3,31) = 30th April 2015 plus one day = 1st May 2015
You can validate the date with a function like this:
function isValidDate(year, month, day) {
var m = month - 1; // Note: the month is 0-based
var d = new Date(year, m, day);
/*
For invalid dates, either the day or month gets changed:
2015-04-31 -> 2015-05-01
2015-02-29 -> 2015-03-01
2015-13-10 -> 2016-01-10
2016-00-10 -> 2015-12-10
*/
return d.getDate() == day && d.getMonth() == m;
}
In general, you should never trust user input. As a result, you should try to put them in a situation where they can not enter bad data. This can be done through validation or thought a User interface that only enables them to select valid dates.
You can look around SO for some examples on validating dates: Detecting an "invalid date" Date instance in JavaScript. If you are using a javascript framework they might already have something built into it as well.
As for why it works and "lets you do it", I would suspect that is because javascript's parse function calculates the "milliseconds elapsed since January 1, 1970, 00:00:00 UTC". The resulting milliseconds is then used and javascript represents that as the correct date.
when i select a date say 12/03/2014 it retuns Tue Dec 02 2014 16:00:00 GMT-0800 (Pacific Standard Time) one day before what i entered?
<script>
function getAge() {
var today = new Date();
var birthDate = new Date(document.forms["name"]["birth"].value);
var age = today.getFullYear() - birthDate.getFullYear();
var m = today.getMonth() - birthDate.getMonth();
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate()+1)) {
age--;
}
document.getElementById('age').innerHTML = birthDate;
}
</script>
<form name='name' onsubmit='return false'>
<input type="date" name="birth">
<p id='age'></p>
<button onclick='getAge()'>check</button>
</form>
This is an old question, but I wanted to resurrect it since this was the first result in my Google query and the accepted answer doesn't really answer the question well. So I'm providing a bit more information for others to stumble upon.
There are several convenience methods in the Date object which may be of help to those trying to work around this little JavaScript quirk.
It seems to me that there is no way to change a Date object timezone offset programmatically, and it must be done using String manipulation, which is truly undesirable, but there is no other way. The Date object constructor always uses the local machine timezone when interpreting dates, and this cannot be changed. As such, you must understand that when you create a Date which essentially ignores the timezone, the altered Date object will represent a different moment in time from the original Date. The new Date object will automatically adopt the local machine's timezone but it will use the (year, month, date, hour, min, sec, ms) which you designate in either the individual constructor parameters or string parameter of the Date class (of the two options below).
I found two ways to work around this and produce a correct date from a type="date" input field. Please use the one you feel is more effective or appropriate for your solution. Personally, I prefer using OPTION B since it is more programatic than string manipulation and probably a bit more reliable as such. It's also a bit simpler.
OPTION A: UTC date string manipulation
getUTCDate - returns the day of the month (from 1 to 31) of the date object, according to universal time. The UTC methods calculate their date assuming that the date object is of local time and date.
getUTCString - converts a Date object to a string, according to universal time.
NOTE: The Date constructor reverts back to local timezone. Therefore, you cannot build a new Date with the getUTCString() value and expect to get the correct date from the input field.
You can solve this issue by using getUTCDate() and calling dateObject.setDate(dateObject.getUTCDate());, which corrects only the date portion of the object, so you will need to correct the month and year as well using dateObject.setMonth(dateObject.getUTCMonth()); and dateObject.setFullYear(dateObject.getUTCFullYear()); respectively. Or you can remove everything after the "GMT" portion of the getUTCString() value, which will give you a string without a timezone designation. You can then use the string to create a new Date object, which will default to the client's timezone.
//convert to UTC to avoid "date minus one" issues where the supplied date is interpreted incorrectly as
//the previous date.
date = new Date(document.getElementById('date').value);
console.log("original date: " + date.toString());
console.log("UTC date string: " + date.toUTCString());
//produces local machine timezone, and therefore incorrect date value
date = new Date(date.toUTCString());
console.log("UTC date string Date constructor: " + date.toString());
utcDate = new Date(utcString.substr(0, utcString.indexOf("GMT")));
console.log("UTC date string Date constructor (without 'GMT*'): " + utcDate.toString());
console.log("UTC date number: " + date.getUTCDate());
date.setDate(date.getUTCDate());
date.setMonth(date.getUTCMonth());
date.setFullYear(date.getUTCFullYear());
console.log("UTC modified date: " + date.toString());
Output:
original date: Fri Jan 29 2021 23:16:11 GMT-0600 (Central Standard Time)
UTC date string: Sat, 30 Jan 2021 05:16:11 GMT
UTC date string Date constructor: Fri Jan 29 2021 23:16:11 GMT-0600 (Central Standard Time)
UTC date string Date constructor (without 'GMT*'): Sat Jan 30 2021 23:16:11 GMT-0600 (Central Standard Time)
UTC date number: 30
UTC modified date: Sat Jan 30 2021 23:16:11 GMT-0600 (Central Standard Time)
OPTION B: provide all UTC parameters to Date constructor
(see also: Create a Date with a set timezone without using a string representation)
example:
date = new Date(document.getElementById('date').value);
console.log("original date: " + date.toString());
date = new Date(
date.getUTCFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
date.getUTCHours(),
date.getUTCMinutes(),
date.getUTCSeconds(),
date.getUTCMilliseconds());
console.log("Date with UTC parameters: " + date.toString());
Output:
original date: Fri Jan 29 2021 23:04:03 GMT-0800 (Pacific Standard Time)
Date with UTC parameters: Sat Jan 30 2021 07:04:03 GMT-0800 (Pacific Standard Time)
the incorrect date is due to your timezone settings. It's GMT-8.
Consider using UTC: http://www.w3schools.com/jsref/jsref_toutcstring.asp
I'm in javascript, running this in the console
d = new Date();
d.setMonth(1);
d.setFullYear(2009);
d.setDate(15);
d.toString();
outputs this:
"Sun Mar 15 2009 18:05:46 GMT-0400 (EDT)"
Why would this be happening? It seems like a browser bug.
That's because when you initialize a new Date, it comes with today's date, so today is Oct 30 2008, then you set the month to February, so there is no February 30, so set first the day, then the month, and then the year:
d = new Date();
d.setDate(15);
d.setMonth(1);
d.setFullYear(2009);
But as #Jason W, says it's better to use the Date constructor:
new Date(year, month, date [, hour, minute, second, millisecond ]);
It's probably best to construct a Date object in one step to avoid the Date object being in an ambiguous or invalid state:
d = new Date(2009, 1, 15);
d = new Date();
d.setDate(15);
d.setMonth(1);
d.setFullYear(2009);
d.toString();
This works.
After a bunch of testing in FF3 on XP with Firebug, here are the things I can tell you
Calling Date.setDate() after calling Date.setMonth() will generate this odd behavior.
Date.setMonth() forces the timezone to be CST (or, some non DST-aware zone)
Date.setDate() forces the timezone to be CDT (or, some DST-aware zone)
So, there's definitely something wonky going on with setMonth() and setDate() in respect to the timezone.
The only solution I can offer is this: Set the date before you set the month.
This will work generally to avoid the rollover behavior of the javascript Date API:
d.setDate(1);
d.setFullYear(year);
d.setMonth(month);
d.setDate(day);
Given that year + month + day are in a "valid" combination, e.g. taken from another Date object using getFullYear(), getMonth(), getDate().
The important parts are:
starting with setDate(1) to avoid possible rollover when the current date value is 29, 30 or 31
call setMonth(month) before setDate(day) to avoid the same rollover in case the current month value is "problematic" (because then the initial setDate(1) would be without effect)