What is up with javascript Date function for only years? - javascript

(EDIT: I am using chrome console for the code below)
I realize that javascript Date() is deprecated, but it's useful for something I'm currently working on; however there is a strange problem:
var x = new Date('999').getFullYear()
returns 999
while
var x = new Date('1000').getFullYear()
returns 999 as well, but
var x = new Date('10000').getFullYear()
returns 10000...
Does anyone know why do 4 digit numbers give the wrong .getFullYear()?

As the other answers suggest, you can't rely on Date.parse across browsers, especially if you are dealing with dates before 1000 CE.
As far as Chrome is concerned, it looks like the issue is how the browser deals with timezones. The following example suggests that before 1000 CE, Chrome parses the date in your local timezone on top of it; >= 1000 CE, it appears to first parse the date in UTC, then applies the timezone conversion:
> new Date('1000')
"Tue Dec 31 999 16:00:00 GMT-0800 (PST)"
> new Date('999')
"Tue Jan 01 999 00:00:00 GMT-0800 (PST)"
I'd be inclined to see this as a bug, but perhaps the Chromium team thinks it's a feature :).
The bottom line is that, if you want accurate date parsing for years, especially ancient years, you need to do some work yourself and/or use a library. Moment.js and Datejs both might help, but I doubt either deals well with ancient years. Moment.js seems to have the same issue (in Chrome):
> moment('999').year()
999
> moment('1000').year()
999
The best way I know of to accurately set dates based on ancient years is generally to a) always use UTC, and b) set the date manually:
var d = new Date();
d.setUTCFullYear('999');
d.getUTCFullYear(); // 999
d.setUTCFullYear('1000');
d.getUTCFullYear(); // 1000
In Chrome at least, this works with both strings and integers.
You might also be interested in the gregorian parser in the Timemap.js library, which handles ancient years with AD, CE, BC, and BCE extensions, as well as negative numbers.

Take a look at this page. You are passing a string which is passed to this parse method. The first argument isn't "years" unless you pass 3 integers. Finally while the class itself isn't being deprecated, some of it's members are (including getYear() which is being replaced with getFullYear() which you are using`)

This fiddle says otherwise on Firefox. Perhaps it's browser related?

alert(new Date('999').getFullYear()); yields NaN on Firefox 15.0.1
Hmm.. Chrome 21.0.1180.57 does yield '999'

var x = new Date('10000').getFullYear()
change it to
var x = new Date(10000).getFullYear()
you are sending an invalid datestring to the date constructor.

Related

Javascript: how to convert a UTC date to local one?

Looks like a very simple thing to do? Not afrer reading this http://dygraphs.com/date-formats.html - what a mess!
var time_utc="2016-04-25 20:19:00.306671";
document.write("Local date:"+new Date(time_utc+" UTC")); // Firefox 44.0.2: Invalid Date
How do I print a date in above format adjusted to local time?
The article you provided mentions halfway through the page,
Using hyphens (-) instead of slashes (/) works in WebKit browsers, but
not in IE or FF. Beware the UTC parse of YYYY-MM-DD!
As well as,
Don't try to specify milliseconds. Only recent versions of Chrome will
understand you.
With the date 2016-04-25 20:19:00.306671 you use hyphens and milliseconds. You could modify your time_utc string a bit to make it compatible like so,
var time_utc = "2016-04-25 20:19:00.306671";
time_utc = time_utc.replace(/-/g, "/");
time_utc = time_utc.split(".").shift();
var d = new Date(time_utc);
d.toString();
The above code outputs,
Mon Apr 25 2016 20:19:00 GMT+0200 (CEST)
Have you looked into Moment.js? http://momentjs.com/ It's a handy date-object wrapper that makes date object manipulation easy. Particularly, the local() function provided will give you what you need here.
All you have to do is install moment from npm and then include it in your js file at the top like this:
var moment = require("moment");
Then to change your time_utc variable to local all you have to do is:
var time_utc="2016-04-25 20:19:00.307";
document.write("Local date:"+moment(time_utc).local());
As someone advised me before, it is not wise to include an entire library for a simple, one time function. As a disclaimer, my work requires me to do many date-time calculations and conversions throughout a large project, so including a library for ease is much preferred in my case. However, if you only have to use it this one time, my answer may not be the best.
If you use moment.js, you can use:
var time_utc = "2016-04-25 20:19:00.306671";
var localDate = moment.utc(time_utc).local();
You need append UTC to the string before converting it to a local date in js:
var date = new Date('25/04/2016 4:52:48 PM UTC');
date.toString() // "Mon Apr 25 2016 09:52:48 GMT-0700 (PDT)"

Why is 2/8888/2016 a valid date in IE and Firefox?

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.

is there any workaround for broken v8 date parser?

V8 Date parser is broken:
> new Date('asd qw 101')
Sat Jan 01 101 00:00:00 GMT+0100 (CET)
I can use fragile regular expression like this:
\d{1,2} (jan|feb|mar|may|jun|jul|aug|sep|oct|nov|dec) \d{1,4}
but it is too fragile. I cannot rely on new Date (issue in V8) and also moment cant help me because moment is getting rid off date detection (github issue-thread).
is there any workaround for broken v8 date parser?
To be clear. We have Gecko and V8, both have Date. V8 has broken Date, Gecko has working one. I need the Date from in Gecko (Firefox).
Update: It’s definitely broken parser https://code.google.com/p/v8/issues/detail?id=2602
nope, Status: WorkingAsIntended
Date objects are based on a time value that is the number of milliseconds since 1 January, 1970 UTC and have the following constructors
new Date();
new Date(value);
new Date(dateString);
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);
From the docs,
dateString in new Date(dateString) is a string value representing a date. The string should be in a
format recognized by the Date.parse() method (IETF-compliant RFC 2822
timestamps and also a version of ISO8601).
Now looking at the v8 sourcecode in date.js:
function DateConstructor(year, month, date, hours, minutes, seconds, ms) {
if (!%_IsConstructCall()) {
// ECMA 262 - 15.9.2
return (new $Date()).toString();
}
// ECMA 262 - 15.9.3
var argc = %_ArgumentsLength();
var value;
if (argc == 0) {
value = %DateCurrentTime();
SET_UTC_DATE_VALUE(this, value);
} else if (argc == 1) {
if (IS_NUMBER(year)) {
value = year;
} else if (IS_STRING(year)) {
// Probe the Date cache. If we already have a time value for the
// given time, we re-use that instead of parsing the string again.
var cache = Date_cache;
if (cache.string === year) {
value = cache.time;
} else {
value = DateParse(year); <- DOES NOT RETURN NaN
if (!NUMBER_IS_NAN(value)) {
cache.time = value;
cache.string = year;
}
}
}
...
it looks like DateParse() does not return a NaN for for a string like 'asd qw 101' and hence the error. You can cross-check the same with Date.parse('asd qw 101') in both Chrome(v8) [which returns -58979943000000] and Gecko (Firefox) [which returns a NaN]. Sat Jan 01 101 00:00:00 comes when you seed new Date() with a timestamp of -58979943000000(in both browsers)
is there any workaround for broken v8 date parser?
I wouldnt say V8 date parser is broken. It just tries to satisfy a string against RFC 2822 standard in the best possible way but so does gecko and both break gives different results in certain cases.
Try new Date('Sun Ma 10 2015') in both Chrome(V8) and Firefox(Gecko) for another such anomaly.
Here chrome cannot decide weather 'Ma' stands for 'March' or 'May' and gives an Invalid Date while Firefox doesnt.
Workaround:
You can create your own wrapper around Date() to filter those strings that V8's own parser cannot. However, subclassing built-ins in ECMA-5 is not feasible. In ECMA-6, it will be possible to subclass built-in constructors (Array, Date, and Error) - reference
However you can use a more robust regular expression to validate strings against RFC 2822/ISO 8601
^(?:(?:31(\/|-|\. |\s)(?:0?[13578]|1[02]|(?:Jan|Mar|May|Jul|Aug|Oct|Dec)))\1|(?:(?:29|30)(\/|-|\.|\s)(?:0?[1,3-9]|1[0-2]|(?:Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.|\s)(?:0?2|(?:Feb))\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.|\s)(?:(?:0?[1-9]|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep))|(?:1[0-2]|(?:Oct|Nov|Dec)))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$
Image generated from debuggex
So, seems like v8 aint broken, it just works differently.
Hope it helps!
You seem to be asking for a way to parse a string that might be in any particular format and determine what data is represented. There are many reasons why this is a bad idea in general.
You say moment.js is "getting rid of date detection", but actually it never had this feature in the first place. People just made the assumption that it could do that, and in some cases it worked, and in many cases it didn't.
Here's an example that illustrates the problem.
var s = "01.02.03";
Is that a date? Maybe. Maybe not. It could be a section heading in a document. Even if we said it was a date, what date is it? It could be interpreted as any of the following:
January 2nd, 2003
January 2nd, 0003
February 1st, 2003
February 1st, 0003
February 3rd, 2001
February 3rd, 0001
The only way to disambiguate would be with knowledge of the current culture date settings. Javascript's Date object does just that - which means you will get a different value depending on the settings of the machine where the code is running. However, moment.js is about stability across all environments. Cultural settings are explicit, via moment's own locale functionality. Relying on the browser's culture settings leads to errors in interpretation.
The best thing to do is to be explicit about the format you are working with. Don't allow random garbage input. Expect your input in a particular format, and use a regex to validate that format ahead of time, rather then just trying to construct a Date and seeing if it's valid after the fact.
If you can't do that, you'll have to find additional context to help decide. For example, if you are scraping some random bits of the web from a back-end process and you want to extract a date from the text, you'd have to have some knowledge about the language and locale of each particular web page. You could guess, but you'd likely be wrong a fair amount of the time.
See also: Garbage in, garbage out
ES5 15.9.4.2 Date.parse: /.../ If the String does not conform to
that format the function may fall back to any implementation-specific
heuristics or implementation-specific date formats. Unrecognizable
Strings or dates containing illegal element values in the format
String shall cause Date.parse to return NaN.
So that's all right and according to the citation above result of v8 date parser:
new Date('asd qw 101') : Sat Jan 01 101 00:00:00 GMT+0100
(CET)
new Date('asd qw') : Invalid Date

Javascript: Difference between `new Date(dateString)` vs `new Date(year, month, day)`

Referencing to the accepted answer on this question How do I get the number of days between two dates in JavaScript?. I see, in the function parseDate:
function parseDate(str) {
var mdy = str.split('/')
return new Date(mdy[2], mdy[0]-1, mdy[1]);
}
He is doing this:
var mdy = str.split('/')
return new Date(mdy[2], mdy[0]-1, mdy[1]);
i.e. splitting the passed date into month, day and year and then passing it on to Date like new Date(year, month, day) while he could simply do new Date(str) and it would have returned the same result (Wouldn't it?). Can anyone please explain the difference between both the ways?
Update: Test results:
var str = '1/1/2000'
var mdy = str.split('/')
console.log( new Date(str) ) // Sat Jan 01 2000 00:00:00 GMT+0500 (Pakistan Standard Time)
console.log( new Date(mdy[2], mdy[0]-1, mdy[1]) ); // Sat Jan 01 2000 00:00:00 GMT+0500 (Pakistan Standard Time)
No, they're not the same (even assuming you'll subtract one month later: he's doing mdy[0] - 1) because new Date(str) is required (by standard, see §15.9.4.2) to accept only date in a specific format ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ, see also this post, I won't repeat myself here):
If the String does not conform to that format [ISO 8601] the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
Please note (as pointed out by Royi in comments) that also RFC 2822 should be supported (according to MDN) but it's not mentioned in JavaScript specifications and Internet Explorer doesn't officially support it (see MSDN, it can parse something similar but it's not the same).
In that code they're parsing using a specific locale rules (MM/DD/YYYY, I suppose en-US locale but it's not only one). To be honest I wouldn't even use that code for parsing (because yes, actually it'll be broken for a different locale: even separator used for splitting is not "locale safe"). Let me explain with an example:
You're using a proper configured date time picker (or <input type="date"/> when supported) you'll enter date according to your locale. For example in Italy (but in general in Europe) we write DD/MM/YYYY.
Now let's imagine that user picked 21 December 2014 (formatted as 21/12/2014 according to his locale).
With string splitting that code will fail (because it'll pick 21 as month number, obviously it's not valid). Even worse than that such errors may even go unnoticed (for example if user picks 1/2/2014 code will "think" it's 2nd Jan but user picked 1st Feb). Do you want to make it more complicate? Even new Date(str) may fail because it's browser dependent (and you can't really trust heuristic to be portable and safe).
If you're asking yourself "Then why they used such code?" I'd say that they used a quick workaround to support dates using en-US locale (probably because browser they used didn't support them with heuristic guess) but it's not something you should reuse.
Solution? Do not ever parse date by hand (unless you really and deep know what you're doing), use a good library (for example moment.js) for that because most assumption you may do about date formatting are...wrong.
I tried to enter your test code into jsperf.com, and the results on my machine are clear, and they say that you should not try to split the string.
I tried two tests for the test using a split string, and supprisingly, the split itself was not what was taking up the time.
Try for yourself at http://jsperf.com/date-from-string-or-dateparts

Regular expression to match Date

I am trying to validate following date format through regExp, but still i didn't get ant working solution-
ex.-
OCT-12-2011
FEB-06-1995
how can i do it using regexp.
Thanks in advance!!
Date JS is the first hit on Google.
Comprehensive, yet simple, stealthy and fast. Datejs has passed all trials and is ready to strike. Datejs doesn’t just parse strings, it slices them cleanly in two.
Sample code from their site:
Date.parse('today');
Date.parse('t + 5 d'); // today + 5 days
Date.parse('next thursday');
Date.parse('February 20th 1973');
Date.parse('Thu, 1 July 2004 22:30:00');
The only downside is that it modifies the prototype of build-in Date object. Although it's considered a discouraged practice I doubt that these particular additions will affect you application. There are still plenty of sites using Prototype without any problems.
EDIT: ragarding parsing Date values via RegExps.
Be very careful especially considering different date.toString() implementation in different browsers. Here's what new Date().toString() gave me:
IE9: "Mon Jul 11 14:50:45 UTC+0300 2011"
FF5: "Mon Jul 11 2011 14:51:08 GMT+0300 (FLE Daylight Time)"
If you get those strings from the server and you feel that adding a library just for dates is an overhead you'll be fine with regular expressions.
Don't try to validate it very strictly using a regex. Check the format, using something like this:
[A-Z]{3}-[0-9]{2}-[0-9]{4}
Then, check each part to see whether that part is valid.
... not sure how strictly you want to do this.
var months = [
'JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'
],
regValidate = RegExp('^(?:' + months.join('|') + ')-[0-3][0-9]-\\d{4}$');
alert(regValidate.test('OCT-12-2011'));
Depending on you requirements, the following could do:
^[A-Z]{3}-\d{2}-\d{4}$
Or more elaborate:
^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)-(0[1-9]|[12]\d|3[01])-[12]\d{3}$
Or anything in between :) (You may even elaborate further on the year part.)
If you want to do it with a single regexp, you could use something like:
/^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)-([012]\d|3[01])-[12]\d{3}$/
This will not match a date before JAN-01-1000 or after DEC-31-2999.
Note: do not forget to check for invalid february dates like FEB-30-2011

Categories

Resources