简体   繁体   English

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

[英]Convert JavaScript Date from one timezone to the other?

I'm receiving a date from the server in a string format (ISO 8601) with timezone specification, eg: 2014-12-14T21:00:00+0300 . 我正在从服务器接收具有时区规范的字符串格式(ISO 8601)的日期,例如: 2014-12-14T21:00:00+0300

I need to convert it to a standard JavaScript Date object in the specific timezone, eg Europe/Moscow with regards to DST and such. 我需要将其转换为特定时区的标准JavaScript Date对象,例如关于DST等的Europe/Moscow

I've tried to use Moment.js with Moment Timezone to achieve this, but to no avail. 我尝试将Moment.jsMoment Timezone结合使用来实现此目的,但无济于事。

Example

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

The jsDate is still in the local timezone of the browser (OS). jsDate仍位于浏览器(OS)的本地时区。

How do I cast JavaScript Date object to another timezone? 如何将JavaScript Date对象转换为另一个时区?

Timezones are merely a presentation issue. 时区只是一个演示问题。

The Date object will always be in UTC, but your browser will typically adjust the output when you convert it to a string into the local timezone. Date对象将始终为UTC,但是当您将其转换为本地时区的字符串时,浏览器通常会调整输出。

If you wish to present the variable jsDate as a string represented in a different timezone then you must also use something like moment to perform that conversion. 如果希望将变量jsDate表示为在不同时区表示的字符串,则还必须使用诸如moment的操作来执行该转换。

As @Alnitak said this is a strictly presentation problem. 正如@Alnitak所说,这是一个严格的呈现问题。 All JavaScript dates are internally stored in the UTC format and manipulated in the local timezone of the browser (OS). 所有JavaScript日期都以UTC格式内部存储,并在浏览器(OS)的本地时区中进行操作。 The only place where target timezone is coming into action is when you converting Date object to a string in order to show it to a user. 目标时区生效的唯一地方是将Date对象转换为字符串以便向用户显示时。 All dates will be formatted according to a local timezone of the each individual user. 所有日期将根据每个用户的本地时区进行格式化。

However, if you want to format dates in some specific timezone (for example when your users are in the New York and you want to show dates in Moscow) you will have to use more advanced formatting utilities like Moment.js and Moment Timezone . 但是,如果您要格式化某个特定时区的日期(例如,当您的用户在纽约,而您想要在莫斯科显示日期时),则必须使用更高级的格式化工具,例如Moment.jsMoment Timezone

When you control all the date formatting in your application - that can be easily implemented. 当您控制应用程序中的所有日期格式时-可以轻松实现。 However, when you need to relay this functionality to some third-party component that doesn't care about timezones and such (like some DatePicker component) it can become pretty ugly. 但是,当您需要将此功能传递给某些与时区无关的第三方组件时(例如某些DatePicker组件),它可能会变得很丑陋。

The only practical solution I've found to overcome this issue is to create a standard Date object that is pretending to be in local timezone of the user. 我发现可以解决此问题的唯一实用解决方案是创建一个标准的Date对象,该对象假装位于用户的本地时区。 Here's the function, that you can use: 这是可以使用的功能:

/**
 * 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;
}

The usage of this function can be demonstrated with the following simple test: 可以通过以下简单测试来演示此功能的用法:

/**
 * 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());

You will get the following result in the console: 您将在控制台中获得以下结果:

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

I would highly recommend not to use this approach in practice if you can do so. 如果可以的话,我强烈建议您不要在实践中使用此方法。 The better course of action is to rewrite the component to support timezones or to switch to a better component. 更好的做法是重写组件以支持时区或切换到更好的组件。 However, if there is no other options, use it very carefully and at your own risk. 但是,如果没有其他选择,请非常小心地使用,后果自负。

If there are better solutions, or this example can be improved - please let me know. 如果有更好的解决方案,或者可以改进此示例-请让我知道。

Cheers! 干杯!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM