Javascript 如何忽略用户的时区并强制 Date() 使用特定时区

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2771609/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:52:03  来源:igfitidea点击:

How to ignore user's time zone and force Date() use specific time zone

javascriptdatetimezone

提问by warpech

In an JS app, I receive timestamp (eq. 1270544790922) from server (Ajax).

在 JS 应用程序中,我1270544790922从服务器(Ajax)接收时间戳(eq. )。

Basing on that timestamp I create Dateobject using:

基于该时间戳,我Date使用以下方法创建对象:

var _date = new Date();
_date.setTime(1270544790922);

Now, _datedecoded timestamp in current user locale time zone. I don't want that.

现在,_date在当前用户语言环境时区解码时间戳。我不想要那个。

I would like _dateto convert this timestamp to current time in city of Helsinki in Europe (disregarding current time zone of the user).

我想 _date将此时间戳转换为欧洲赫尔辛基市的当前时间(不考虑用户的当前时区)。

How can I do that?

我怎样才能做到这一点?

采纳答案by Parris

A Date object's underlying value is actually in UTC. To prove this, notice that if you type new Date(0)you'll see something like: Wed Dec 31 1969 16:00:00 GMT-0800 (PST). 0 is treated as 0 in GMT, but .toString()method shows the local time.

Date 对象的基础值实际上是 UTC。为了证明这一点,请注意,如果您键入,new Date(0)您将看到类似:Wed Dec 31 1969 16:00:00 GMT-0800 (PST)。0 在 GMT 中被视为 0,但.toString()method 显示的是本地时间。

Big note, UTC stands for Universaltime code. The current time right now in 2 different places is the same UTC, but the output can be formatted differently.

重要提示,UTC 代表通用时间码。现在 2 个不同地方的当前时间是相同的 UTC,但输出的格式可以不同。

What we need here is some formatting

我们这里需要的是一些格式

var _date = new Date(1270544790922); 
// outputs > "Tue Apr 06 2010 02:06:30 GMT-0700 (PDT)", for me
_date.toLocaleString('fi-FI', { timeZone: 'Europe/Helsinki' });
// outputs > "6.4.2010 klo 12.06.30"
_date.toLocaleString('en-US', { timeZone: 'Europe/Helsinki' });
// outputs > "4/6/2010, 12:06:30 PM"

This works but.... you can't really use any of the other date methods for your purposes since they describe the user's timezone. What you want is a date object that's related to the Helsinki timezone. Your options at this point are to use some 3rd party library (I recommend this), or hack-up the date object so you can use most of it's methods.

这有效,但是......你不能真正使用任何其他日期方法来达到你的目的,因为它们描述了用户的时区。您想要的是与赫尔辛基时区相关的日期对象。此时您的选择是使用一些 3rd 方库(我推荐这个),或者修改日期对象,以便您可以使用它的大部分方法。

Option 1 - a 3rd party like moment-timezone

选项 1 - 像 moment-timezone 这样的 3rd 方

moment(1270544790922).tz('Europe/Helsinki').format('YYYY-MM-DD HH:mm:ss')
// outputs > 2010-04-06 12:06:30
moment(1270544790922).tz('Europe/Helsinki').hour()
// outputs > 12

This looks a lot more elegant than what we're about to do next.

这看起来比我们接下来要做的要优雅得多。

Option 2 - Hack up the date object

选项 2 - 修改日期对象

var currentHelsinkiHoursOffset = 2; // sometimes it is 3
var date = new Date(1270544790922);
var helsenkiOffset = currentHelsinkiHoursOffset*60*60000;
var userOffset = _date.getTimezoneOffset()*60000; // [min*60000 = ms]
var helsenkiTime = new Date(date.getTime()+ helsenkiOffset + userOffset);
// Outputs > Tue Apr 06 2010 12:06:30 GMT-0700 (PDT)

It still thinks it's GMT-0700 (PDT), but if you don't stare too hard you may be able to mistake that for a date object that's useful for your purposes.

它仍然认为它是 GMT-0700 (PDT),但是如果您不仔细看,您可能会将其误认为是对您的目的有用的日期对象。

I conveniently skipped a part. You need to be able to define currentHelsinkiOffset. If you can use date.getTimezoneOffset()on the server side, or just use some if statements to describe when the time zone changes will occur, that should solve your problem.

我很方便地跳过了一部分。您需要能够定义currentHelsinkiOffset. 如果您可以date.getTimezoneOffset()在服务器端使用,或者只是使用一些 if 语句来描述时区更改何时发生,那应该可以解决您的问题。

Conclusion- I think especially for this purpose you should use a date library like moment-timezone.

结论- 我认为特别是为了这个目的,你应该使用像moment-timezone这样的日期库。

回答by Ehren

To account for milliseconds and the user's time zone, use the following:

要考虑毫秒和用户的时区,请使用以下内容:

var _userOffset = _date.getTimezoneOffset()*60*1000; // user's offset time
var _centralOffset = 6*60*60*1000; // 6 for central time - use whatever you need
_date = new Date(_date.getTime() - _userOffset + _centralOffset); // redefine variable

回答by Mohanraj Balasubramaniam

Just another approach

只是另一种方法

function parseTimestamp(timestampStr) {
  return new Date(new Date(timestampStr).getTime() + (new Date(timestampStr).getTimezoneOffset() * 60 * 1000));
};

//Sun Jan 01 2017 12:00:00
var timestamp = 1483272000000;
date = parseTimestamp(timestamp);
document.write(date);

Cheers!

干杯!

回答by Timo K?hk?nen

I have a suspicion, that the Answerdoesn't give the correct result. In the question the asker wants to convert timestamp from server to current time in Hellsinki disregarding current time zone of the user.

我怀疑,答案没有给出正确的结果。在问题中,提问者希望将时间戳从服务器转换为 Hellsinki 的当前时间,而不管用户的当前时区。

It's the fact that the user's timezone can be what ever so we cannot trust to it.

事实上,用户的时区可能是什么,所以我们不能相信它。

If eg. timestamp is 1270544790922 and we have a function:

如果例如。时间戳是 1270544790922,我们有一个函数:

var _date = new Date();
_date.setTime(1270544790922);
var _helsenkiOffset = 2*60*60;//maybe 3
var _userOffset = _date.getTimezoneOffset()*60*60; 
var _helsenkiTime = new Date(_date.getTime()+_helsenkiOffset+_userOffset);

When a New Yorker visits the page, alert(_helsenkiTime) prints:

当纽约人访问该页面时,alert(_helsenkiTi​​me) 会打印:

Tue Apr 06 2010 05:21:02 GMT-0400 (EDT)

And when a Finlander visits the page, alert(_helsenkiTime) prints:

当芬兰人访问页面时, alert(_helsenkiTi​​me) 打印:

Tue Apr 06 2010 11:55:50 GMT+0300 (EEST)

So the function is correct only if the page visitor has the target timezone (Europe/Helsinki) in his computer, but fails in nearly every other part of the world. And because the server timestamp is usually UNIX timestamp, which is by definition in UTC, the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT), we cannot determine DST or non-DST from timestamp.

因此,只有当页面访问者的计算机中具有目标时区(欧洲/赫尔辛基)时,该功能才正确,但在世界其他几乎所有地方都失败。并且因为服务器时间戳通常是 UNIX 时间戳,根据 UTC 的定义,它是自 Unix 纪元(1970 年 1 月 1 日 00:00:00 GMT)以来的秒数,我们无法根据时间戳确定 DST 或非 DST。

So the solution is to DISREGARD the current time zone of the user and implement some way to calculate UTC offset whether the date is in DST or not. Javascript has not native method to determine DST transition history of other timezone than the current timezone of user. We can achieve this most simply using server side script, because we have easy access to server's timezone database with the whole transition history of all timezones.

因此,解决方案是忽略用户的当前时区并实施某种方法来计算日期是否在 DST 中的 UTC 偏移量。Javascript 没有本地方法来确定除用户当前时区之外的其他时区的 DST 转换历史。我们可以使用服务器端脚本最简单地实现这一点,因为我们可以轻松访问服务器的时区数据库以及所有时区的整个转换历史。

But if you have no access to the server's (or any other server's) timezone database AND the timestamp is in UTC, you can get the similar functionality by hard coding the DST rules in Javascript.

但是,如果您无权访问服务器(或任何其他服务器)的时区数据库并且时间戳为 UTC,您可以通过在 Javascript 中硬编码 DST 规则来获得类似的功能。

To cover dates in years 1998 - 2099 in Europe/Helsinki you can use the following function (jsfiddled):

要涵盖欧洲/赫尔辛基 1998 - 2099 年的日期,您可以使用以下函数(jsfiddled):

function timestampToHellsinki(server_timestamp) {
    function pad(num) {
        num = num.toString();
        if (num.length == 1) return "0" + num;
        return num;
    }

    var _date = new Date();
    _date.setTime(server_timestamp);

    var _year = _date.getUTCFullYear();

    // Return false, if DST rules have been different than nowadays:
    if (_year<=1998 && _year>2099) return false;

    // Calculate DST start day, it is the last sunday of March
    var start_day = (31 - ((((5 * _year) / 4) + 4) % 7));
    var SUMMER_start = new Date(Date.UTC(_year, 2, start_day, 1, 0, 0));

    // Calculate DST end day, it is the last sunday of October
    var end_day = (31 - ((((5 * _year) / 4) + 1) % 7))
    var SUMMER_end = new Date(Date.UTC(_year, 9, end_day, 1, 0, 0));

    // Check if the time is between SUMMER_start and SUMMER_end
    // If the time is in summer, the offset is 2 hours
    // else offset is 3 hours
    var hellsinkiOffset = 2 * 60 * 60 * 1000;
    if (_date > SUMMER_start && _date < SUMMER_end) hellsinkiOffset = 
    3 * 60 * 60 * 1000;

    // Add server timestamp to midnight January 1, 1970
    // Add Hellsinki offset to that
    _date.setTime(server_timestamp + hellsinkiOffset);
    var hellsinkiTime = pad(_date.getUTCDate()) + "." + 
    pad(_date.getUTCMonth()) + "." + _date.getUTCFullYear() + 
    " " + pad(_date.getUTCHours()) + ":" +
    pad(_date.getUTCMinutes()) + ":" + pad(_date.getUTCSeconds());

    return hellsinkiTime;
}

Examples of usage:

用法示例:

var server_timestamp = 1270544790922;
document.getElementById("time").innerHTML = "The timestamp " + 
server_timestamp + " is in Hellsinki " + 
timestampToHellsinki(server_timestamp);

server_timestamp = 1349841923 * 1000;
document.getElementById("time").innerHTML += "<br><br>The timestamp " + 
server_timestamp + " is in Hellsinki " + timestampToHellsinki(server_timestamp);

var now = new Date();
server_timestamp = now.getTime();
document.getElementById("time").innerHTML += "<br><br>The timestamp is now " +
server_timestamp + " and the current local time in Hellsinki is " +
timestampToHellsinki(server_timestamp);?

And this print the following regardless of user timezone:

无论用户时区如何,这都会打印以下内容:

The timestamp 1270544790922 is in Hellsinki 06.03.2010 12:06:30

The timestamp 1349841923000 is in Hellsinki 10.09.2012 07:05:23

The timestamp is now 1349853751034 and the current local time in Hellsinki is 10.09.2012 10:22:31

Of course if you can return timestamp in a form that the offset (DST or non-DST one) is already added to timestamp on server, you don't have to calculate it clientside and you can simplify the function a lot. BUT remember to NOT use timezoneOffset(), because then you have to deal with user timezone and this is not the wanted behaviour.

当然,如果您可以以偏移量(DST 或非 DST 偏移量)已添加到服务器上的时间戳的形式返回时间戳,则不必在客户端计算它,并且可以大大简化函数。但记住不要使用 timezoneOffset(),因为那样你就必须处理用户时区,这不是想要的行为。

回答by Tibor

Presuming you get the timestamp in Helsinki time, I would create a date object set to midnight January 1 1970 UTC (for disregarding the local timezone settings of the browser). Then just add the needed number of milliseconds to it.

假设您获得赫尔辛基时间的时间戳,我将创建一个设置为 UTC 1970 年 1 月 1 日午夜的日期对象(忽略浏览器的本地时区设置)。然后只需添加所需的毫秒数。

var _date = new Date( Date.UTC(1970, 0, 1, 0, 0, 0, 0) );
_date.setUTCMilliseconds(1270544790922);

alert(_date); //date shown shifted corresponding to local time settings
alert(_date.getUTCFullYear());    //the UTC year value
alert(_date.getUTCMonth());       //the UTC month value
alert(_date.getUTCDate());        //the UTC day of month value
alert(_date.getUTCHours());       //the UTC hour value
alert(_date.getUTCMinutes());     //the UTC minutes value

Watch out later, to always ask UTC values from the date object. This way users will see the same date values regardless of local settings. Otherwise date values will be shifted corresponding to local time settings.

稍后注意,始终从日期对象中询问 UTC 值。这样,无论本地设置如何,用户都会看到相同的日期值。否则日期值将根据本地时间设置进行移动。

回答by jimasun

You could use setUTCMilliseconds()

你可以用 setUTCMilliseconds()

var _date = new Date();
_date.setUTCMilliseconds(1270544790922);