【问题标题】:Javascript date is invalid on iOSJavascript 日期在 iOS 上无效
【发布时间】:2012-11-02 01:19:00
【问题描述】:

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

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

Android 的结果:

31.11 2015

iOS 上的结果:

NaN. NaN NaN

差异从何而来?

【问题讨论】:

    标签: javascript android ios cordova


    【解决方案1】:

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

    四种选择:

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

    使用即将推出的Temporal 功能

    截至 2021 年 8 月的此更新,Temporal proposal 处于第 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 不存在下面提到的指定日期/时间字符串格式在历史上未指定时区时存在的问题。

    指定格式

    如果您将空格更改为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 said 始终默认为 UTC。ES2015 said 始终默认为本地时间。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());

    几乎普遍支持的未指定格式

    规范中没有第二种格式,但几乎得到普遍支持并且已经存在很长时间了: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());

    【讨论】:

    • @PhilippSiegfried:太好了,很高兴有帮助!
    • @NULL:我从未见过不支持它的 JavaScript 引擎。 IE6 及更高版本,我见过的每个版本的 Firefox,每个版本的 Chrome,每个版本的 Opera。 但是,必须始终在目标环境上进行测试(遗憾的是,某事物是否“标准”)。
    • 在我自己的测试中,我发现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 已针对时区进行了调整,在某些情况下会出错)
    • @user1290746:可能是规范中的Z错误,我在上面添加了一个注释。
    • 你救了我的命,非常感谢
    【解决方案2】:

    我不能告诉你为什么。可能是因为 iOS 不像 Android 那样支持 Javascript Date 函数,或者支持不同的格式?

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

    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,那么由于某种原因,该日期会延迟一个月。 var s = d.replace(/[ :]/g, "-").split("-") console.log(new Date(s[0], s[1] - 1, s[2]));
    【解决方案3】:

    一种适用于 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]);
    }
    

    【讨论】:

      【解决方案4】:

      如果有人还在寻找它。

      在 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;
      }
      

      【讨论】:

      • 这很好用,谢谢!
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        试试var d = new Date("2015/12/31 00:00:00"); 对我有用。

        【讨论】:

        • 这在我的带有 Safari 的 iPhone MN4P2X/A 上不起作用。它需要日期和时间之间的 T 或返回“无效日期”
        【解决方案7】:

        还有

        var d = new Date("2015/12/31T00:00:00");
        

        为我工作:)

        谢谢@dda

        【讨论】:

        • 不使用T替换空间对我不起作用
        • 这在我的带有 Safari 的 iPhone MN4P2X/A 上不起作用。它需要日期和时间之间的 T 或返回“无效日期”
        • @NULLpointer 它对我有用,可能是由于旧版本。无论如何,我编辑了我对你的 cmets 的回答。
        • 更正我上面的评论 - 你编辑的答案(用 T 代替空格)这对我有用。
        猜你喜欢
        • 1970-01-01
        • 2016-10-18
        • 2022-01-23
        • 1970-01-01
        • 2010-11-24
        相关资源
        最近更新 更多