Javascript 日期在 iOS 上无效

IT技术 javascript android ios cordova
2021-03-13 14:17:20

我正在开发基于 Phonegap 的 iOS 应用程序,该应用程序已经针对 Android 完成。以下几行适用于 Android,但不适用于 iOS。为什么?

var d = new Date("2015-12-31 00:00:00");
console.log(d.getDate() + '. ' + d.getMonth() + ' ' + d.getFullYear();

安卓的结果:

31.11 2015

iOS上的结果:

NaN. NaN NaN

差异从何而来?

6个回答

您的日期字符串不是指定使用的格式new Date规范中唯一的格式是在 ES5 (2009) 中添加的 ISO-8601 的简化版本你的字符串不是那种格式,但它真的很接近。将其更改为规范中未包含但普遍支持的格式也很容易

四种选择:

  • 使用即将推出的Temporal功能(现在处于第 3 阶段)
  • 指定格式
  • 一种几乎得到普遍支持的未指定格式
  • 自己解析

使用即将推出的Temporal功能

截至 2021 年 8 月的此更新,Temporal提案处于第 3 阶段。您可以使用它来解析您的字符串,将其视为 UTC 或本地时间:

将字符串视为 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>

或将其视为当地时间:

// (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 没有指定的日期/时间字符串格式历史上在没有指定时区时存在的下面提到的问题。

指定格式

如果您将空格更改为 a T,您将符合规范:

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());

(我假设您实际上没有使用字符串文字,因此replace调用。)

为了在旧浏览器中可靠地处理时区,您还需要附加一个Z(对于 GMT/UTC)或时区指示符 ( +/ - HH:MM),因为在 ES5 中错误地指定了没有它们的字符串的处理,在 ES2015 中更新,然后在 ES2016 中进一步更新。当前版本的现代浏览器现在遵循规范,其中说:

  • 如果字符串上有时间但没有时区指示符,则以本地时间解析字符串
  • 如果字符串上没有时间且没有时区指示符,则以 UTC 解析字符串

ES5 表示始终默认为UTC。ES2015 表示始终默认为本地时间。ES2016是定义当前行为的地方。此后一直稳定。)

所以最好包含一个时区指示器,特别是如果你必须支持旧浏览器。请注意,它必须是Z(UTC) 或+/ - HH:MMCST不允许使用缩写,因为它们没有标准。这是一个 UTC 示例:

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());

一种几乎得到普遍支持的未指定格式

有第二种格式不在规范中,但几乎得到普遍支持并且已经存在很长时间了:YYYY/MM/DD HH:MM:SS,它被解释为本地时间。所以:

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());

不过,这又是一种未指明的行为,所以请注意空客。但它至少适用于 IE8+(可能更早)、Chrome 和其他任何使用 V8 JavaScript 引擎、Firefox 和 Safari 的产品。

自己解析

自己解析该字符串也很容易。使用 ES2020+ 特性:

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());

或者只有 ES5 级别的功能(因为问题来自 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());

你拯救了我的一天,非常感谢
2021-04-21 14:17:20
@PhilippSiegfried:太好了,很高兴有帮助!
2021-04-28 14:17:20
@NULL:我从未见过不支持它的 JavaScript 引擎。IE6 及更高版本,我见过的每个版本的 Firefox,每个版本的 Chrome,每个版本的 Opera。但是,必须始终在目标环境上进行测试(遗憾的是,无论某些东西是否“标准”)。
2021-05-01 14:17:20
在我自己的测试中,我发现,YYYY/MM/DD HH:MM:SS解决方案提供了iOS上的同一天我希望从获得YYYY-MM-DD HH:MM:SS,而该解决方案以替代空间T调整好自己的日期由时区数量。(增加10个小时的时间)。(总结: YYYY/MM/DD HH:MM:SS 有效YYYY-MM-DDTHH:MM:SS根据时区和某些会出错的情况进行调整)
2021-05-13 14:17:20
@user1290746:可能是Z规范中错误,我在上面添加了一个注释。
2021-05-14 14:17:20

我不能告诉你为什么。也许是因为 iOS 不像 Android 那样支持 Javascript 日期功能,或者支持不同的格式?

但我可以给你一个解决方法:

var s = "2015-12-31 00:00:00".split(" ")[0].split("-"),
    d = new Date( s[0], s[1], s[2], 0, 0, 0 );

console.log(d);

var s = "2015-12-31 00:00:00".replace(/[ :]/g, "-").split("-"),
    d = new Date( s[0], s[1], s[2], s[3], s[4], s[5] );

console.log(d);
我会试试看然后回复你
2021-04-22 14:17:20
这是一个有效的解决方案。适用于 iOS 和其他平台。但是,出于某种原因,如果您有一个包含 2019 年的日期 2 1 出于某种原因,该日期会倒退一个月。 var s = d.replace(/[ :]/g, "-").split("-") console.log(new Date(s[0], s[1] - 1, s[2]));
2021-05-04 14:17:20

一种适用于 IOS 和 Android 的解决方案,并在不需要时避免字符串操作

let fullDate = "1991-03-29 00:00:00";
let date = new Date(fullDate);

// In case its IOS, parse the fulldate parts and re-create the date object.
if(Number.isNaN(date.getMonth())) {
  let arr = fullDate.split(/[- :]/);
  date = new Date(arr[0], arr[1]-1, arr[2], arr[3], arr[4], arr[5]);
}

如果有人还在寻找它。

适用于 IE、Safari、IOS-FF、IOS-Safari 等。

getIOSSaveDateObj = function(dateString){
    if(dateString.indexOf('-') > 0){
        var arr = dateString.split(/[- :]/);
        var date = new Date(arr[0], arr[1]-1, arr[2], arr[3], arr[4], arr[5]);
    }else{
        var arr = dateString.split(/[. :]/);
        var date = new Date(arr[2], arr[1]-1, arr[0], arr[3], arr[4], arr[5]);
    }
    return date;
}

我很难解决这个问题,我终于找到了一个更简单的解决方案并使用了moment.js您只需声明日期并在每个设备中工作即可,无需做太多事情。