It seems to me that Date.parse assumes all months have 31 days. Including months with 30 days and including February(which should only ever have 28/29 days).
I checked this question 31 days in February in Date object
But the answer there suggested there was nothing wrong with Date in his issue..Somebody said something to the questioner about zero indexing and he pretty much said "oh ok", and determined that it was his mistake and not a mistake of Date.
Another question Date is considering 31 days of month the guy was doing some subtraction was a number of lines of code and he seemed to not put the error down to Date in the end.
But this example that I have seems to be a bit different and more clear cut. It involves Date.parse and can be demonstrated with one/two lines of code.
Date.parse is aware that there are not 32 days in a month, that's good
Date.parse("2000-01-32");
NaN
But In February it thinks there can be 30 or 31 days
Date.parse("2013-02-30");
1362182400000
Date.parse("2013-02-31");
1362268800000
In fact it looks like it thinks all months have 31 days. That is really strange for a method that is meant to parse a date.
And there's no issue of zero indexing here. As Date.parse("...") doesn't use zero indexing (And even if it did, it wouldn't cause it tot make the error of thinking there are 31 days in February - that is more than one off!
Date.parse("01-00-2000");
NaN
Date.parse("00-01-2000");
NaN
According to the specification for Date.parse() (emphasis mine):
The function first attempts to parse the format of the String according to the rules called out in Date Time String Format. […] Unrecognisable Strings or dates containing illegal element values in the format String shall cause Date.parse to return NaN.
And according to the specification for Date Time String Format (emphasis mine):
ECMAScript defines a string interchange format for date-times based upon a simplification of the ISO 8601 Extended Format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ
Where the fields are as follows: […] DDis the day of the month from 01 to 31.
Therefore, any date with a day of month greater than 31 is illegal and Date.parse() returns NaN.
Please notice that the standard defines a date format, not a date: the static method isn't required to make additional verifications, and everything else is implementation-specific. For instance, Date.parse('2013-02-30') and Date.parse('2013-04-31') both return NaN on Firefox.
The implementation differs between browsers. IE, Edge and Chrome will parse strings that doesn't represent actual dates, but Firefox will return NaN for those strings. The safe thing to do is to consider the result from Date.parse as undefined for date strings where the day falls outside the range of the month.
Browsers that allow parsing of non-existent dates will return a different date. Parsing "2015-04-31" will return the date 2015-05-01. This is the same behaviour as when using new Date(2015, 3, 31), where numbers out of range is allowed and will wrap around into a different month or year. That means that the result is still usable, if you don't mind that some invalid dates turn into other dates in some browsers.
The standard isn't very clear about what values are valid:
Illegal values (out-of-bounds as well as syntax errors) in a format
string means that the format string is not a valid instance of this
format.
The day component is defined as having a range from 1 to 31:
DD is the day of the month from 01 to 31.
However, the format is based on ISO 8601, and that is not a format for parsing strings into dates, that is a format for representing dates as string. Clearly you can't represent a date that doesn't even exist as a string.
Right, so how to check if a date string has a valid value?
with moment is very easy:
export function dateStringIsValid(aDateString){
return (moment(aDateString, "DD/MM/YYYY", true).isValid())
}
Related
This question already has answers here:
Detecting an "invalid date" Date instance in JavaScript
(52 answers)
Closed 3 years ago.
When I pass "test 2" string in new Date(), I am getting an actual date, how?
I am trying to whether it is a date or not.
console.log(new Date("test 2"));
Passing a string to new Date is the same as using Date.parse.
When a non-standard date string is passed, the result is implementation-dependent; the browser can do whatever it wants, including guessing. On Chrome, your input results in a date, but not on Firefox (NaN is returned).
test isn't part of a date string, so it looks like Chrome just parses the 2:
console.log(new Date('2'));
console.log(new Date('1'));
console.log(new Date('0'));
Essentially, this is undefined behavior, so strange results aren't surprising. Unless the passed string conforms to the format defined in the specification - that is, something like "2011-10-10" or "2011-10-10T14:48:00" or "2011-10-10T14:48:00.000+09:00", the results are unpredictable.
Consider instead figuring out what sort of string format you'd be expecting as an input, and then checking if the format is followed with a regular expression. If so, pass to new Date and see if it gives you a meaningful results; otherwise, don't.
As per MDN:
The ECMAScript specification states: If the String does not conform to the standard format the function may fall back to any implementation–specific heuristics or implementation–specific parsing algorithm.
Google Chrome tries to parse your date and picks up the number as a month. Other browsers may reject the passed value or return something totally different.
Hence please consider the following note:
Parsing of date strings with the Date constructor (and Date.parse(), which works the same way) is strongly discouraged due to browser differences and inconsistencies.
MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Parameters
From mdn:
A string representing a simplification of the ISO 8601 calendar date extended format (other formats may be used, but results are implementation-dependent).
You're encountering an implementation-specific attempt to parse your string as a date. It is not recommended to depend on this behaviour.
Because your test text includes a number. See, it's interpreting a number like 1 as January, 2 as February,3 as March, and so on.
new Date( "3")
will give you
Thu Mar 01 2001 00:00:00 GMT+0530 (India Standard Time)
If you use text with no number, e.g. new Date("test") it will throw invalid date.
Trying to understand how moment.js is converting a string to date, I've bounced into this issue.
let date = "User has logged in to more than 10 .";
console.log(moment(date)); //output date
let invalid = "User has logged in to more than 10 a";
console.log(moment(invalid)); //output invalid date
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.js
"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-range/2.2.0/moment-range.js"></script>
can someone explain it to me ??
CodePen link
When you pass the string moment checks whether it is a valid date format, and if not, it falls back to the built-in javascript Date.parse() method.
The moment.js docs say:
When creating a moment from a string, we first check if the string
matches known ISO 8601 formats, we then check if the string matches
the RFC 2822 Date time format before dropping to the fall back of new
Date(string) if a known format is not found.
Date.parse does not recognize anything useful in your string until it encounters 10; it drops the rest. A default date format is assumed, which will depend on your location and language. In my own case, here in the US, the format is MM/DD. The result is that the string is parsed to a date of Oct. 1st (10th month, no day specified defaults to the 1st). And then (for Y2K-ish reasons, I suspect) it assumes a year of 2001, since no year is given.
We get the same behavior from javascript's built-in Date methods:
new Date(Date.parse('User has logged in to more than 10.'))
// Mon Oct 01 2001 00:00:00 GMT-0400 (EDT) <- As printed from Michigan.
In your second case, you tried ending the string with 10 a instead of 10 . and you will notice the same behavior (invalid date) if you pass the same to the built-in Date methods.
This question already has answers here:
JavaScript's getDate returns wrong date
(12 answers)
Closed 6 years ago.
This is so basic, but it makes no sense to me:
new Date("2010-01-01").getFullYear();
result: 2009
wth? My goal is to reformat the date as mm/dd/yyyy given the format yyyy-mm-dd..
Adding on:
new Date("2010-01-01").getMonth();
result: 11
new Date("2010-01-01").getDate();
result: 31
The date string you're passing into new Date() has no timezone in it. It's being interpreted as UTC. The critical thing to understand here is that a Date is stored as a Unix timestamp (seconds since 1970-01-01 00:00, making 'Date' a misleading name) so if you don't specify the time within the date, it's going to apply a default.
Date.prototype.getFullYear() retrieves the full year for that timestamp in your LOCAL time. (See the docs here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getFullYear)
You're somewhere west of UTC, and 2010-01-01 UTC is 2009-12-31 in your local time.
And for your final mystery....getMonth() is 0-based, not 1-based, so '11' is December.
Don't use the Date constructor to parse strings, it's largely implementation dependent and inconsistent.
An ISO 8601 date and time without a timezone like "2016-02-29T12:00:00" should be treated as local (i.e. use the host system timezone offset to create a Date), but a date–only string is treated like "2016-02-29" as UTC. The first behaviour is consistent with ISO 8601, but the second isn't.
Some versions of browsers will treat date–only strings as UTC, and some as invalid dates, so always parse strings manually (a two line function or library can help). That way you know how it will be parsed in all hosts.
Provide Time with date in the new Date() as parameter . Then u will get exact Result.
If you take the following:
var s = "2/8888/2016";
var d = new Date(s);
alert(d);
In Chrome, you'll get:
Invalid Date
But in IE and Firefox, you'll get:
Fri Jun 01 2040 00:00:00 GMT-0500 (Central Daylight Time)
It appears to be just adding 8888 days to Feb 01. Instead, I would expect the date to be considered invalid. Is there a way I can make FireFox and IE think this date string is invalid?
Short answer:
It's a misbehaviuor of the browsers you're mentioning.
You have to check date is in correct format on your own. But it's quite trivial, I suggest this approach:
Split the date in year y, month m, day d and create the Date object:
var date = new Date( y, m - 1, d ); // note that month is 0 based
Then compare the original values with the logical values obtained using the Date methods:
var isValid = date.getDate() == d &&
date.getMonth() == m-1 &&
date.getFullYear() == y;
Before doing all of this you may want to check if the date string is valid for any browser:
Detecting an "invalid date" Date instance in JavaScript
Long answer:
Firefox (and IE) accepting "2/8888/2016" as a correct string sate format seem to be a bug / misbehaviour.
In fact according to ECMAScript 2015 Language Specification when Date() is invoked with a single string argument should behave just as Date.parse()
http://www.ecma-international.org/ecma-262/6.0/#sec-date-value
The latter
attempts to parse the format of the String according to the rules (including extended years) called out in Date Time String Format (20.3.1.16)
..that is specified here
http://www.ecma-international.org/ecma-262/6.0/#sec-date-time-string-format
where you can read
The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ
[...]
MM is the month of the year from 01 (January) to 12 (December).
DD is the day of the month from 01 to 31.
It seems that Firefox is interpreting the string value as when Date() is invoked with multiple arguments.
From
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
Note: Where Date is called as a constructor with more than one argument, if values are greater than their logical range (e.g. 13 is provided as the month value or 70 for the minute value), the adjacent value will be adjusted. E.g. new Date(2013, 13, 1) is equivalent to new Date(2014, 1, 1), both create a date for 2014-02-01 (note that the month is 0-based). Similarly for other values: new Date(2013, 2, 1, 0, 70) is equivalent to new Date(2013, 2, 1, 1, 10) which both create a date for 2013-03-01T01:10:00.
This may explain how "2/8888/2016" turns into 2040-05-31T22:00:00.000Z
There's no way to make IE and FF think it's invalid, except:
you could change their javascript implementations
you use a library instead to deal with that.
We can also expect that Javascript, as a language, evolves and we can cross our fingers that browsers decide to follow a more strict specification. The problem of course is that every "fix" must be also backward compatible with previous versions of the language (does not happen always, Perl for example).
So the best thing by now is to use some library just like momentjs as suggested by Derek in the post comments.
You have stumbled across yet another reason why you should manually parse date strings.
When Date is provided a single string argument, it is treated as a date string and parsed according to the rules in Date.parse. The rules there first attempt to parse it as an ISO 8601 format string. If that doesn't work, it may fall back to any parsing algorithm it wants.
In the case of "2/8888/2016", browsers will first attempt to parse it as an ISO format and fail. It seems from experimentation that IE and Firefox determine that the string is in month/day/year format and effectively call the Date constructor with:
new Date(2016,1,8888);
However, other browsers may attempt to validate the values and decide that 8888 is not a valid date or month, so return an invalid date. Both responses are compliant with ECMA-262.
The best advice is to always manually parse date strings (a library can help, but generally isn't necessary as a bespoke parse function with validation is 3 lines of code) then you can be certain of consistent results in any browser or host environment.
A leading zero for the day within a string seems to break the Javascript Date object in Chrome. There are also some inconsistencies between browsers, since Firefox handles the leading zero correctly, but fails when the zero is not included. See this example: https://jsfiddle.net/3m6ovh1f/3/
Date('2015-11-01'); // works in Firefox, not in Chrome
Date('2015-11-1'); // works in Chrome, not in Firefox
Why? Is there a good way to work around/with the leading zero?
Please note, the strings are coming from MySQL via AJAX and all dates will contain the leading zero, and I can fix this by formating the dates server-side. What format would work the best?
EDIT
Just to specify what my problem was, it looks like Chrome is applying a time zone to the YYYY-MM-DD format, which reverts the Nov. 1st date back to the Oct. 31st date (because of my EDT local time).
According to ECMA-262 (5.1):
The function first attempts to parse the format of the String according to the rules called out in Date Time String Format (15.9.1.15). If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
The date/time string format as described in 15.9.1.15 is YYYY-MM-DDTHH:mm:ss.sssZ. It can also be a shorter representation of this format, like YYYY-MM-DD.
2015-11-1 is not a valid date/time string for Javascript (note it's YYYY-MM-D and not YYYY-MM-DD). Thus, the implementation (browser) is able to do whatever it wants with that string. It can attempt to parse the string in a different format, or it can simply say that the string is an invalid date. Chrome chooses the former (see DateParser::Parse) and attempts to parse it as a "legacy" date. Firefox seems to choose the latter, and refuses to parse it.
Now, your claim that new Date('2015-11-01') doesn't work in Chrome is incorrect. As the string conforms to the date/time string format, Chrome must parse it to be specification compliant. In fact, I just tried it myself -- it works in Chrome.
So, what are your options here?
Use the correct date/time format (i.e. YYYY-MM-DD or some extension of it).
Use the new Date (year, month, date) constructor, i.e. new Date(2015, 10, 1) (months go from 0-11) in this case.
Whichever option is up to you, but there is a date/time string format that all specification compliant browsers should agree on.
As an alternative, why not use unix timestamps instead? In JavaScript, you would would multiply the timestamp value by 1000,
e.g
var _t = { time: 1446220558 };
var _d = new Date( _t.time*1000 );
Test in your browser console:
new Date( 14462205581000 );
// prints Fri Oct 30 2015 11:55:58 GMT-0400 (EDT)
There's a little benefit in it as well (if data comes via JS) - you'd save 2 bytes on every date element '2015-10-30' VS 1446220558 :)