如何 JSON 字符串化 javascript 日期并保留时区

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31096130/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 06:12:54  来源:igfitidea点击:

How to JSON stringify a javascript Date and preserve timezone

javascriptjsondatedatetimemomentjs

提问by XwipeoutX

I have a date object that's created by the user, with the timezone filled in by the browser, like so:

我有一个由用户创建的日期对象,时区由浏览器填充,如下所示:

var date = new Date(2011, 05, 07, 04, 0, 0);
> Tue Jun 07 2011 04:00:00 GMT+1000 (E. Australia Standard Time)

When I stringify it, though, the timezone goes bye-bye

但是,当我对其进行字符串化时,时区会再见

JSON.stringify(date);
> "2011-06-06T18:00:00.000Z"

The best way I can get a ISO8601 string while preserving the browser's timezone is by using moment.js and using moment.format(), but of course that won't work if I'm serializing a whole command via something that uses JSON.stringifyinternally (in this case, AngularJS)

在保留浏览器时区的同时获得 ISO8601 字符串的最佳方法是使用 moment.js 和 using moment.format(),但是如果我通过JSON.stringify内部使用的东西序列化整个命令当然这将不起作用(在这种情况下,AngularJS )

var command = { time: date, contents: 'foo' };
$http.post('/Notes/Add', command);

For completeness, my domain doesneed both the local time and the offset.

为了完整起见,我的域确实需要本地时间和偏移量。

回答by Matt Johnson-Pint

Assuming you have some kind of object that contains a Date:

假设您有某种包含以下内容的对象Date

var o = { d : new Date() };

You can override the toJSONfunction of the Dateprototype. Here I use moment.js to create a momentobject from the date, then use moment's formatfunction without parameters, which emits the ISO8601 extended format including the offset.

您可以覆盖原型的toJSON功能Date。这里我使用 moment.jsmoment从日期创建一个对象,然后使用format不带参数的moment函数,它发出 ISO8601 扩展格式,包括偏移量。

Date.prototype.toJSON = function(){ return moment(this).format(); }

Now when you serialize the object, it will use the date format you asked for:

现在,当您序列化对象时,它将使用您要求的日期格式:

var json = JSON.stringify(o);  //  '{"d":"2015-06-28T13:51:13-07:00"}'

Of course, that will affect allDateobjects. If you want to change the behavior of only the specific date object, you can override just that particular object's toJSONfunction, like this:

当然,这将影响所有Date对象。如果您只想更改特定日期对象的行为,您可以只覆盖该特定对象的toJSON功能,如下所示:

o.d.toJSON = function(){ return moment(this).format(); }

回答by bvgheluwe

Based on Matt Johnsons 's answer, I re-implemented toJSONwithout having to depend on moment(which I think is a splendid library, but a dependency in such a low level method like toJSONbothers me).

基于 Matt Johnsons 的回答,我重新实现toJSON而不必依赖moment(我认为这是一个出色的库,但是像这样低级方法的依赖toJSON让我感到困扰)。

Date.prototype.toJSON = function () {
  var timezoneOffsetInHours = -(this.getTimezoneOffset() / 60); //UTC minus local time
  var sign = timezoneOffsetInHours >= 0 ? '+' : '-';
  var leadingZero = (Math.abs(timezoneOffsetInHours) < 10) ? '0' : '';

  //It's a bit unfortunate that we need to construct a new Date instance 
  //(we don't want _this_ Date instance to be modified)
  var correctedDate = new Date(this.getFullYear(), this.getMonth(), 
      this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds(), 
      this.getMilliseconds());
  correctedDate.setHours(this.getHours() + timezoneOffsetInHours);
  var iso = correctedDate.toISOString().replace('Z', '');

  return iso + sign + leadingZero + Math.abs(timezoneOffsetInHours).toString() + ':00';
}

The setHoursmethod will adjust other parts of the date object when the provided value would "overflow". From MDN:

setHours当提供的值“溢出”时,该方法将调整日期对象的其他部分。来自MDN

If a parameter you specify is outside of the expected range, setHours() attempts to update the date information in the Date object accordingly. For example, if you use 100 for secondsValue, the minutes will be incremented by 1 (minutesValue + 1), and 40 will be used for seconds.

如果您指定的参数超出预期范围,则 setHours() 会相应地尝试更新 Date 对象中的日期信息。例如,如果您将 100 用于 secondsValue,则分钟将增加 1 (minutesValue + 1),而 40 将用于秒。

回答by Shawson

I'd always be inclined to not mess with functions in the prototype of system objects like the date, you never know when that's going to bite you in some unexpected way later on in your code.

我总是倾向于不搞乱系统对象原型中的函数,比如日期,你永远不知道什么时候会在你的代码中以某种意想不到的方式咬你。

Instead, the JSON.stringify method accepts a "replacer" function (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter) which you can supply, allowing you to override the innards of how JSON.stringify performs its "stringification"; so you could do something like this;

相反, JSON.stringify 方法接受您可以提供的“替换器”函数(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter),允许您可以覆盖 JSON.stringify 如何执行其“字符串化”的内部结构;所以你可以做这样的事情;

var replacer = function(key, value) {

   if (this[key] instanceof Date) {
      return this[key].toUTCString();
   }
   
   return value;
}

console.log(JSON.stringify(new Date(), replacer));
console.log(JSON.stringify({ myProperty: new Date()}, replacer));

回答by CBroe

When I stringify it, though, the timezone goes bye-bye

但是,当我对其进行字符串化时,时区会再见

That's because Tue Jun 07 2011 04:00:00 GMT+1000 (E. Australia Standard Time)is actually the result of the toStringmethod of the Dateobject, whereas stringifyseems to call the toISOStringmethod instead.

那是因为Tue Jun 07 2011 04:00:00 GMT+1000 (E. Australia Standard Time)实际上是对象的toString方法的结果Date,而stringify似乎是调用toISOString方法。

So if the toStringformat is what you want, then simply stringify that:

所以,如果toString格式是你想要什么,然后简单字符串化的是

JSON.stringify(date.toString());

Or, since you want to stringify your “command” later on, put thatvalue in there in the first place:

或者,由于您想稍后对“命令”进行字符串化,请首先将该值放在那里:

var command = { time: date.toString(), contents: 'foo' };

回答by Sujith S

let date = new Date(JSON.parse(JSON.stringify(new Date(2011, 05, 07, 04, 0, 0))));

let date = new Date(JSON.parse(JSON.stringify(new Date(2011, 05, 07, 04, 0, 0))));