AngularのDatePipeでタイムゾーン付きの日時変換をする時の注意点

2020.11.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!DA(データアナリティクス)事業本部 インテグレーション部の大高です。

みなさま、AngularのPipeの一つ「DatePipe」は使われていますでしょうか?

コードベースでのよくある使い方は、UI上に表示したい日付フォーマットを以下のように変換するパターンかと思います。

this.datePipe.transform(new Date(), 'yyyy/MM/dd HH:mm:ss');

この際に、タイムゾーン付きの日付を変換する場合は、注意するべき点があることを初めて知ったので、本エントリにて説明したいと思います。

やりたかったこと

今回、やりたかったことは以下の通りです。

  • サーバー側のAPIが、JSTのタイムゾーン付きの日付データを返却する
  • 返却された日付をフロントエンド(Angular)で、JSTのまま画面に表示したい

想定外だった挙動

以下のようなコードでtargetDateにJSTの日付データが入っている場合、formattedDateにフォーマット変換後の値が入るのですが、この場合ローカル環境のタイムゾーンによって値が変わります。

formattedDate = this.datePipe.transform(targetDate), 'yyyy/MM/dd HH:mm:ss');

詳しく

サンプルとして、以下のようなコードで「2020/12/25 22:15:30+0900」(JSTの日付)が、画面上でどのように表示されるかを試してみました。

date-pipe-sample.component.ts

import { DatePipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-date-pipe-sample',
  templateUrl: './date-pipe-sample.component.html',
  styleUrls: ['./date-pipe-sample.component.scss'],
  providers:[DatePipe]
})
export class DatePipeSampleComponent implements OnInit {
  localTimezoneOffset:number = -(new Date().getTimezoneOffset() / 60);
  originalDateTime:Date = new Date('2020/12/25 22:15:30+0900');
  dateTimeValue:string|null = '';
  dateTimeValueWithTz:string|null = '';

  constructor(public datePipe: DatePipe) { }

  ngOnInit(): void {
    this.dateTimeValue = this.datePipe.transform(this.originalDateTime, 'yyyy/MM/dd HH:mm:ss');
    this.dateTimeValueWithTz = this.datePipe.transform(this.originalDateTime, 'yyyy/MM/dd HH:mm:ss', '+0900');
  }

}

date-pipe-sample.component.html

<div>
  <div>Local Timezone Offset:</div>
  <hr class="fade">
  <div class="terminal">
    <pre>{{localTimezoneOffset}} Hours</pre>
  </div>
  <br/>
  <div>DateTime:</div>
  <hr class="fade">
  <div class="terminal">
    <pre>{{dateTimeValue}}</pre>
  </div>
  <br/>
  <div>DateTime with Timezone:</div>
  <hr class="fade">
  <div class="terminal">
    <pre>{{dateTimeValueWithTz}}</pre>
  </div>
</div>

ローカル環境のタイムゾーンが「(UTC+09:00)大阪、札幌、東京」の場合

まずは、ローカル環境(OS)のタイムゾーンが「(UTC+09:00)大阪、札幌、東京」の環境での結果です。

「Local Timezone Offset」は、タイムゾーンのオフセットとして認識されている時間です。タイムゾーンを「UTC+09:00」にしているので、9 Hoursとなっています。

「DateTime」と「DateTime with Timezone」は、どちらもオリジナルのデータと同じく2020/12/25 22:15:30ですので、この場合は特に問題ないかと思います。

ローカル環境のタイムゾーンが「(UTC+07:00)バンコク、ハノイ、ジャカルタ」の場合

つぎに、ローカル環境(OS)のタイムゾーンを「(UTC+07:00)バンコク、ハノイ、ジャカルタ」に変更して試してみます。

「Local Timezone Offset」は、タイムゾーンのオフセットとして認識されている時間です。タイムゾーンを「UTC+07:00」にしたので、7 Hoursになりました。

「DateTime」は、オリジナルのデータから時間が変わりました。 オリジナルのデータはJST(UTC+09:00)のデータだったので、そこから時差の2時間分の時間がズレて2020/12/25 20:15:30となっています。

一方で、「DateTime with Timezone」は、オリジナルのデータと同じく2020/12/25 22:15:30です。

これは、this.datePipe.transform(this.originalDateTime, 'yyyy/MM/dd HH:mm:ss', '+0900')のように、第3引数のtimezoneにオフセットを明示的に指定したことにより、UI上も時間がズレていません。

具体的には以下に記載があります。

angular/date_pipe.ts at 11.0.2 · angular/angular · GitHub

  • @param timezone A timezone offset (such as '+0430'), or a standard
  • UTC/GMT or continental US timezone abbreviation.
  • When not supplied, uses the end-user's local system timezone.

記載のとおり、「指定なし」の場合はローカルシステムのタイムゾーンを参照するので、今回のケースでは+0700を指定したのと同義となっています。

そのため、オリジナルのデータから2時間ズレた値が表示されたということになります。

このように、「JSTのまま時刻を表示したい」というケースでは注意が必要ですね。

まとめ

以上、AngularのDatePipeでタイムゾーン付きの日時変換をする時の注意点についてでした。

「サーバーサイドのAPIがUTCで時刻を返すケースで、フロントエンド側ではタイムゾーンに応じて表示を変えたい」というようなケースでは特に問題はありませんが、「サーバーサイドのAPIがJSTで時刻を返すケースで、フロントエンド側ではタイムゾーンに関わらずJSTで表示したい」というようなケースでは注意が必要ですね。

どなたかのお役に立てば幸いです。それでは!