Как в формате 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 при сохранении часового пояса браузера - это использовать moment.js и использовать moment.format()
, но, конечно, это не сработает, если я сериализую целую команду через что-то, что использует JSON.stringify
внутренне (в данном случае AngularJS)
var command = { time: date, contents: 'foo' };
$http.post('/Notes/Add', command);
Для полноты моему домену нужно как местное время, так и смещение.
8 ответов
Предполагая, что у вас есть какой-то объект, который содержит 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(); }
Я всегда был бы склонен не связываться с функциями в прототипе системных объектов, такими как дата, вы никогда не знаете, когда это будет неожиданно для вас кусаться в вашем коде.
Вместо этого метод JSON.stringify принимает функцию "замена" ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify), которую вы можете предоставить, что позволяет переопределить внутреннюю часть того, как 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));
Основываясь на ответе Мэтта Джонсона, я повторно реализовал toJSON
без необходимости зависеть от moment
(который я считаю великолепной библиотекой, но зависимость в таком низкоуровневом методе, как toJSON
беспокоит меня).
Date.prototype.toJSON = function () {
var timezoneOffsetInHours = -(this.getTimezoneOffset() / 60); //UTC minus local time
var sign = timezoneOffsetInHours >= 0 ? '+' : '-';
var leadingZero = (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)
на самом деле результат toString
метод Date
объект, тогда как stringify
кажется, называют toISOString
метод вместо.
Так что если toString
Формат - это то, что вы хотите, а затем просто зафиксируйте это:
JSON.stringify(date.toString());
Или, так как вы хотите позже привести в порядок свою "команду", поместите это значение в первую очередь:
var command = { time: date.toString(), contents: 'foo' };
Я создал небольшую библиотеку, которая сохраняет часовой пояс со строкой ISO8601 после JSON.stringify
. Библиотека позволяет легко изменять поведение родного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 всеми возможными способами.
Например, если вы хотите напечатать « Пятница, 1 февраля 2019 г., стандартное тихоокеанское время »,
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 .
let date = новая дата (JSON.parse(JSON.stringify(новая дата (2011, 05, 07, 04, 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}`;
};