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


104

在一个JS应用程序中,我1270544790922从服务器(Ajax)收到时间戳记(eq。)。

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

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

现在,_date解码当前用户区域设置时区中的时间戳。我不要

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

我怎样才能做到这一点?


我知道赫尔辛基的时区偏移在冬季为+2,在DST为+3。但是谁知道DST是什么时候?JS中仅提供了一些语言环境不可用的机制
warpech 2010年

可以但不使用Javascript的本地方法,因为javascript除了用户系统的当前时区之外,没有其他方法可以确定其他时区的时区转换历史记录(至少当我们进入80年代时,这取决于浏览器)。但是这种方式是可能的:stackoverflow.com/a/12814213/1691517,我认为我的回答会为您提供正确的结果。
TimoKähkönen2012年

Answers:


64

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

重要说明,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 party库(我建议这样做)或修改date对象,以便您可以使用其大多数方法。

选项1-第三方,例如moment-timezone

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的日期库。


也...只需使用一个位置的gmt偏移量即可完成类似的任务。在这种情况下,您根本不需要javascript。
帕里斯(Parris)2010年

抱歉,不,我的意思是完全相反:)我编辑了问题,也许现在更清楚了
warpech 2010年

好的,我更改了解决方案。我认为这就是您想要的。
巴黎

不幸的是,这也是我唯一想到的。我以为浏览器可能会为我生成“ _helsinkiOffset”。
2010年

2
我相信*60*60应该改为*60000,因为getTime以毫秒为单位,getTimezoneOffset以分钟为单位,其中一分钟有60000毫秒,而不是60 * 60 == 3600
AaronLS 2013年

20

要计算毫秒数和用户的时区,请使用以下命令:

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的日期,然后使用该日期的getUTCHHours的概念。
Grantwparks

+1这对我有用。不知道“毫秒”的答案如何在不经过毫秒的情况下工作。
克里斯·沃利斯

2
@Ehren您是否不应该添加timezoneOffset到达gmt,然后减去中心偏移量?
编码器

15

只是另一种方法

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

干杯!


2
由于夏令时,这可能会产生意想不到的结果。如果客户端位于使用DST的时区,则解析结果可能会减少一个小时(某些时区使用一小时的分数)。例如:用户在纽约,今天是7月4日。这意味着用户处于格林尼治标准时间-0400(自夏令时开始以来的东部夏令时区);传入的时间戳记是1月30日,即GMT-0500(东部标准时区-一年中的该时间没有DST)。结果将减少一个小时,因为getTimezoneOffset()现在为您提供了偏移量,而不是一月份的偏移量。
Dimitar Darazhanski

2
要解决此问题,您需要采用传入日期的时间偏移(不是当前时间)new Date().getTimezoneOffset()应该更改为new Date(timestampStr).getTimezoneOffset()
Dimitar Darazhanski

13

我怀疑答案没有给出正确的结果。在这个问题中,问问者希望将赫尔辛基的时间戳从服务器转换为当前时间,而不考虑用户的当前时区。

事实是用户的时区可以是任何时区,因此我们无法信任它。

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

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

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

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

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(),因为这时您必须处理用户时区,而这不是想要的行为。


2
nb。赫尔辛基只有一个“ l”。这个错误确实有损于此答案。
本·麦金太尔

@BenMcIntyre这是个小玩笑。或者应该是这样的。:)
TimoKähkönen17年

啊,永远不要编写自己的时间/时区实现代码...但是我太懒了,-1
mb21 '18

3

假设您获得赫尔辛基时间的时间戳,我将创建一个日期对象设置为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值。这样,无论本地设置如何,用户都将看到相同的日期值。否则,日期值将与当地时间设置相对应。


0

你可以用 setUTCMilliseconds()

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

这个答案是错误的。setUTCMilliseconds将指定的毫秒数添加到Date。
Tibor

@Tibor:来自MozDev:该setUTCMilliseconds()方法根据世界时间设置指定日期的毫秒数。[...]如果您指定的参数超出预期范围,请setUTCMilliseconds()尝试相应地更新Date对象中的日期信息。换句话说,您Date在UTC 创建具有给定Unix时间戳记的对象。
jimasun

从w3schools:setUTCMilliseconds()方法根据通用时间设置毫秒(从0到999)。请尝试上面的答案,自己看看。
蒂博尔

来自MDN:参数millisecondsValue:0到999之间的数字,表示毫秒...
Tibor

您的示例无法按预期工作,因为new Date()使用当前的本地日期创建了一个日期对象。然后加上指定的毫秒数。因此,在您的示例中,如果当前的本地日期是2017-05-04,则生成的日期将是4027
Tibor
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.