Ignite UI for Blazor が提供する日付ピッカーコンポーネント、IgbDatePicker ですが、このコンポーネント上で選択した日付値は、Blazor Server でホストされている場合でも、サーバーのローカルタイムゾーンに関係なく、ブラウザのローカルタイムゾーンにおける選択日付 + 午前0時として扱われます。

その結果、例えば米国・太平洋時間 (UTC-8) のタイムゾーンで稼働している Blazor Server に、日本のタイムゾーン (UTC+9) で稼働している PC のブラウザからアクセスし、その Blazor Server アプリケーション上の IgbDatePicker で何か日付を選択すると、日本における選択した日付 + 午前0時は、太平洋時間では前日の午前7時に該当するため、この前日の午前7時のローカル日時を指す DateTime オブジェクトが選択結果として Blazor Server 側に格納されます (下図例)。

もちろん、この動作仕様で妥当な場合も多いと思われます。しかし、IgbDatePicker 上に表示された「日付」を、文字どおりにサーバー側でも扱いたい、という場合は、このままでは困ったことになります。

本ナレッジベース記事を作成時点では、IgbDatePicker からはこのシナリオをサポートする直接の機能は用意がありません。そのため、「IgbDatePicker 上に表示された日付を、文字どおりにサーバー側ローカル時刻として扱いたい」という場合は、以下の実装で対応します。

まずは、サーバー側からブラウザのタイムゾーンオフセットを取得するため、以下の内容の JavaScript プログラムを用意します。この例では time-zone.js というファイル名で、wwwroot フォルダ直下に保存することとします。

export const getTimezoneOffset = () => new Date().getTimezoneOffset();

次に、IgbDatePicker を貼り付けた Blazor Server 側のコードですが、前述のとおり、IgbDatePicker の既定の動作に任せられないため、@bind-Value によるバインドは使わず、値からコンポーネントへの Value パラメーターへの設定と、コンポーネントで選択された値を書き戻す用の ValueChanged コールバックの設定とにそれぞれ分けて実装します。

@inject IJSRuntime JSRuntime

<IgbDatePicker Value="_value" ValueChanged="OnValueChanged" />

@code
{
  private DateTime _value = DateTime.Today;

  private async ValueTask OnValueChanged() {
    // このあと中身を実装
  }
}

IgbDatePicker で日付が選択されたときに、その選択された日時 (繰り返しになりますが、ブラウザのタイムゾーンにおける午前0時の日時を、サーバーのタイムゾーンのローカル日時に換算したあとの DateTime 値) を引数に呼び出されるコールバックメソッドである、OnValueChanged メソッド内で、以下の実装を記述します。

private async Task OnValueChanged(DateTime value)
{
  // ブラウザのタイムゾーンオフセット (分) を取得
  await using var module = await JSRuntime.InvokeAsync<IJSObjectReference>(
                                               "import", "./time-zone.js");
  var timezoneOffset = await module.InvokeAsync<int>("getTimezoneOffset");

  // IgbDatePicker から送られてきた値 (このサーバー上でのローカル時刻) を、
  // ブラウザ上のローカル時刻に復元
  var browserLocalTime = value.ToUniversalTime().AddMinutes(-timezoneOffset);

  // その上で、年・月・日だけを取り出して、サーバー上での 0:00AM のローカル時刻に変換
  _value = new DateTime(
    browserLocalTime.Year, 
    browserLocalTime.Month, 
    browserLocalTime.Day, 
    0, 0, 0, DateTimeKind.Local);
}

以上の実装で、どのようなタイムゾーンのブラウザからのアクセスであっても、画面上で選択した日付の、Blazor Server が稼働しているサーバー上のタイムゾーンにおけるローカル時刻午前 0 時を指す DateTime 値がサーバー側で格納できるようになります (下図)。

なお、上記サンプルコードは簡略化のため、JavaScript モジュールの参照を毎回破棄しています (await using 構文)。実際のプロダクションコードでは、処理性能上の観点から、オーナーとなるコンポーネントのフィールド変数に JavaScript モジュール参照をキャッシュし、適宜破棄するべきです。JavaScript モジュール参照のキャッシュと破棄も実装した完全なサンプルコードは以下のリンク先から参照いただけます。

また、以下のリンクから、GitHub Codespaces でこの Blazor Server によるサンプルプログラムを編集・実行することも可能です。

Open in GitHub Codespaces

Tagged:

製品について

Ignite UI for Blazor