web-dev-qa-db-ja.com

JavaScriptの日付を特定のタイムゾーンに初期化する方法

特定のタイムゾーンの日付時刻を文字列として持っているので、これを現地時間に変換します。しかし、Dateオブジェクトにタイムゾーンを設定する方法はわかりません。

たとえば、私はFeb 28 2013 7:00 PM ET,を持っています

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);  

私の知る限りでは、UTC時間か現地時間のどちらかを設定できます。しかし、どうやって別のタイムゾーンに時間を設定するのですか?

UTCからのオフセットの加算/減算を使用しようとしましたが、夏時間に対抗する方法がわかりません。私が正しい方向に向かっているかどうかわからない。

JavaScriptで別のタイムゾーンからローカルタイムに時間を変換する方法を教えてください。

141
pavanred

バックグラウンド

JavaScriptのDateオブジェクトは内部的にUTCで時間を追跡しますが、通常はそれが実行されているコンピュータの現地時間で入出力を受け付けます。他のタイムゾーンで時間とともに作業するための機能はありません。 UTCまたはローカルの日付を解析して出力することはできますが、他のタイムゾーンと直接連動することはできません.

絶対に正確に言うと、Dateオブジェクトの内部表現は単一の数値で、うるう秒に関係なく、1970-01-01 00:00:00 UTCから経過したミリ秒数を表します。 Dateオブジェクト自体にはタイムゾーンや文字列の形式は格納されていません。Dateオブジェクトのさまざまな関数が使用されている場合は、コンピュータのローカルタイムゾーンが適用されます。内部表現へ。関数が文字列を生成する場合は、その文字列の生成方法を決定するためにコンピュータのロケール情報を考慮に入れることができます。詳細は機能ごとに異なり、一部は実装固有のものです。

図書館

幸いなことに、タイムゾーンを操作するために使用できるライブラリがあります。それでもDateオブジェクトの動作を変えることはできませんが、通常は標準のOlson/IANAタイムゾーンデータベースを実装し、それをJavaScriptで使用するための関数を提供します。 Webブラウザで実行している場合、オーバーヘッドが発生することがあります。すべての機能が必要な場合はデータベースが少し大きくなる可能性があります。幸いなことに、これらのライブラリの多くはあなたがサポートしたいゾーンを選択的に選択することを可能にし、データサイズをずっとおいしくします。また、最新の機能を使用して、自分自身で出荷する代わりにIntl APIからタイムゾーンデータを取得することもあります。

私が知っているこのためのいくつかのライブラリがあります:

Luxonは、おそらく現代のあらゆる用途に最も安全な方法であり、タイムゾーンデータにIntl APIを使用しているため、最も軽量です。

Moment-timezoneは moment.js の拡張であり、独自のタイムゾーンデータを持っています。

js-jodaは(Javaからの)Joda-Time APIのJavaScript実装であり、別のモジュールによるタイムゾーンサポートを含みます。

date-fns-tzは date-fns 2.xの拡張です。 date-fns-timezoneは date-fns 1.xの拡張です。

BigEasy/TimeZoneも正しい方向に進んでいるように見えます。

WallTime-js サポート終了 、そしてオーナーはmoment-timezoneに移行しています。

TimeZoneJSは最も長い間使用されてきましたが、特に夏時間の間の移行の近くで、長年のバグがあることが知られています。うまくいけば、これらは将来のある時点で修正されるでしょう。

tz.jsもしばらく前から出回っていますが、あまり文書化されていません、私見。

これらのライブラリを評価して、どれがニーズを満たすかを確認してください。よくわからない場合は、moment/moment-timezoneを付けてください。

現代環境におけるネイティブサポート

あなたが現代の環境にあなたの使用を制限することができるならば、今あなたは特別なライブラリなしで以下をすることができます:

new Date().toLocaleString("en-US", {timeZone: "America/New_York"})

これは包括的なソリューションではありませんが、出力変換のみを必要とする多くのシナリオ(UTCまたは現地時間から特定の時間帯への変換、ただしそれ以外の方向への変換)には有効です。これはECMAScript国際化API(ECMA-402)の一部です。詳しくは この投稿 をご覧ください。 この互換性表 はどのバージョンがサポートされているかを追跡します。これは上記のIntl APIで、特定のライブラリが現在内部で使用しています。

(明確にするために、これはDateオブジェクトを初期化しませんが、ロケール固有の文字列表現を作成するときにタイムゾーンを適用するために使用できます。)

今後の提案

TC39 Temporal Proposal は、JavaScript言語自体で日付と時刻を処理するための新しい標準オブジェクトセットを提供することを目的としています。これはタイムゾーン対応オブジェクトのサポートを含みます。

232
Matt Johnson

マットジョンソンが言ったように

最近のWebブラウザに使用を制限することができれば、特別なライブラリなしで次をすることができます:

new Date().toLocaleString("en-US", {timeZone: "America/New_York"})

これは包括的なソリューションではありませんが、出力変換のみを必要とする多くのシナリオ(UTCまたは現地時間から特定の時間帯への変換、ただしそれ以外の方向への変換)には有効です。

そのため、ブラウザは、日付を作成するときにIANAタイムゾーンを読み取ることができないか、既存のDateオブジェクトのタイムゾーンを変更するためのメソッドを持っていますが、ハッキングがあるようです。

  function changeTimezone(date,ianatz) {

     // suppose the date is 12:00 UTC
     var invdate = new Date(date.toLocaleString('en-US', { 
        timeZone: ianatz 
     }));

     // then invdate will be 07:00 in Toronto
     // and the diff is 5 hours
     var diff = date.getTime()-invdate.getTime();

     // so 12:00 in Toronto is 17:00 UTC
     return new Date(date.getTime()+diff);

   }

使用法

var there = new Date(when);
var here = changeTimezone(there,"America/Toronto");
17
commonpike

以下のように、new Date()にタイムゾーンオフセットを指定できます。

new Date('Feb 28 2013 19:00:00 EST')

または

new Date('Feb 28 2013 19:00:00 GMT-0500')

DateはUTC時間を格納するので(つまりgetTimeはUTCで戻る)、javascriptは時間をUTCに変換し、toStringのようなものを呼び出すと、UTC時間をブラウザのローカルタイムゾーンに変換し、ローカルタイムゾーンで文字列を返します。 UTC+8を使っています:

> new Date('Feb 28 2013 19:00:00 GMT-0500').toString()
< "Fri Mar 01 2013 08:00:00 GMT+0800 (CST)"

また、通常のgetHours/Minute/Secondメソッドを使うことができます。

> new Date('Feb 28 2013 19:00:00 GMT-0500').getHours()
< 8

(この8は、時間が私の現地時間に変換された後 - UTC+8、時間数は8です。)

16
maowtm

サードパーティのライブラリを気にせずにこれを行うための最もサポートされた方法は、適切なタイムスタンプを計算するためにgetTimezoneOffsetを使用すること、または必要な日時を取得するために時間を更新することです。

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);

// ET timezone offset in hours.
var timezone = -5;
// Timezone offset in minutes + the desired offset in minutes, converted to ms.
// This offset should be the same for ALL date calculations, so you should only need to calculate it once.
var offset = (mydate.getTimezoneOffset() + (timezone * 60)) * 60 * 1000;

// Use the timestamp and offset as necessary to calculate min/sec etc, i.e. for countdowns.
var timestamp = mydate.getTime() + offset,
    seconds = Math.floor(timestamp / 1000) % 60,
    minutes = Math.floor(timestamp / 1000 / 60) % 60,
    hours   = Math.floor(timestamp / 1000 / 60 / 60);

// Or update the timestamp to reflect the timezone offset.
mydate.setTime(mydate.getTime() + offset);
// Then Output dates and times using the normal methods.
var date = mydate.getDate(),
    hour = mydate.getHours();

EDIT

以前は日付変換を実行するときにUTCメソッドを使用していましたが、これは正しくありませんでした。時間にオフセットを加えることで、ローカルのget関数を使うことは望ましい結果を返します。

2
Shaun Cockerill

私は単体テストについても同様の問題に遭遇しました(単体テストがスナップショットを作成するためにローカルで実行され、その後CIサーバーが(潜在的に)別のタイムゾーンで実行されスナップショット比較が失敗する場合)。私たちのDateとそれをサポートするいくつかのメソッドをモックしました:

describe('...', () => {
  let originalDate;

  beforeEach(() => {
    originalDate = Date;
    Date = jest.fn(
      (d) => {
        let newD;
        if (d) {
          newD = (new originalDate(d));
        } else {
          newD = (new originalDate('2017-05-29T10:00:00z'));
        }
        newD.toLocaleString = () => {
          return (new originalDate(newD.valueOf())).toLocaleString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleDateString = () => {
          return (new originalDate(newD.valueOf())).toLocaleDateString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleTimeString = () => {
          return (new originalDate(newD.valueOf())).toLocaleTimeString("en-US", {timeZone: "America/New_York"});
        };
        return newD;
      }
    );
    Date.now = () => { return (Date()); };
  });

  afterEach(() => {
    Date = originalDate;
  });

});
2
JRulle

私はその3年は遅すぎることを知っていますが、多分それは誰か他の人を助けることができます。

ドイツのタイムゾーンと似たようなことをしているのですが、夏時間とうるう年が366日あるため、これは少し複雑です。

サマータイムの時間帯によってタイムゾーンが変わると、 "isDaylightSavingTimeInGermany"関数を使用して少し作業が必要になる場合があります。

とにかく、このページをチェックしてください。 https://github.com/zerkotin/german-timezone-converter/wiki

主なメソッドは以下のとおりです。convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

それを文書化することに努力を払うので、それはそれほど混乱しないでください。

2
zerkotin

上記の答えに基づいて、私はこのネイティブの1つのライナーを使用して、長いタイムゾーン文字列を3文字の文字列に変換しています。

var longTz = 'America/Los_Angeles';
var shortTz = new Date().
    toLocaleString("en", {timeZoneName: "short", timeZone: longTz}).
    split(' ').
    pop();

これにより、提供された日付に応じてPDTまたはPSTが提供されます。 Salesforce(Aura/Lightning)で開発している私の特定のユースケースでは、バックエンドから長い形式のユーザータイムゾーンを取得できます。

1
Shane

Npmのctocを使ってみてください。 https://www.npmjs.com/package/ctoc_timezone

タイムゾーン(ほとんどのタイムゾーンは400前後)とそれを表示したいすべてのカスタムフォーマットを変更する簡単な機能があります。

1

date-from-timezone を試してください。ネイティブに利用可能なIntl.DateTimeFormatを使って、予定日を解決します。

私は数年前から自分のプロジェクトでこの方法を使っていましたが、今ではそれを小さなOSプロジェクトとして公開することにしました:)

0
Mariusz Nowak