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

How to JSON stringify a javascript Date and preserve timezone

本文关键字:保留 时区 日期 javascript JSON 字符串 如何      更新时间:2023-09-26

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

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

不过,当我把它串起来时,时区就会告别

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

在保留浏览器时区的同时,我可以获得ISO8601字符串的最好方法是使用moment.js和moment.format(),但如果我通过内部使用JSON.stringify的东西(在本例中为AngularJS)序列化整个命令,当然这是行不通的

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

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

假设您有某种包含Date:的对象

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

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

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

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

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

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

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

我总是倾向于不处理系统对象原型中的函数,比如日期,你永远不知道这会在以后的代码中以某种意想不到的方式折磨你。

相反,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));
console.log(JSON.stringify({ myProperty: new Date(), notADate: "I'm really not", trueOrFalse: true}, replacer));

根据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';
}

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

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

不过,当我把它串起来时,时区就会告别

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

因此,如果toString格式是您想要的,那么只需字符串即可

JSON.stringify(date.toString());

或者,因为你想稍后字符串化你的"命令",所以把这个值放在第一位:

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

我创建了一个小库,在JSON.stringify之后使用ISO8601字符串保留时区。该库使您可以轻松地更改本机Date.prototype.toJSON方法的行为。

npm:https://www.npmjs.com/package/lbdate

示例:

lbDate().init();
const myObj = {
  date: new Date(),
};
const myStringObj = JSON.stringify(myObj);
console.log(myStringObj);
// {"date":"2020-04-01T03:00:00.000+03:00"}

如果需要,该库还提供了自定义序列化结果的选项。

如果您有一个JS日期对象,并希望将其字符串化以保留时区,那么您肯定应该使用toLocaleDateString()。它是一个非常强大的助手函数,可以帮助您以各种可能的方式格式化Date对象。

例如,如果您想打印"2019年2月1日,星期五,太平洋标准时间";,

 const formatDate = (dateObject : Date) => {
    const options: any  = {
        weekday: 'long',
        year: 'numeric',
        month: 'long',
        day: 'numeric',
        timeZoneName: 'long'
      };
      
    return dateObject.toLocaleDateString('en-CA', options);
  };

因此,通过修改options对象,可以为Date对象实现不同样式的格式设置。

有关格式化方式的更多信息,请参阅这篇Medium文章:https://medium.com/swlh/use-tolocaledatestring-to-format-javascript-dates-2959108ea020

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