Как JSON строит дату javascript и сохраняет часовой пояс
У меня есть объект даты, созданный пользователем, с часовым поясом, заполненным браузером, например:
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 при сохранении часового пояса браузера - использовать time.js и использовать moment.format()
, но, конечно, это не сработает, если я сериализую целую команду с помощью чего-то, что использует JSON.stringify
внутренне (в данном случае, AngularJS)
var command = { time: date, contents: 'foo' };
$http.post('/Notes/Add', command);
Для полноты моего домена требуется как местное время, так и смещение.
Ответы
Ответ 1
Предполагая, что у вас есть какой-то объект, содержащий Date
:
var o = { d : new Date() };
Вы можете переопределить функцию toJSON
прототипа Date
. Здесь я использую moment.js для создания объекта 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(); }
Ответ 2
Основываясь на ответе Мэтта Джонсона, я повторно реализовал 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 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 будут использованы для секунд.
Ответ 3
Однако, когда я его строкую, часовой пояс переходит в прошлое
Thats, потому что 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' };
Ответ 4
let date = new Date (JSON.parse(JSON.stringify (новая дата (2011, 05, 07, 04, 0, 0))));