简体   繁体   English

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

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

In an JS app, I receive timestamp (eq. 1270544790922 ) from server (Ajax).在 JS 应用程序中,我从服务器 (Ajax) 接收时间戳 (eq. 1270544790922 )。

Basing on that timestamp I create Date object using:基于该时间戳,我使用以下方法创建Date object:

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

Now, _date decoded timestamp in current user locale time zone.现在, _date在当前用户区域设置时区中解码时间戳。 I don't want that.我不想要那个。

I would like _ date to 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?我怎样才能做到这一点?

A Date object's underlying value is actually in UTC. Date 对象的基础值实际上是 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) .为了证明这一点,请注意,如果您输入new Date(0)您将看到类似: Wed Dec 31 1969 16:00:00 GMT-0800 (PST) 0 is treated as 0 in GMT, but .toString() method shows the local time. 0 在 GMT 中被视为 0,但.toString()方法显示本地时间。

Big note, UTC stands for Universal time code.重要提示,UTC 代表通用时间码。 The current time right now in 2 different places is the same UTC, but the output can be formatted differently.现在 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 .您需要能够定义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.如果您可以在服务器端使用date.getTimezoneOffset() ,或者仅使用一些 if 语句来描述时区更改何时发生,那应该可以解决您的问题。

Conclusion - I think especially for this purpose you should use a date library like moment-timezone .结论- 我认为特别是为了这个目的,你应该使用像moment-timezone这样的日期库。

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

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!干杯!

I have a suspicion, that the Answer doesn'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.因此,解决方案是忽略用户的当前时区并实施某种方法来计算日期是否在 DST 中的 UTC 偏移量。 Javascript has not native method to determine DST transition history of other timezone than the current timezone of user. Javascript 没有本地方法来确定除用户当前时区之外的其他时区的 DST 转换历史。 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.我们可以使用服务器端脚本最简单地实现这一点,因为我们可以轻松访问服务器的时区数据库以及所有时区的整个转换历史。

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.当然,如果您可以以偏移量(DST 或非 DST 的)已添加到服务器上的时间戳的形式返回时间戳,则不必在客户端计算它,并且可以大大简化函数。 BUT remember to NOT use timezoneOffset(), because then you have to deal with user timezone and this is not the wanted behaviour.但记住不要使用 timezoneOffset(),因为那样你就必须处理用户时区,这不是想要的行为。

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).假设您获得赫尔辛基时间的时间戳,我将创建一个设置为 UTC 1970 年 1 月 1 日午夜的日期对象(忽略浏览器的本地时区设置)。 Then just add the needed number of milliseconds to it.然后只需添加所需的毫秒数。

 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.稍后注意,始终从日期对象中询问 UTC 值。 This way users will see the same date values regardless of local settings.这样,无论本地设置如何,用户都会看到相同的日期值。 Otherwise date values will be shifted corresponding to local time settings.否则日期值将根据本地时间设置进行移动。

My solutions is to determine timezone adjustment the browser applies, and reverse it:我的解决方案是确定浏览器应用的时区调整,并将其反转:

var timestamp = 1600913205;  //retrieved from unix, that is why it is in seconds

//uncomment below line if you want to apply Pacific timezone
//timestamp += -25200;

//determine the timezone offset the browser applies to Date()
var offset = (new Date()).getTimezoneOffset() * 60;   

//re-initialize the Date function to reverse the timezone adjustment
var date = new Date((timestamp + offset) * 1000);

//here continue using date functions.

This point the date will be timezone free and always UTC, You can apply your own offset to timestamp to produce any timezone.此时日期将不受时区限制且始终为 UTC,您可以将自己的偏移量应用于时间戳以生成任何时区。

Use this and always use UTC functions afterwards eg mydate.getUTCHours();使用它并之后始终使用 UTC 函数,例如mydate.getUTCHours();

 function getDateUTC(str) { function getUTCDate(myDateStr){ if(myDateStr.length <= 10){ //const date = new Date(myDateStr); //is already assuming UTC, smart - but for browser compatibility we will add time string none the less const date = new Date(myDateStr.trim() + 'T00:00:00Z'); return date; }else{ throw "only date strings, not date time"; } } function getUTCDatetime(myDateStr){ if(myDateStr.length <= 10){ throw "only date TIME strings, not date only"; }else{ return new Date(myDateStr.trim() +'Z'); //this assumes no time zone is part of the date string. Z indicates UTC time zone } } let rv = ''; if(str && str.length){ if(str.length <= 10){ rv = getUTCDate(str); }else if(str.length > 10){ rv = getUTCDatetime(str); } }else{ rv = ''; } return rv; } console.info(getDateUTC('2020-02-02').toUTCString()); var mydateee2 = getDateUTC('2020-02-02 02:02:02'); console.info(mydateee2.toUTCString()); // you are free to use all UTC functions on date eg console.info(mydateee2.getUTCHours()) console.info('all is good now if you use UTC functions')

You could use setUTCMilliseconds()你可以使用setUTCMilliseconds()

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

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

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