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

IT技术 javascript date timezone
2021-01-30 06:58:33

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

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

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

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

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

我怎样才能做到这一点?

6个回答

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

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

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

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"

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

选项 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

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

选项 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)

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

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

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

抱歉,不,我的意思正好相反:) 我编辑了这个问题,也许现在更清楚了
2021-03-16 06:58:33
好的,我改变了我的解决方案。我想这就是你要找的。
2021-03-16 06:58:33
也...可以完成类似的任务,只需使用一个位置的 gmt 偏移量即可。在这种情况下,您根本不需要 javascript。
2021-03-28 06:58:33
我认为*60*60应该是*60000,因为 getTime 以毫秒为单位,getTimezoneOffset 以分钟为单位,其中一分钟有 60000 毫秒,而不是 60*60==3600
2021-03-31 06:58:33
不幸的是,这也是我唯一想到的。我想也许浏览器可以为我生成“_helsinkiOffset”。
2021-04-13 06:58:33

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

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
为了替换中央的固定偏移量,我使用了使用 CST 创建日期的概念,固定时间为 00:00,然后使用该日期的 getTTCHHours。
2021-03-19 06:58:33
+1 这对我有用。不确定“答案”如何在不以毫秒为单位进行处理的情况下工作。
2021-03-19 06:58:33
@Ehren 您不应该添加 timezoneOffset 以到达 gmt,然后减去中央偏移量吗?
2021-04-10 06:58:33

只是另一种方法

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

干杯!

由于夏令时,这可能会产生意想不到的结果。如果客户端位于使用 DST 的时区,则解析结果可能会偏离一小时(某些区域使用一小时的几分之一)。例如:用户在纽约,今天是 7 月 4 日。这意味着用户处于 GMT -0400(自 DST 开始以来的东部夏令时区);传入的时间戳是 1 月 30 日,即 GMT-0500(东部标准时区 - 一年中的那个时间没有 DST)。结果将是一个小时,因为 getTimezoneOffset() 现在为您提供偏移量,而不是一月份的偏移量。
2021-04-10 06:58:33
要解决此问题,您需要取传入日期的时间偏移量(不是当前时移)new Date().getTimezoneOffset()应更改为new Date(timestampStr).getTimezoneOffset()
2021-04-10 06:58:33

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

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

如果例如。时间戳是 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);

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

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

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

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

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

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

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

要涵盖欧洲/赫尔辛基 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;
}

用法示例:

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

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

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

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

啊,永远不要编写你自己的时间/时区实现......但我懒得 -1
2021-03-21 06:58:33
@BenMcIntyre 这是个小玩笑。或者应该是这样的。:)
2021-04-03 06:58:33
备注 赫尔辛基只有一个“l”。这个错误确实有损于这个答案。
2021-04-09 06:58:33

假设您获得赫尔辛基时间的时间戳,我将创建一个设置为 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

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