web-dev-qa-db-ja.com

ユーザーのタイムゾーンを無視し、Date()で特定のタイムゾーンを使用するように強制する方法

JSアプリでは、サーバー(Ajax)からタイムスタンプ(eq。1270544790922)を受け取ります。

そのタイムスタンプに基づいて、次を使用してDateオブジェクトを作成します。

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

現在、_dateは、現在のユーザーロケールタイムゾーンでタイムスタンプをデコードしました。私はそれを望んでいません。

_dateを使用して、このタイムスタンプをヨーロッパのヘルシンキ市の現在の時刻に変換します(ユーザーの現在のタイムゾーンは無視します)。

どうやってやるの?

93
warpech

Dateオブジェクトの基礎となる値は、実際にはUTCです。これを証明するために、new Date(0)と入力すると、Wed Dec 31 1969 16:00:00 GMT-0800 (PST)のようなものが表示されることに注意してください。 0はGMTでは0として扱われますが、.toString()メソッドは現地時間を示します。

UTCはUniversalタイムコードを表します。現在、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"

これは機能しますが、他の日付メソッドはユーザーのタイムゾーンを記述するため、目的に実際に使用することはできません。必要なのは、ヘルシンキのタイムゾーンに関連する日付オブジェクトです。この時点での選択肢は、サードパーティのライブラリを使用することです(これをお勧めします)。または、日付オブジェクトをハックアップして、ほとんどのメソッドを使用できるようにします。

オプション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 のような日付ライブラリを使用すべきだと思います。

61
Parris

ミリ秒とユーザーのタイムゾーンを考慮するには、次を使用します。

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
18
Ehren

ちょうど別のアプローチ

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

乾杯!

14
dheeran

Answer では正しい結果が得られないという疑いがあります。質問では、質問者はユーザーの現在のタイムゾーンを無視して、タイムスタンプをサーバーからヘルシンキの現在の時刻に変換したいと考えています。

ユーザーのタイムゾーンがこれまでにないものになる可能性があるため、信頼できません。

例えばタイムスタンプは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()を使用しないでください。ユーザーのタイムゾーンを処理する必要があり、これは望ましい動作ではないためです。

13
Timo Kähkönen

ヘルシンキ時間でタイムスタンプを取得すると仮定すると、1970年1月1日UTCの深夜に設定された日付オブジェクトを作成します(ブラウザのローカルタイムゾーン設定を無視するため)。次に、必要なミリ秒数を追加します。

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値を要求するには、後で注意してください。これにより、ユーザーはローカル設定に関係なく同じ日付値を見ることができます。それ以外の場合、現地時間の設定に応じて日付値がシフトされます。

2
Tibor

setUTCMilliseconds()を使用できます

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