在IE11中,ToLocaleDateString()的更改

51
在IE 11中,使用ToLocaleDateString()时我得到了一些奇怪的结果。在浏览器中返回的字符串看起来很好,例如“1/28/2014 11:00:46 AM”,但是如果我将该值复制并粘贴到纯文本编辑器中,它看起来像这样:“?1?/?28?/?2014 ?11?:?00?:?46? ?AM”。有趣的是,如果我将文本粘贴到Microsoft产品中,它看起来很好......问题在于,如果您尝试以编程方式使用该值创建日期,则无效。您可以通过在IE11中打开控制台,对其使用ToLocaleDateString(),然后尝试使用生成的字符串在javascript或所选语言(我在此处使用ASP.NET...)中创建新日期来测试此功能。
我做错了什么,或者是否应该以其他方式与javascript日期进行交互?如何摆脱那些奇怪的符号?
编辑:由于下面的评论,我能够弄清楚未显示字符是从左到右的标记。根据我将值粘贴到的编辑器和编辑器设置要使用的编码,文本将以不同的方式显示:有时带有“?”,有时没有。

你使用的“纯文本编辑器”是什么,它使用的编码是什么? - Bergi
1
请执行以下代码 var str=(new Date).toLocaleDateString();for(var i=0;i<str.length;i++)console.log(i,str.charCodeAt(i),str.charAt(i)) 并将结果发布在此处。 - Bergi
2
我使用“TextPad”作为我的编辑器。它在ANSI、DOS、UTF-8和Unicode中都显示出来了。但是当我将它粘贴到记事本中时,它就不会显示出来。运行你的JavaScript代码得到以下结果: var str=(new Date).toLocaleDateString();for(var i=0;i<str.length;i++)console.log(i,str.charCodeAt(i),str.charAt(i)) 0 8206 ? 1 49 1 2 8206 ? 3 47 / 4 8206 ? 5 50 2 6 56 8 7 8206 ? 8 47 / 9 8206 ? 10 50 2 11 48 0 12 49 1 13 52 4 undefined - ctb
14
这是IE11中的一个bug。您可以使用toLocaleDateString().replace(/\u200E/g, '')来解决它。 - Chase
1
谢谢,这让我疯了。 - Gordon Thompson
显示剩余5条评论
5个回答

49

我用以下内容进行了修复:replace(/[^ -~]/g,'') ,如下所示:

(new Date("7/15/2014").toLocaleString().replace(/[^ -~]/g,'')

2
你的回答对我来说是最简单和最直接的。谢谢。 - Austin Lovell

28
问题在于,如果您尝试使用该值编程创建日期,它是无效的。...我做错了什么,或者有其他方式可以与JavaScript Date交互吗?
是的,您做错了。您不应该使用旨在格式化某些内容以进行特定于区域设置的人类显示的函数,并期望输出可供机器解析。任何toLocaleStringtoLocaleDateStringtoLocaleTimeString的输出都仅用于人类可读的显示。(正如Bergi在评论中澄清的那样,toString也是用于人类显示的,但ECMA §15.9.4.2说它应该往返运行)您可能会得到LTR标记,因为您的显示区域设置是RTL。除此之外,请考虑地区设置将始终影响输出。也许您的语言环境使用dd/mm/yyyy格式而不是mm/dd/yyyy格式。或者您的语言环境需要亚洲或阿拉伯字符。这些都是确定显示格式时要考虑的因素,但从不适用于机器解析。
另外需要考虑到,ECMAScript规范并未定义这些方法的任何特定格式规则,不同的浏览器将会产生不同的结果。
如果意图不是为了展示给用户,那么应该使用以下方法之一:
- toISOString 将提供一个ISO8601/RFC3339格式化的时间戳 - toGMTString 或 toUTCString 将提供一个RFC822/RFC1123格式化的时间戳 - getTime 将提供一个带有毫秒精度的整数Unix时间戳
以上所有方法都将返回基于UTC的值。如果你想要本地时间,则可以使用各种访问器函数(getFullYear、getMonth等)构建自己的字符串,或者使用类库比如moment.js
这个示例使用moment.js从日期中返回一个带有偏移量的本地时间的ISO8601格式:
moment(theDate).format()   // ex:  "2014-08-14T13:32:21-07:00"

我想我明白了。那么,如果您正在构建一个要求出生日期的控件,并且您必须适应其他外国语言环境(RTL、dd/mm/yy等),那么您如何安全地获取可解析的内容(例如UTC时间戳)返回到服务器,同时仍以本地化格式向用户呈现所选日期? - ctb
这就是为什么许多日期输入控件让你从日历中选择而不是解析输入的原因。虽然有一些方法。Moment.js具有各种语言环境的输入解析功能,HTML5日期/时间/日期时间输入也有一些功能。Date对象也可以进行一些解析,并且应该与当前语言环境兼容。但是没有一种方法可以接受用户可能输入的所有内容。大多数情况下,您需要给用户一些关于预期输入格式的提示。 - Matt Johnson-Pint
然而,如果它只是一个简单的日期(比如生日),我可能不想将其转换为UTC。整个日期和日期+时间之间有区别。JavaScript的Date对象实际上是一个日期+时间对象。 - Matt Johnson-Pint
2
尽管 toString 的输出旨在用于人类可读的显示,但它并不仅仅是为此而设计的。请查看 §15.9.4.2: "使用 Date 对象 x,期望 x.valueOf() === Date.parse(x.toString())" - 当然这并不意味着它是一种互相实现交换格式,但它需要以某种方式进行解析。 - Bergi
1
@Bergi - 谢谢。在同一部分中,它说 toLocaleString 不是必需的。我已经编辑了我的答案。 :) - Matt Johnson-Pint

3
function FixLocaleDateString(localeDate) {
    var newStr = "";
    for (var i = 0; i < localeDate.length; i++) {
        var code = localeDate.charCodeAt(i);
        if (code >= 47 && code <= 57) {
            newStr += localeDate.charAt(i);
        }
    }
    return newStr;
}

只返回数字和 / 字符。似乎是这样实现的:

new Date(FixLocaleDateString(new Date("7/15/2014").toLocaleString()));

返回正确日期。如果没有调用FixLocaleDateString(),则结果将是无效日期。


这看起来应该可以工作,所以我会将其标记为答案。但它感觉像是一个hack。从ToLocaleDateString()的输出中获取Date对象是否有问题?如果日期选择器使用ToLocaleDateString()填充输入,应该如何在服务器端获取日期?如果您只是从输入中获取值,它将具有那些从左到右的字符,这将使您混乱。 - ctb
根据您使用的服务器端技术,左/右字符会被正确处理(即被忽略)。我正在将此与MVC 5 Web服务一起使用,并将输入作为字符串传输,然后通过DateTime.Parse进行解析 - 它可以处理任何编码的字符串,包括包含像Unicode字节顺序标记和类似的空格字符的字符串。毫不犹豫地说,这是一个hack。这是IE JavaScript实现中一个错误的解决方法。Webkit浏览器不会遇到这个问题,因此对它们来说不需要FixLocaleDateString。 - Evan Machusak

3

为了完整性,回答格式如下:

在我的系统上,IE 11的Date对象的方法toLocaleDateString在控制台中运行时会得到"7/6/2014"字符串,其表示为以下字节:

00000000  22 e2 80 8e 37 e2 80 8e  2f e2 80 8e 36 e2 80 8e  |"â.Z7â.Z/â.Z6â.Z|
00000010  2f e2 80 8e 32 30 31 34  22                       |/â.Z2014"|

这些不可打印字符在整个字符串中都是0xe2 0x80 0x8e,这是Unicode代码点U+200E的UTF-8表示。正如上面的注释所说,它是左至右标记。

这个 JSFiddle 看起来没有问题,可以使用 toLocaleDateString() 返回的值恢复日期。至少在我的 IE 11.0.9600.17239 with Update Version 11.0.11 (KB2976627) 中是这样的。所以也许只有控制台添加了额外的字符?


这解释了字符是什么,但那不是谜题所在。我认为更重要的问题是,如果你从ToLocaleDateString()的输出开始,如何返回实际日期。 - ctb
我更新了答案,包括一个JSFiddle链接,似乎没有遇到这个问题。 - opello

1
var startDateConverted = new Date(start).toLocaleString().replace(/[^A-Za-z 0-9 \.,\?""!@#\$%\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]*/g, '')

如果您也想去掉时间,可以使用.split(' ').slice(0, -1).join(' ');

虽然你可以只用''替换LTR标记\u200E。;-) - RobG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接