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

IT技术 javascript json date datetime momentjs
2021-01-26 22:23:47

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

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 和 using moment.format(),但是如果我通过JSON.stringify内部使用的东西序列化整个命令当然这将不起作用(在这种情况下,AngularJS )

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

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

6个回答

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

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

您可以覆盖原型toJSON功能Date这里我使用 moment.jsmoment从日期创建一个对象,然后使用format不带参数的moment函数,它发出 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(); }
@KamyarGhasemlou - 更新了问题以显示一种方式。您可以改为更改单个Date对象实例的toJSON功能。
2021-03-17 22:23:47
感谢您的回复和快速添加。我应该考虑覆盖特定对象的方法,非常聪明。:)
2021-03-19 22:23:47
睡了一个好觉:) 我在这个问题上坚持了很长一段时间。只需在 module.js (angular 2+) 中导入并覆盖它
2021-03-23 22:23:47
感谢有关覆盖 toJSON 方法的提示,这确实解决了我的问题。但是有什么安全的方法吗?我的意思是如果我覆盖它,它会在任何地方被覆盖,如果我使用它,它会在其他地方弄乱 .stringify。
2021-04-05 22:23:47

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

相反,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));

为了挽救其他人同样的挣扎:replacer 被递归调用,其中this是对象的当前节点,key被转换的属性,以及value一个预转换的属性值,当原始是一个日期时,它是一个字符串。因此:this[key],它给你一个 Date 而不是value,它给你一个字符串。
2021-03-17 22:23:47
太棒了,非常感谢(没有双关语)。值得指出的是,虽然这this[key]在箭头函数中不起作用。
2021-03-23 22:23:47
@Polv 是的,这不是最直观的!
2021-03-25 22:23:47
我才意识到我也不能惹value必须使用this[key].
2021-03-30 22:23:47

根据 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 将用于秒。

@Endless,根据您的要求,我删除了: string(三个地方)和: Date(一个地方)。现在它是普通的 javascript。
2021-03-18 22:23:47
进一步@JonathanWilson:澳大利亚也有非基于小时的时区
2021-03-25 22:23:47
你是对的,它通常是整小时,但我知道一个事实,即也有分钟的时区偏移。例如尼泊尔是+05:45。我认为在发布关于时间和时区的通用解决方案时这是一个好主意,以使其尽可能健壮。
2021-03-27 22:23:47
如果我还没有将 moment js 作为依赖项,我想我会使用这样的解决方案
2021-03-29 22:23:47
也许在时区偏移中允许几分钟会更好。IE。var offsetMinutes = this.getTimezoneOffset() % 60
2021-04-10 22:23:47

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

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

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

JSON.stringify(date.toString());

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

var command = { time: date.toString(), contents: 'foo' };
您可以date.toJSON = function () { return this.toString(); }指定如何将整个对象的“日期”字符串化。
2021-03-20 22:23:47
toString 可能有偏移量,但不是 ISO 格式。
2021-03-28 22:23:47
我知道我可以自己做,但我想用现有的对象来做。当我只想指定一个部分如何序列化时,不得不翻译整个对象是非常狡猾的。
2021-04-13 22:23:47

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