我从服务器收到带有时区规范的字符串格式(ISO 8601)的日期,例如:2014-12-14T21:00:00+0300
。
我需要将其转换为特定时区的标准 JavaScript Date
对象,例如 Europe/Moscow
关于 DST 等。
我试图使用Moment.js和Moment Timezone来实现这一点,但无济于事。
例
var date = '2014-12-14T21:00:00+0300';
var timezone = 'Europe/Moscow';
var momentDate = moment.tz(date, timezone);
var jsDate = momentDate.toDate();
console.log(
date,
momentDate.format('DD.MM.YYYY HH:mm:ss ZZ'),
jsDate
);
jsDate
仍采用浏览器 (OS) 的本地时区。
如何将 JavaScript Date 对象投射到另一个时区?
只是一个表示问题。
Date
对象将始终采用 UTC,但当您将其转换为本地时区的字符串时,浏览器通常会调整输出。
如果您希望将变量jsDate
显示为以不同时区表示的字符串,则还必须使用类似 moment 的内容来执行该转换。
@Alnitak所说,这是一个严格的表示问题。所有 JavaScript 日期都以 UTC 格式存储在内部,并在浏览器 (OS) 的本地时区进行操作。目标时区发挥作用的唯一位置是将 Date 对象转换为字符串以向用户显示它。所有日期将根据每个用户的本地时区进行格式化。
但是,如果要以某些特定时区设置日期格式(例如,当用户在纽约并且您希望在莫斯科显示日期时),则必须使用更高级的格式设置实用程序,如时刻.js和时刻时区。
当您控制应用程序中的所有日期格式时 - 这可以很容易地实现。但是,当您需要将此功能中继到某些不关心时区之类的第三方组件(如某些 DatePicker 组件)时,它可能会变得非常丑陋。
我发现解决此问题的唯一实用解决方案是创建一个假装在用户本地时区的标准Date
对象。这是您可以使用的函数:
/**
* Returns standard Date from Moment.js date as if it was in local timezone.
* Use getMoment() method of the result to get proper Moment.js date back.
*
* @param {object} momentDate
*
* @returns {Date}
*/
function momentAsLocalDate(momentDate) {
var origOffset = momentDate.format('ZZ');
var localOffset = moment().format('ZZ');
// Creating a standard date as if date was in current browser TZ.
var stdDate = new Date(
// Creating date from ISO:8601 format.
momentDate.format('YYYY-MM-DDTHH:mm:ss') + localOffset
);
stdDate.getMoment = function() {
function pad(number) {
if (number < 10) {
return '0' + number;
}
return number;
}
// String in ISO:8601 format.
var dateString =
this.getFullYear() +
'-' + pad(this.getMonth() + 1) +
'-' + pad(this.getDate()) +
'T' + pad(this.getHours()) +
':' + pad(this.getMinutes()) +
':' + pad(this.getSeconds()) +
origOffset
;
// Creating a new moment from date string.
var momentDate = moment(dateString);
// Setting original timezone.
momentDate.zone(origOffset);
return momentDate;
};
return stdDate;
}
可以通过以下简单测试来演示此函数的用法:
/**
* A stupid third-party component that doesn't care about timezones.
*
* @param {Date} inputDate
*/
var thirdPartyComponent = function(inputDate) {
// Showing date to a user.
console.log('Here is the date: ' + inputDate);
// Changing the date.
inputDate.setHours(17, 30);
};
var momentDate = moment('2014-12-14T21:00:00+03:00').zone('+03:00');
console.log('Original Moment Date', momentDate.format());
var stdDate = momentAsLocalDate(momentDate);
console.log('Converted Local Date', stdDate);
thirdPartyComponent(stdDate);
console.log('Changed Local Date', stdDate);
console.log('Converted Moment Date', stdDate.getMoment().format());
您将在控制台中获得以下结果:
Original Moment Date 2014-12-14T21:00:00+03:00
Converted Local Date Sun Dec 14 2014 21:00:00 GMT-0500 (EST)
Here is the date: Sun Dec 14 2014 21:00:00 GMT-0500 (EST)
Changed Local Date Sun Dec 14 2014 17:30:00 GMT-0500 (EST)
Converted Moment Date 2014-12-14T17:30:00+03:00
如果可以的话,我强烈建议不要在实践中使用这种方法。更好的做法是重写组件以支持时区或切换到更好的组件。但是,如果没有其他选择,请非常小心地使用它,并自行承担风险。
如果有更好的解决方案,或者这个例子可以改进 - 请告诉我。
干杯!