使用本地时间而非UTC时间的Date.toISOString()

38

假设我们有这个日期时间:

var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");

将其导出为字符串(console.log(d))在不同的浏览器中结果不一致:
  • Chrome: Sat Jul 21 2018 14:00:00 GMT+0200 (Paris, Madrid (heure d’été))

  • Internet Explorer等: Sat Jul 21 14:00:00 UTC+0200 2018

因此,我们无法以不一致的格式将日期时间发送到服务器。
然后自然的想法是要求一个ISO8601日期时间,并使用d.toISOString();,但它会给出UTC日期时间:2018-07-21T12:00:00.000Z,而我想要本地时区的时间。
2018-07-21T14:00:00+0200
or
2018-07-21T14:00:00

如何在不依赖第三方库(如momentjs)的情况下获取这个?

我尝试了这种方法,似乎可以工作,但有没有更自然的方法呢?

var pad = function(i) { return (i < 10) ? '0' + i : i; };

var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
Y = d.getFullYear();
m = d.getMonth() + 1;
D = d.getDate();
H = d.getHours();
M = d.getMinutes();
S = d.getSeconds();
s = Y + '-' +  pad(m) + '-' + pad(D) + 'T' + pad(H) + ':' + pad(M) + ':' + pad(S);
console.log(s);


4个回答

37

ECMA-262对于在日期字符串中使用时区的格式化有限支持,要么实现相关依赖于实现的toStringtoLocaleString方法,要么使用永远是UTC的toISOString。如果toISOString允许参数指定UTC或本地偏移量(默认为UTC)将会很好。

编写自己的函数以生成符合ISO 8601标准的带有本地偏移量的时间戳并不困难:

function toISOLocal(d) {
  var z  = n =>  ('0' + n).slice(-2);
  var zz = n => ('00' + n).slice(-3);
  var off = d.getTimezoneOffset();
  var sign = off > 0? '-' : '+';
  off = Math.abs(off);

  return d.getFullYear() + '-'
         + z(d.getMonth()+1) + '-' +
         z(d.getDate()) + 'T' +
         z(d.getHours()) + ':'  + 
         z(d.getMinutes()) + ':' +
         z(d.getSeconds()) + '.' +
         zz(d.getMilliseconds()) +
         sign + z(off/60|0) + ':' + z(off%60); 
}

console.log(toISOLocal(new Date()));


请查看此答案以获取每个操作的更详细说明:https://dev59.com/dmct5IYBdhLWcg3wAo-H#51643788 - LucaM
@LucaM - 那个答案是另一种获取时间戳的完全不同的方法,它并没有解释上述方法。它也没有包括偏移量。 - RobG
嗨@RobG,我总是更喜欢解决方案详细解释每个步骤。这就是为什么我链接了那个答案。如果您在代码片段中包含一些注释,我认为它会真正提高您的答案质量。(只是我的两分钱) - LucaM
4
这段代码相当简单。唯一不确定的是使用位运算符对偏移量进行取整。我认为最好使用Math.floor(off/60)而不是off/60|0来进行取整。 - Maciej Krawczyk
这个答案和另一个答案有同样的问题:尝试使用new Date("Sat Jul 21 2018 22:00:00 GMT-0800"),你会发现跳过了一天。在我的当前时区,这将导致2018-07-22T01:00:00.000-05:00 - undefined
@atoth— 你的初始时间戳是22:00,偏移量为-0800。如果你将偏移量改为-0500,时间会增加3个小时(因为-5比-8提前3个小时),所以时间会变成第二天的01:00。代码正好按照预期的方式运行,没有跳过任何一天。 - undefined

12

诀窍是通过时区调整时间,然后使用toISOString()。您可以通过创建新的日期并从原始时间中减去时区偏移量来实现此目的:

var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
var newd = new Date(d.getTime() - d.getTimezoneOffset()*60000);
console.log(newd.toISOString());    //  2018-07-21T22:00:00.000Z

或者,您可以简单地调整原始日期变量:

var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
d.setTime(d.getTime() - d.getTimezoneOffset()*60000);
console.log(d.toISOString());       //  2018-07-21T22:00:00.000Z

请注意,以此方式调整原始日期将影响所有日期方法。
为了方便起见,.getTime() 的结果是自1970年1月1日以来的毫秒数。然而,getTimezoneOffset() 给出与UTC的时区差异(以分钟为单位);这就是为什么需要乘以60000 以得到毫秒数。
当然,新时间仍然相对于UTC,因此您需要忽略末尾的Z
d = d.slice(0,-1);                  //  2018-07-21T22:00:00.000

将日期设置为"Sat Jul 21 2018 22:00:00 GMT-0800"将产生2018-07-22T01:00:00.000Z。我们多了一天。(我目前正在寻找解决此问题的确切版本)。 - undefined

1

我的版本:

// https://dev59.com/XGgv5IYBdhLWcg3wHdR_#37661393
// https://dev59.com/-VUM5IYBdhLWcg3wF9IX#49332027
function toISOLocal(d) {
  const z = n => ('0' + n).slice(-2);
  let off = d.getTimezoneOffset();
  const sign = off < 0 ? '+' : '-';
  off = Math.abs(off);
  return new Date(d.getTime() - (d.getTimezoneOffset() * 60000)).toISOString().slice(0, -1) + sign + z(off / 60 | 0) + ':' + z(off % 60);
}
console.log(toISOLocal(new Date()));


0

我已经找到了一个对我有效的解决方案。

请参考这篇文章:在Javascript中修改ISO日期

我自己进行了一些修改,去掉了“T”,并且它可以正常工作。以下是我正在使用的代码:

// Create date at UMT-0
  var date = new Date();
// Modify the UMT + 2 hours
  date.setHours(date.getHours() + 2);
// Reformat the timestamp without the "T", as YYYY-MM-DD hh:mm:ss
  var timestamp = date.toISOString().replace("T", " ").split(".")[0];

另一种方法是指定您需要的格式,就像这样:

// Create the timestamp format
  var timeStamp = Utilities.formatDate(new Date(), "GMT+2", "yyyy-MM-dd' 'HH:mm:ss");

注意:这些适用于一年中没有夏令时变化的地区。

有人指出上述公式是针对特定时区的。

为了获得符合ISO格式的本地时间,首先要指定适当的语言环境("sv-SE"是最接近且最容易修改的),然后进行修改(将空格改为T)以与ISO格式相同。像这样:

var date = new Date(); // Create date
var timestamp = date.toLocaleString("sv-SE").replace(" ", "T").split(".")[0]; // Reformat the Locale timestamp ISO YYYY-MM-DDThh:mm:ss

参考资料:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString)

https://www.w3schools.com/Jsref/jsref_tolocalestring.asp

https://www.w3schools.com/Jsref/tryit.asp?filename=tryjsref_tolocalestring_date_all


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community
第一个答案只适用于您的计算机,因为并非每个人都处于GMT+2时区。 - Alexandre Senges
1
原帖提到了GMT+2,因此最初的解决方案是基于此提供的。但是,我现在已经添加了一种解决方案,它将根据用户所在的时区返回本地日期和时间,并以ISO格式显示。 - Mr Shane
1
toLocaleString("sv-SE")实际上很有趣,它只是格式和本地时间仍然完整。 - Dee

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