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

问题描述 投票:0回答:8

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

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);

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

javascript json date datetime momentjs
8个回答
105
投票

假设您有某种包含

Date
:

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

您可以重写

toJSON
原型的
Date
函数。在这里,我使用 moment.js 从日期创建一个
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(); }

56
投票

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

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


36
投票

基于 Matt Johnsons 的 answer,我重新实现了

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 作为秒值,则分钟将增加 1 (分钟值 + 1),秒将使用 40。


12
投票

但是,当我将其字符串化时,时区就再见了

那是因为

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

因此,如果

toString
格式是您想要的,那么只需将 that:

字符串化即可
JSON.stringify(date.toString());

或者,因为您想稍后对“命令”进行字符串化,所以首先将 that 值放在那里:

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

2
投票

我创建了一个小型库,在

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"}

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


1
投票

如果你有一个 JS Date 对象并希望将其字符串化以保留时区,那么你绝对应该使用

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
对象,您可以为日期对象实现不同风格的格式设置。

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


0
投票

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


0
投票

bvgheluwe 的答案提供了修改后的 Date.prototype.toJSON() 是一个好的开始,但不适用于非整数时区(阿德莱德 UTC+9.5、马克萨斯群岛 UTC -9.5 等)

根据他的回答,我提供了适用于所有时区的更新

Date.prototype.toJSON = function () {
    const offsetHoursDecimalMins = -this.getTimezoneOffset() / 60; //Will give 9.5 for Adelaide Australia (UTC +9.5) and -9.5 for Marquesas Islands ( UTC -9.5)
    const sign= offsetHoursDecimalMins>0?'+':'-';
    const nAbsOffsetHours = Math.abs(Math.trunc(offsetHoursDecimalMins)); //Absolute value of offset hours with decimal truncated
                      
    const offsetMins = Math.abs(this.getTimezoneOffset() % 60); //Will give 30 for Adelaide Australia and 30 for Marquesas Islands
    const strUTCOffset = `${sign}${nAbsOffsetHours.toString().padStart(2, '0')}:${offsetMins.toString().padStart(2, '0')}`; //"-09:30" for Marquesas Islands
                    
    /*The method Date.toISOString() returns UTC time in the format "2025-08-19T23:15:32.000Z", 
    but we want Local Time in this same format. To achieve this we create a new 
    date object (we don't want to change this one), and then add the offset hours
    and mins and call toISOString() on this new object*/
                    
    const correctedDate = new Date(this.getFullYear(), this.getMonth(),
        this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds(),
        this.getMilliseconds());
                    
    correctedDate.setHours(this.getHours() + offsetHoursDecimalMins); //Will add Hours and Mins (e.g 9.5 for Adelaide, -9.5 for Marquesas Islands )
                
    const x = correctedDate.toISOString().replace('Z', '');
    return `${x}${strUTCOffset}`;

    };
© www.soinside.com 2019 - 2024. All rights reserved.