I'm working on a Phonegap-based iOS app, which is already done for Android. The following lines are working fine for Android but not for iOS. Why?
var d = new Date("2015-12-31 00:00:00");
console.log(d.getDate() + '. ' + d.getMonth() + ' ' + d.getFullYear();
Result for Android:
31.11 2015
Result on iOS:
NaN. NaN NaN
Where is the difference coming from?
Your date string is not in a format specified to work with new Date
. The only formats in the spec are a simplified version of ISO-8601 (added in ES5 in 2009 and updated in ES2015 and ES2016), and the format output by Date.prototype.toString
. Your string isn't in either format, but it's really close to the ISO-8601-like format. It would also be easy to change it to a format that isn't in the spec, but is universally supported
Four options for you:
Temporal
feature (it's now at Stage 3)Temporal
featureThe Temporal
proposal is at Stage 3 as of this update in August 2021. You can use it to parse your string, either treating it as UTC or as local time:
Treating the string as UTC:
// (Getting the polyfill)
const {Temporal} = temporal;
const dateString = "2015-12-31 00:00:00";
const instant = Temporal.Instant.from(dateString.replace(" ", "T") + "Z");
// Either use the Temporal.Instant directly:
console.log(instant.toLocaleString());
// ...or get a Date object:
const dt = new Date(instant.epochMilliseconds);
console.log(dt.toString());
<script src="https://unpkg.com/@js-temporal/polyfill/dist/index.umd.js"></script>
Or treating it as local time:
// (Getting the polyfill)
const {Temporal} = temporal;
const dateString = "2015-12-31 00:00:00";
console.log("Parsing as local time:");
const tz = Temporal.Now.timeZone();
const instant = tz.getInstantFor(dateString.replace(" ", "T"));
console.log(instant.toLocaleString());
const dt = new Date(instant.epochMilliseconds);
console.log(dt.toString());
<script src="https://unpkg.com/@js-temporal/polyfill/dist/index.umd.js"></script>
Temporal
doesn't have the problem mentioned below that the specified date/time string format historically had when no timezone was specified.
If you change the space to a T
, you'll be in spec:
var dateString = "2015-12-31 00:00:00";
// Treats the string as local time -- BUT READ BELOW, this varied
var d = new Date(dateString.replace(" ", "T"));
console.log(d.toString());
(I'm assuming you're not actually using a string literal, hence the replace
call.)
For reliable timezone handling in old browsers, you'll also want to append a Z
(for GMT/UTC) or a timezone indicator (+
/-
HH:MM
), because the handling of strings without them was mis-specified in ES5, updated in ES2015, and then updated further in ES2016. Current versions of modern browsers follow the spec now, which says:
(ES5 said always default to UTC. ES2015 said always default to local time. ES2016 is where the current behavior was defined. It's been stable since.)
So it's best to include a timezone indicator, especially if you have to support older browsers. Note that it must be a Z
(UTC) or +
/-
HH:MM
; abbreviations like CST
are not allowed, as there's no standard for them. Here's a UTC example:
var dateString = "2015-12-31 00:00:00";
// Treat the string as UTC
var d = new Date(dateString.replace(" ", "T") + "Z");
console.log(d.toString());
There's a seconnd format that isn't in the specification but is near-universally supported and has been for a long time: YYYY/MM/DD HH:MM:SS
, which is interpreted as local time. So:
var dateString = "2015-12-31 00:00:00";
// Treats the string as local time
var d = new Date(dateString.replace(/-/g, "/"));
console.log(d.toString());
Again, though, that's unspecified behavior, so caveat emptor. But it works in at least IE8+ (probably earlier), Chrome and anything else using the V8 JavaScript engine, Firefox, and Safari.
It's also easy to parse that string yourself. Using ES2020+ features:
function parseDate(str) {
const [dateparts, timeparts] = str.split(" ");
const [year, month, day] = dateparts.split("-");
const [hours = 0, minutes = 0, seconds = 0] = timeparts?.split(":") ?? [];
// Treats the string as UTC, but you can remove the `Date.UTC` part and use
// `new Date` directly to treat the string as local time
return new Date(Date.UTC(+year, +month - 1, +day, +hours, +minutes, +seconds));
}
const dateString = "2015-12-31 00:00:00";
const d = parseDate(dateString);
console.log(d.toString());
Or with only ES5-level features (since the question is from 2015):
function parseDate(str) {
var parts = str.split(" ");
var dateparts = parts[0].split("-");
var timeparts = (parts[1] || "").split(":");
var year = +dateparts[0];
var month = +dateparts[1];
var day = +dateparts[2];
var hours = timeparts[0] ? +timeparts[0] : 0;
var minutes = timeparts[1] ? +timeparts[1] : 0;
var seconds = timeparts[2] ? +timeparts[2] : 0;
// Treats the string as UTC, but you can remove the `Date.UTC` part and use
// `new Date` directly to treat the string as local time
return new Date(Date.UTC(year, month - 1, day, hours, minutes, seconds));
}
var dateString = "2015-12-31 00:00:00";
var d = parseDate(dateString);
console.log(d.toString());