Safari和Chrome中的javascript中的日期解析是不同的


68

我有以下代码

var c = new Date(Date.parse("2011-06-21T14:27:28.593Z"));
console.log(c);

在Chrome上,它可以在控制台上正确打印出日期。在Safari中失败。谁是正确的,更重要的是,处理此问题的最佳方法是什么?


他们俩都给我Tue Jun 21 2011 10:27:28 GMT-0400 (Eastern Daylight Time)
罗伯特·

2
你确定。jsfiddle.net/A26Gu在Safari 5.0.4版本(6533.20.27)运行让我在“无效的日期”控制台输出
bradgonesurfing

为什么要创建两次Date对象?您对正确的定义是什么?您可以使用'Date.toISOString()'方法。但请注意:较旧的浏览器不支持此功能。
沃尔夫冈·库恩

2
Javascript Date支持2个时区,UTC和操作系统中的本地时区。您无法确定本地时区设置正确。而且由于Javascript是客户端,因此您不能真正相信它可以做任何正确的事情-甚至不分析日期。任何对应用程序至关重要的计算都应在服务器端进行。
Erik

1
@Erik-好评论,浏览器中的日期非常不可靠。在Date.parse中的使用new Date(Date.parse(string))是多余的,因为如果使用字符串调用Date构造函数,则无论如何它将其传递给Date.parse。此外,Safari在创建日期方面存在一些错误,这些错误很难(如果不是不可能)解决。
RobG 2014年

Answers:


114

您不能真正使用Date.parse。我建议您使用: new Date (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )

要分割字符串,您可以尝试

var s = '2011-06-21T14:27:28.593Z';
var a = s.split(/[^0-9]/);
//for (i=0;i<a.length;i++) { alert(a[i]); }
var d=new Date (a[0],a[1]-1,a[2],a[3],a[4],a[5] );
alert(s+ " "+d);

这些日期字符串来自服务器。如果您有办法将它们解析为单独的组件,我将非常高兴听到。
bradgonesurfing 2011年


2
Date.parse只是让我喜欢旧的野生动物园,谢谢您的回答。
Peter Mellett 2014年

12
仅当日期是当地时间时才有效。它将剥离时区并应用本地。示例:您处于-0500时区,您将它发送给世界标准时间(UTC)上午11点,您应该在当地时间上午6点返回,而在当地时间上午11点返回。
dlsso

2
正如Disso所说,此答案忽略了时区。它采用偏移量为00:00(“ Z”)的字符串并将其解析为本地字符串。Date.UTC应该已经被使用。
RobG '17

23

我的类似问题是由Safari不知道如何以RFC 822时区格式读取时区引起的。我能够使用ISO 8601格式解决此问题。如果您可以控制日期格式,则可以使用为我生成的java的SimpleDateFormat“ yyyy-MM-dd'T'HH:mm:ss.sssXXX”来工作。“ 2018-02-06T20:00:00.000 + 04:00”。不论出于何种原因,Safari无法读取“ 2018-02-06T20:00:00.000 + 0400”,请注意时区格式中缺少冒号。

// Works
var c = new Date("2018-02-06T20:00:00.000+04:00"));
console.log(c);

// Doesn't work
var c = new Date("2018-02-06T20:00:00.000+0400"));
console.log(c);

1
发现相同的行为;尚未能够找到有关此已知错误的文档。然而,这两种格式(带冒号,没有冒号)似乎是打算为可接受的格式The UTC offset is appended to the time in the same way that 'Z' was above, in the form ±[hh]:[mm], ±[hh][mm], or ±[hh]. 事实上,Chrome和火狐都能够解析偏移没有冒号
杰克科普帕

2
这篇博客文章解释了Safari和Internet Explorer的问题和一个正则表达式解决方案,但仍未表明Safari或IE是否在某处承认它们不符合ISO规范
Jack Koppa

1
嗯,这里是:bugs.webkit.org/show_bug.cgi?id=160287 尚未解决,并且鉴于IE肯定不会提供修复程序,如果可能的话,包括冒号可能最简单。
杰克·科帕

1
如果您需要带时区的日期,那就完美了。请注意,new Date("2020-04-16T20:00+04:00"))在所有浏览器(Chrome,Firefox,Safari,Edge)上也可以正常使用。
G. Egli

18

我倾向于避免 Date.parse根据该问题的其他答案,。它似乎不是可靠处理日期的可移植方法。

相反,我使用了类似下面的函数。这使用jQuery将字符串数组映射为数字数组,但这是一个非常容易删除/更改的依赖项。我还包括我认为合理的默认值,以允许您解析2007-01-092007-01-09T09:42:00使用相同的功能。

function dateFromString(str) {
  var a = $.map(str.split(/[^0-9]/), function(s) { return parseInt(s, 10) });
  return new Date(a[0], a[1]-1 || 0, a[2] || 1, a[3] || 0, a[4] || 0, a[5] || 0, a[6] || 0);
}

17

我已经在多个浏览器中对其进行了检查,是的,safari返回了invalid date。顺便说一句,您不必Date.parse在这里使用,也可以使用new Date([datestring])。Safari显然需要您提供的日期字符串的更多格式。如果将“-”替换为“ /”,则删除T和点号(.593Z)之后的所有内容,它将为您提供有效日期。此代码已经过测试,可以在Safari中使用

var datestr = '2011-06-21T14:27:28.593Z'.split(/[-T.]/);
var safdat = new Date( datestr.slice(0,3).join('/')+' '+datestr[3] );

或使用String.replace(...)

new Date("2016-02-17T00:05:01+0000".replace(/-/g,'/').replace('T',' ').replace(/(\..*|\+.*/,""))

8
这会导致您丢失时区和几分之一秒的结果Date对象。我想您可以在末尾添加一些其他代码来重新添加这些组件。
Tim Tisdall

2
不需要在第三个正则表达式中new Date("2016-02-17T00:05:01+0000".replace(/-/g,'/').replace('T',' ').replace(/\..*|\+.*/,""))
加上

只是使用.replace(/-/g,'/')作品。我的日期字符串是这样的“ 2019-06-26 23:59:59”
mars-o

9

我使用以下函数来解析带时区的日期。Chrome和Safari均可正常运行:

function parseDate(date) {
  const parsed = Date.parse(date);
  if (!isNaN(parsed)) {
    return parsed;
  }

  return Date.parse(date.replace(/-/g, '/').replace(/[a-z]+/gi, ' '));
}

console.log(parseDate('2017-02-09T13:22:18+0300'));  // 1486635738000 time in ms


1
这是最安全的方法。我想您应该将最后一个替换项更改为/[a-z]+/gi,以匹配更广泛的受众,例如使用UTC而不是仅使用
TTC的

1
@Narayon谢谢您的发言,我更改了答案。
Londeren


2

您可以添加本地客户端时区偏移量,而不是在日期字符串末尾使用“ Z”。您可能想要一个为您生成该方法的方法:

let timezoneOffset = () => {
    let date = new Date(),
        timezoneOffset = date.getTimezoneOffset(),
        hours = ('00' + Math.floor(Math.abs(timezoneOffset/60))).slice(-2),
        minutes = ('00' + Math.abs(timezoneOffset%60)).slice(-2),
        string = (timezoneOffset >= 0 ? '-' : '+') + hours + ':' + minutes;
    return string;
}

因此最终结果将是:

var c = new Date("2011-06-21T14:27:28.593" + timezoneOffset());


Z是时区偏移量,代表“零时偏移量”,也称为“ Zulu时间”(UTC),或+00:00
seBaka28 '20

1

我尝试通过截断和解析那样转换日期,使其与safari和ios正常工作。

var dateString = "2016-01-22T08:18:10.000+0000";
 var hours = parseInt(dateString.split("+")[1].substr("0","2"));
 var mins = parseInt(dateString.split("+")[1].substr("2"));
 var date = new Date(dateString.split("+")[0]);
 date.setHours(date.getHours()-hours);
 date.setMinutes(date.getMinutes()-mins);

1

这是比其他人发布的功能更强大的ISO 8601解析器。它不处理星期格式,但应在所有浏览器中一致地处理所有其他有效ISO 8601日期。

function newDate(value) {
  var field = value.match(/^([+-]?\d{4}(?!\d\d\b))(?:-?(?:(0[1-9]|1[0-2])(?:-?([12]\d|0[1-9]|3[01]))?)(?:[T\s](?:(?:([01]\d|2[0-3])(?::?([0-5]\d))?|24\:?00)([.,]\d+(?!:))?)?(?::?([0-5]\d)(?:[.,](\d+))?)?([zZ]|([+-](?:[01]\d|2[0-3])):?([0-5]\d)?)?)?)?$/) || [];
  var result = new Date(field[1], field[2] - 1 | 0, field[3] || 1, field[4] | 0, field[5] | 0, field[7] | 0, field[8] | 0)
  if (field[9]) {
    result.setUTCMinutes(result.getUTCMinutes() - result.getTimezoneOffset() - ((field[10] * 60 + +field[11]) || 0));
  }
  return result;
}

console.log(newDate('2011-06-21T14:27:28.593Z'));
console.log(newDate('1970-12-31T06:00Z'));
console.log(newDate('1970-12-31T06:00-1200'));


github.com/csnover/js-iso8601上也有一个polyfill,但是它的解析器仅限于new Date()可以处理的内容,它是ISO 8601的子集,并且将忽略的偏移量视为UTC,这是不正确的。
亚当·莱格特

0

这不是我的第三方库,而是我的-比较简单-解决方案:

function parseDateTime(datetime, timezone) {

  base = new Date(datetime.replace(/\s+/g, 'T') + 'Z');

  hoursUTC = base.toLocaleTimeString('de-AT',{ timeZone: 'UTC' }).split(':')[0];
  hoursLocal = base.toLocaleTimeString('de-AT',{ timeZone: 'Europe/Vienna' }).split(':')[0];
  
  timeZoneOffsetSign = (hoursLocal-hoursUTC) < 0 ? '-':'+';
  timeZoneOffset = Math.abs(hoursLocal-hoursUTC);
  timeZoneOffset = timeZoneOffsetSign + timeZoneOffset.toString().padStart(2, '0') + ':00';
  
  return new Date(datetime.replace(/\s+/g, 'T') + timeZoneOffset);
}

localDate = parseDateTime('2020-02-25 16:00:00','Europe/Vienna');
console.log(localDate);
console.log(localDate.toLocaleString('de-AT','Europe/Vienna'));


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.