iOS上的Javascript日期是无效的

74 人关注

我正在开发一个基于Phonegap的iOS应用,这个应用已经在Android上完成。以下几行在安卓系统中工作正常,但在iOS系统中不正常。为什么?

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

安卓系统的结果。

31.11 2015

Result on iOS:

NaN. NaN NaN

Where is the difference coming from?

javascript
android
ios
cordova
Philipp Siegfried
Philipp Siegfried
发布于 2012-11-13
7 个回答
T.J. Crowder
T.J. Crowder
发布于 2016-09-13
已采纳
0 人赞同

你的日期字符串没有指定与 new Date 一起工作的格式。该规范中唯一的格式是 ISO-8601的一个简化版本 (2009年在ES5中加入,在ES2015和ES2016中更新),以及由 Date.prototype.toString .你的字符串没有采用这两种格式,但它确实接近于类似ISO-8601的格式。把它改成规范中没有的、但被普遍支持的格式也很容易

Four options for you:

  • Use the upcoming Temporal feature (it's now at Stage 3)
  • The specified format
  • An unspecified format that's near-universally supported
  • Parse it yourself
  • Use the upcoming Temporal feature

    The Temporal proposal 在2021年8月的这次更新中处于第三阶段。你可以用它来解析你的字符串,将其视为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>

    替换代码2】没有下面提到的问题,即历史上没有指定时区时,指定的日期/时间字符串格式。

    The ISO-8601-like format

    如果你把空间改成 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中进一步更新。当前版本的现代浏览器现在遵循的规范是:。

  • If there's a time on the string but and no timezone indicator, parse the string in local time
  • If there's no time on the string and no timezone indicator, parse the string in UTC
  • ( ES5说 始终默认为UTC。 ES2015说 始终默认为当地时间。 ES2016 是定义当前行为的地方。此后一直很稳定)。

    因此,最好包括一个时区指示器,特别是如果你必须支持旧的浏览器。注意,它必须是 Z (UTC)或 + / - 。 HH:MM ;像 CST 这样的缩写是不允许的,因为对它们没有标准。这里有一个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());

    An unspecified format that's near-universally supported

    还有一种格式不在规范中,但几乎被普遍支持,而且已经有很长一段时间了。替换代码23】,它被解释为当地时间。所以。

    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中工作。

    Parse it yourself

    自己解析这个字符串也很容易。使用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());
    @NULL:我从来没有见过一个不支持它的JavaScript引擎。IE6及以上版本支持,我见过的每个版本的Firefox,每个版本的Chrome,每个版本的Opera。 But 我们必须始终在自己的目标环境中进行测试(可悲的是,无论东西是否是 "标准 "的)。
    kris :
    在我自己的测试中,我发现 YYYY/MM/DD HH:MM:SS 的解决方案在iOS上得到的日期与我希望从 YYYY-MM-DD HH:MM:SS 得到的日期相同,而用 T 替换空格的解决方案将我的日期按时区量进行调整......(在时间上增加10小时)。 (总结。 YYYY/MM/DD HH:MM:SS works 替换代码4】会根据时区进行调整,在某些情况下会出现错误)
    @user1290746:可能是规范中的 Z 的错误,我在上面加了一个关于它的说明。
    @user1290746 这是因为添加T会将其转换为通用时间格式。另一个解决方案是将'-'替换为'/'更好。
    @T.J.Crowder - 现在是2020年,这个解决方案仍然适用于我的项目:)。我不得不处理以 "2015-05-30 00:00:00 "格式存储的日期,而iO中的JavaScript(我假设它是在Safari上运行的)仍然产生了NaN的结果。很好的解决方案!
    Andreas Louv
    Andreas Louv
    发布于 2016-09-13
    0 人赞同

    我不能告诉你为什么。也许是因为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);
        
    我将尝试一下,然后再给你答复。
    这是一个有效的解决方案。在iOS和其他系统上都适用。然而,由于某些原因,如果你有一个由2019年2月1日组成的日期,由于某些原因,日期会倒退一个月。 替换代码0
    Eyal c
    Eyal c
    发布于 2016-09-13
    0 人赞同

    一个同时适用于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]);
        
    user13091938
    user13091938
    发布于 2016-09-13
    0 人赞同

    如果有人还在寻找它。

    我在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;
        
    这个工作非常好,谢谢
    gepex
    gepex
    发布于 2016-09-13
    0 人赞同

    我在尝试解决这个问题时遇到了困难,我最终选择了一个更简单的解决方案,使用了 moment.js .你需要做的不多,只需声明日期,并在每个设备上工作。

    dda
    dda
    发布于 2016-09-13
    0 人赞同

    Try with var d = new Date("2015/12/31 00:00:00"); Works for me.

    这在我的iPhone MN4P2X/A的Safari浏览器上不起作用。它需要在日期和时间之间加上T,否则会返回 "无效日期"。
    Zvi
    Zvi
    发布于 2016-09-13
    0 人赞同
    var d = new Date("2015/12/31T00:00:00");