簡體   English   中英

如何忽略用戶的時區並強制 Date() 使用特定時區

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

在 JS 應用程序中,我從服務器 (Ajax) 接收時間戳 (eq. 1270544790922 )。

基於該時間戳,我使用以下方法創建Date object:

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

現在, _date在當前用戶區域設置時區中解碼時間戳。 我不想要那個。

我想 _ date將此時間戳轉換為歐洲赫爾辛基市的當前時間(忽略用戶的當前時區)。

我怎樣才能做到這一點?

Date 對象的基礎值實際上是 UTC。 為了證明這一點,請注意,如果您輸入new Date(0)您將看到類似: Wed Dec 31 1969 16:00:00 GMT-0800 (PST) 0 在 GMT 中被視為 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 方庫(我推薦這個),或者修改日期對象,以便您可以使用它的大部分方法。

選項 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這樣的日期庫。

要考慮毫秒和用戶的時區,請使用以下內容:

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

只是另一種方法

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

干杯!

我懷疑,答案沒有給出正確的結果。 在問題中,提問者希望將時間戳從服務器轉換為 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(),因為那樣你就必須處理用戶時區,這不是想要的行為。

假設您獲得赫爾辛基時間的時間戳,我將創建一個設置為 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 值。 這樣,無論本地設置如何,用戶都會看到相同的日期值。 否則日期值將根據本地時間設置進行移動。

我的解決方案是確定瀏覽器應用的時區調整,並將其反轉:

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.

此時日期將不受時區限制且始終為 UTC,您可以將自己的偏移量應用於時間戳以生成任何時區。

使用它並之后始終使用 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')

你可以使用setUTCMilliseconds()

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM