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

Xwi*_*utX 49 javascript datetime json date momentjs

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

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

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

JSON.stringify(date);
> "2011-06-06T18:00:00.000Z"
Run Code Online (Sandbox Code Playgroud)

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

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

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

Mat*_*int 70

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

var o = { d : new Date() };
Run Code Online (Sandbox Code Playgroud)

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

Date.prototype.toJSON = function(){ return moment(this).format(); }
Run Code Online (Sandbox Code Playgroud)

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

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

当然,这会影响所有 Date对象.如果要仅更改特定日期对象的行为,则可以仅覆盖该特定对象的toJSON函数,如下所示:

o.d.toJSON = function(){ return moment(this).format(); }
Run Code Online (Sandbox Code Playgroud)

  • @KamyarGhasemlou - 更新问题以显示一种方式.您可以改为使用单个`Date`对象实例的`toJSON`函数. (4认同)
  • 感谢您的回复和快速添加。我应该考虑覆盖特定对象的方法,非常聪明。:) (2认同)

Sha*_*son 32

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

相反, 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));
Run Code Online (Sandbox Code Playgroud)

  • 我刚刚意识到我也不能搞乱“价值”。必须使用“this[key]”。 (2认同)
  • 为了挽救其他人同样的挣扎:replacer 被递归调用,其中 `this` 是对象的当前节点,`key` 是被转换的属性,而 `value` 是一个预先转换的属性值,当 original 是一个字符串时日期。因此:`this[key]`,它给你一个 Date 而不是 `value`,它给你一个字符串。 (2认同)
  • @GrimaceofDespair 我只是在这上面浪费了很多时间!您知道为什么“this[key]”和“value”*不*是同一件事吗?我找不到任何有关它的文档。 (2认同)

bvg*_*uwe 23

基于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';
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 你是正确的通常是整整几个小时,但我知道有一个事实,即时区偏移也有分钟.例如,尼泊尔是+05:45.我认为在发布关于时间和时区的一般解决方案时,这是一个好主意,以使其尽可能健壮. (3认同)
  • 也许允许时区偏移分钟数会更好。IE。`var offsetMinutes = this.getTimezoneOffset() % 60` (2认同)

CBr*_*roe 8

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

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

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

JSON.stringify(date.toString());
Run Code Online (Sandbox Code Playgroud)

或者,既然您想稍后对"命令"进行字符串化,那么首先将该值放在那里:

var command = { time: date.toString(), contents: 'foo' };
Run Code Online (Sandbox Code Playgroud)