将JavaScript日期从一个时区转换为另一个时区?

时间:2014-12-23 13:32:30

标签: javascript date timezone momentjs dst

我是以字符串格式(ISO 8601)从服务器接收带有时区规格的日期,例如:2014-12-14T21:00:00+0300

我需要将其转换为特定时区中的标准JavaScript Date对象,例如关于DST等的Europe/Moscow

我尝试使用Moment.jsMoment 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对象转换为另一个时区?

2 个答案:

答案 0 :(得分:3)

时区仅仅是演示文稿问题。

Date对象将始终为UTC,但是当您将其转换为字符串到本地时区时,您的浏览器通常会调整输出。

如果您希望将变量jsDate显示为不同时区中表示的字符串,那么您还必须使用类似时刻的内容来执行该转换。

答案 1 :(得分:0)

正如 @Alnitak 所说,这是一个严格的演示问题。所有JavaScript日期都在内部以UTC格式存储,并在浏览器(OS)的本地时区中进行操作。目标时区生效的唯一地方是将Date对象转换为字符串以便向用户显示。所有日期都将根据每个用户的当地时区进行格式化。

但是,如果您要在某个特定时区格式化日期(例如,当您的用户位于纽约并且您希望在莫斯科显示日期时),则必须使用更高级的格式设置工具,例如Moment.jsMoment Timezone

当您控制应用程序中的所有日期格式时 - 可以轻松实现。但是,当您需要将此功能转发给某些不关心时区等的第三方组件(如某些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

如果你能这样做,我强烈建议不要在实践中使用这种方法。更好的做法是重写组件以支持时区或切换到更好的组件。但是,如果没有其他选择,请非常小心地使用它,并且风险自负。

如果有更好的解决方案,或者这个例子可以改进 - 请告诉我。

干杯!