new Date() using Javascript in Safari - javascript

I'm having an issue using the new Date() function in Javascript. Safari is giving me an "Invalid Date" message.
I've created a short example at jsbin.
This appears to work on all other browsers, but not Safari. Any ideas on how I can take the value from an input (such as 2011-01-03) and turn it into a date object, while having it work properly in Safari?
Many thanks!

The date parsing behavior on JavaScript is implementation-dependent, the ISO8601 format was recently added to the ECMAScript 5th Edition Specification, but this is not yet supported by all implementations.
I would recommend you to parse it manually, for example:
function parseDate(input) {
var parts = input.match(/(\d+)/g);
return new Date(parts[0], parts[1]-1, parts[2]);
}
parseDate('2011-01-03'); // Mon Jan 03 2011 00:00:00
Basically the above function matches each date part and uses the Date constructor, to build a date object, note that the months argument needs to be 0-based (0=Jan, 1=Feb,...11=Dec).

The easy solution I tried
Download date.js from http://datejs.com/
Include in your file
then
var date = Date.parse('1970-01-12 00:00:00');
var formattedDate = date.toString('yyyy-MM-dd');

While #CMS's solution is probably superior, I found that using Date.parse('2011-01-13') is also a quick, working solution.

csnover has some progressive ISO 8601 Date enhancement code available on GitHub:
https://github.com/csnover/js-iso8601/blob/master/iso8601.js
Including his code should provide a temporary fix while the Safari team work toward a more complete ES5 implementation.

Related

How to convert date string with specific locale to ISO format

I want to convert a date time string with a specific locale (locale defined in IANA format) to a Date object and print UTC time in ISO 8601 format. This code below works perfectly.
<script src="https://momentjs.com/downloads/moment.min.js"></script>
<script src="https://momentjs.com/downloads/moment-timezone-with-data.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', ()=>{
console.log(moment('2021-01-01T00:00:00').tz('America/New_York').toISOString());
});
</script>
The result is my console log shows 2021-01-01T05:00:00.000Z.
However, I want to achieve the same result without using any 3rd party libraries. I only want to use the browser's Javascript default objects/classes/apis etc... So I tried all of these but they all gave errors:
new Date("2021-01-01T00:00:00 America/New_York").toISOString();
(new Date("2021-01-01T00:00:00")).setLocale('America/New_York').toISOString();
(new Date("January 1, 2021 12:00:00 AM America/New_York").toISOString();
What is the correct way in javascript to convert a date string with a locale to ISO 8601 format without the use of javascript libraries like moment, luxon, etc...?
I need it to work in any Desktop version of Chrome, FireFox, Edge and Safari released after January 1, 2021 (support not needed for older versions).
Taking help from this StackOverflow answer, this can be done as follows. You were almost there. toLocaleString() is the method that you want. As the name suggests, it returns a string so it needs to be converted back into a date object. I think the bulkiness of doing this is why a lot of people prefer using a library like moment.
new Date(
new Date("2021-01-01T00:00:00")
.toLocaleString("en-US", {timeZone: "America/New_York"})
).toISOString();
You can use the vanilla javascript class called Intl.
const newDate = new Date();
console.log(new Intl.DateTimeFormat('en-us', { dateStyle: 'short', timeStyle: 'medium' }).format(newDate))
;

When converting date, I get previous day

I want to convert a string into a date "as it is".
const date = "8/16/2019"
console.log(new Date(date))
However, I get:
As you can see I get the prevous day. I was thinking that it might be a timezone issue, even though there is no timezone that I am converting it from.
Any suggestions how to convert is as it is?
I appreciate you replies!
If your format is consistent, you could split on / and use Date.UTC. Creating your new Date from that would ensure it's UTC.
const date = "8/16/2019"
const [month,day,year] = date.split("/");
const utcDate = Date.UTC(year,month-1,day);
console.log(new Date(utcDate));
const date = "8/16/2019"
console.log(new Date(date).toLocaleString("en-US", {timeZone: "Asia/kolkata"}))
Note:- You need to add timezone
You can use toLocaleDateString
console.log(new Date("8/16/2019").toLocaleDateString('en-us', {timeZone: "Asia/Kolkata"}))
new Date("8/16/2019") will create a date object using your current timezone. Add a "Z" at the end if you want your date to be in UTC.
console.log(new Date("8/16/2019Z"))
EDIT
It appears that Firefox is not implementing the parsing of standard date format. Unfortunately until recently how exactly was a date parsed was completeley based on heuristics and intrinsically non portable.
Looking at Firefox bug tracker seems the issue has been discussed but the problem is still present (some toolkit just works around by replacing "Z" with "+00:00" before calling the parser).
The only way to be sure on every browser is to parse the string yourself and build the date from the fields. I didn't notice because I'm using chrome instead (in both chrome and Node works as expected).
EDIT 2
After more investigation seems the standard requires that:
If you use yyyy-mm-ddThh:mm:ssz then you get what ISO format for datetime defines it to be. Also the syntax described in the standard is not very precise and for example is not clear to me if the time zone can be present when no time is present (Chrome says yes, Firefox says no).
If you use another format then anything goes (so for example there is no string that is guaranteed to issue an invalid date response).
In other words new Date("8/16/2019") is not portable Javascript (with the meaning that you don't know what date / time / timezone you will get, if any). Either you parse yourself the date or you just live with what that version of that Javascript engine in that moment decides to give you.

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

What is needed for a valid Javascript Date object?

I've been banging my head over this one all day. No matter how I initialize a Javascript Date I cannot seem to get a valid Date object... I'm assuming the Date is invalid and not working properly by inspecting it with Chrome's debugger, which has the value '__proto__: Invalid Date'.
I've tried all of the following:
var d = new Date();
var d = new Date('2012-10-08');
var d = new Date('2012-10-08 00:00:00');
var d = new Date(Date('2012-10-08'));
var d = new Date(Date.parse('2012-10-08'));
var d = new Date(2012,10,08);
var d = new Date("October 13, 1975 11:13:00");
Along with countless other attempts.
This is presenting a problem in iOS where I'm trying to get values from these Date objects but every function just returns NaN. I'd prefer to avoid having to use external libraries or have to convert YYYY-MM-DD format into any other format since I'm trying to get this to work with an HTML5 input type="date" with minimal code for a mobile site.
Essentially this boils down to: What are the parameters that make a Date object valid?!
Do not trust the Date object to parse strings, you must do it manually. Given the format 2012-10-08,
function stringToDate(s) {
s = s.match(/\d+/g);
if (s) {
return new Date(s[0], --s[1], s[2]);
}
}
You may want to do some validation of the input string and the resulting date object, the above just shows the conversion.
Edit
BTW, the only string format that seems to be parsed consistently in all browsers is the US-specific month/date/year format. There is no specification to support its use, nor is there any reason to believe browsers will continue to support it other than pragmatism and "legacy" reasons.
For the vast majority of regions, '2/3/2012' is interpreted as 2 March, so getting 3 February might be unexpected.
Once older versions of IE are no longer in use (probably a few years yet), it should be safe to use the ISO8601 extended format per ECMA-262. However, even browsers that support it are inconsitent. e.g given:
new Date('2011-02-29');
Firefox 15 returns 'Invalid Date', IE 9 and Chrome 22 return a date object for 1 March, 2011.
There are three ways of calling the method:
The number of milliseconds from the epoch:
new Date(milliseconds)
Any IETF-compliant RFC 2822 timestamp:
new Date("November 2, 1988 10:00:00");
Individual args:
new Date(year, month, day [, hour, minute, second, millisecond])
new Date(1988,11,02,10,0,0);
If your main concern is about parsing, have a look at Moment.js which is clearly superior to parsing things yourself. (IMHO)
Turns out jQuery doesn't bind the .change() event to input type="date" properly in iOS. Changed the event to .blur() and everything seems to work now. However, it still seems it is impossible to create a valid date object in Chrome... an issue for another day.
Thanks for the help everyone!

What is up with javascript Date function for only years?

(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.

Categories

Resources