時差を考慮した日時の格納と表示この記事のURLhttp://japan.internet.com/developer/20100727/26.html
著者:Scott Mitchell
海外internet.com発の記事
はじめにクライアント、同僚、そして4Guys の読者から良く求められるのが、データ駆動式 Web アプリケーションにおける日付や時間の最適な格納および表示方法に関するアドバイスだ。 Web アプリケーションに日付を格納および表示する際に難しいことの1つが、サイトを訪れる訪問者の地域の時間帯がウェブサーバがある地域のそれと同じとは限らないという問題だ。あなたのサイトには、かなりの確率で世界中の多くの異なる時間帯の地域から訪問者が集まっている。100万件以上の書き込みのすべてに作成日時が付いているASPMessageboard.comのようなオンライン掲示板サイトを考えてみたい。ニューヨークのユーザーが4月7日の午後4:30に書き込みをし、同サイトをホスティングするウェブサーバはニューヨークより1時間早いテキサス州ダラスにある場合を想像したい。この書き込みをデータベースに格納するときは、訪問者から見た日時(4:30 PM)か、ウェブサーバから見た日時(3:30 PM)か、それともほかの値を記録するのか? また、この書き込みをニューヨークより3時間早いカリフォルニア州サンフランシスコのユーザーに表示するときの日時はどうするのだろうか? 表示するのは投稿者から見た時間(4:30 PM)か、ウェブサーバから見た時間(3:30 PM)か、それともユーザーから見た時間(1:30 PM)するのだろうか? さらに、投稿者や訪問者の時間帯を基準にしてこの日付を格納もしくは表示することにしたなら、どうやってその時間帯と時差を知るのだろうか? 夏時間などはどう調整するのだろうか? 本稿は異なる時間帯をまたいだ訪問者に対する日時の格納および表示方法の指針を提供し、これらのテクニックの一部が実際に動いている例を示すデモも用意する。ぜひ詳細をお読みいただきたい。 日時の格納日付の格納が必要なデータ駆動式の Web アプリケーションを構築するときに最初に下さなければならない判断は、この日時をデータベースに格納するときの方法だ。たいてい、データベースシステムは日時情報の格納に最低1つのデータタイプを提供する。Microsoft SQL Server では以前から「datetime」という名前のデータタイプを提供しているが、これは日付(1753-01-01から9999-12-31まで)と時間(0.00333秒の精度の時、分、秒、およびマイクロ秒)の両方を格納する。さらに、「smalldatetime」 データタイプもあり、これは狭い範囲の日付値(1900-01-01から2079-06-06まで)と分精度の時間を格納する。SQL Server 2008には、時間だけを格納するデータタイプ(「time」)、日付だけを格納するデータタイプ(「date」)、そして大幅に広い範囲の日付とさらに精度の高い時間を格納するデータタイプ(「datetime2」)など、多数の新しい日付/時間タイプが搭載されている。本稿では、「datetime」 データタイプの使用に重点を置くが、ここで解説する概念は「smalldatetime」と「datetime2」 の両データタイプにも対応する。SQL Server 2008に追加されたこれらの新しい日付/時間データタイプの詳細は、New Date Data Types in Microsoft SQL Server 2008(Microsoft SQL Server 2008の新しいデータタイプ)を参照のこと。 日時情報を格納するデータタイプを決めたら、次は日時をそのまま格納するか、時間帯に合わせて格納するかを決める必要がある。ユーザーに日時を入力させるのか、あるいは日時が特定の地理的な位置に結びついている場合は、たぶん日時はそのまま格納したいだろう。カンファレンスプランナー向けのウェブサイトを構築中だとする。カンファレンスプランナーにあなたのウェブサイトを使って彼らのカンファレンスに関する情報(開催場所、日時、各種セッション、講演者など)を入力させ、顧客にこのサイトを使ってカンファレンスサービスを吟味し、予約させるなどすることが狙いになる。カンファレンスプランナーがカンファレンス情報を入力する画面では、カンファレンスの開始日時と終了日時を入力する必要がある。また、各セッションの日時も提供する必要がある。おそらく、ユーザーが入力した日時の値をそのまま格納したい場合が多いだろう。つまり、ユーザーが特定のセッションの開始日時(2010年4月10日の1:30 PM など)を入力する時は、その実際の値をデータベースに格納する。入力された日付/時間をウェブサーバやそのほかの時間帯に合わせないこと。同様に、これらの日付/時間の値を表示するときは、そのままの値を表示し、訪問者の時間帯に合わせて変えないこと。 掲示板サイトに投稿があったときの日時など、日時が自動生成される場合は、時間帯に合わせた日時の値を格納する必要がある。SQL Server の「 getdate () 」ファンクションや.NET の「DateTime.Now」プロパティを使って日時の値を保存する場合は、ウェブサーバの時間帯に合わせた日時の値を格納することになる。これは確かに便利だがマイナス面もある。まず、あなたのアプリケーションのデータがそのウェブサーバの時間帯限定のものになってしまう。異なる時間帯にあるウェブホスティング会社に乗り換えた場合に何が起こるのか考えたい。新しい投稿では新しい時間帯が反映されるが、既存の投稿はすべて古い時間帯を示したままになる。 もっと良いアプローチとしては、時間を協定世界時(UTC)で格納する方法がある。これは、すべての時間帯をオフセットで表す標準国際時間となっている(また、UTC は夏時間調整を行わない)。日付/時間の値を UTC で核のする主な利点は、特定の時間帯に縛られなくなるためデータが可搬型になることだ。SQL Server で現在の日時を UTC で取得するには、「 getutcdate () 」ファンクションを使う。.NET では、「 DateTime.UtcNow」を使う。UTC と、Web アプリケーションで UTC を使うメリットの詳細は、「Using Coordinated Universal Time (UTC) to Store Date/Time Values」(協定世界時を使った日付/時間の値の格納)を参照。 一般的な経験則として、ユーザーに日付/時間の値を入力させる場合は、当てはまる時間帯に合わせた日時を入力している可能性が高いため(カンファレンスやフライトのスケジューリングなど)、日付をそのまま格納する。しかし、日付/時間の値が自動的に計算される場合(最も一般的なのは現在の日時)は、日付/時間の値をウェブサーバの時間帯や訪問者の時間帯ではなく UTC で格納するのが最良だと思われる。 日付と時間の表示訪問者に対して日時を表示するときは、日時を訪問者の時間帯に合わせるかどうかを決める必要がある。そのまま入力した日付/時間、あるいは特定の地理的な位置に結びつけられた日付/時間(飛行機のフライトやカンファレンスセッション)は日付をそのまま表示し、訪問者の時間帯に合わせてはならない。時間帯オフセット(UTC あるいはウェブサーバの時間帯)に合わせた日時には表示に関して3つの選択肢がある。
Entries」というテーブルを1つ持つ SQL Server 2008 Express Edition データベースがある。このテーブルは、「datetime」タイプとなっている「EntryDate」列で来賓名簿への入力が行われた日時を格納し、デフォルト値はgetutcdate () となっている。この「EntryDate」の値はユーザーが入力するものではない。来賓名簿へ記入を行う際、ユーザーが入力するのは自分の名前とメッセージだけで、「EntryDate」の値は現在の UTC 日時に自動的に設定される。来賓名簿の項目はListView コントロールを使って表示される。ItemTemplate では、「EntryDate」の値が「DisplayDate」という名前のメソッドに渡され、それがドロップダウンリストで指定されたフォーマットにしたがってデータをフォーマットして返す。 日時をそのまま表示する日時をそのまま表示する際は、たいていの場合は文化的なあいまいさをすべて排除するフォーマットを使うのがベストだ。米国では、日付は MM/DD/YY で表示されることが多いが、ほかの多くの国では DD/MM/YY のフォーマットを使う。あいまいな部分をすべて排除するために、以下のカスタム日付/時間フォーマット指定子を使って月の名前を表示に加えることを推奨する。
「 DateTime」 ストラクチャのToString ("フォーマット指定子")オーバーロードもしくはString.Formatメソッドを使うことにより、以下のコードで示されるようにこの方法で日時を出力することができる。
日時を相対的に表示するデータの相対経過日時の方が絶対日時より重要なシナリオでは、日時の値を相対値で示すことを考えたい。掲示板の例に戻ると、土壇場で行われた投稿の日時の値を言葉で示すと、「1分以内」というようになる。1時間以内に行われた投稿の時間は、「○○分前」というようになり、「○○」は投稿時点からの経過分数となる。投稿が古くなるにつれ、正確な早退時間は重要でなくなっていく。1週間以上は経過したが1カ月は経過していないという投稿は「3週間前」のように表示することができる。このような文章は確かにあまり正確ではないが、このような精度は古い投稿にとってあまり重要でない。また、1カ月以上が経過した投稿はもしかすると投稿日だけ表示し、時間は完全に削除するかもしれない。このようにするには、格納された日時の値と現在の日時の値の差を判断する簡単なコードを書く必要がある(日時の値を UTC で格納した場合は、ウェブサーバの現在の日時ではなく、現在の UTC 日時と格納された値とを必ず比較する必要がある)。筆者はこのようなコードを相当数のプロジェクトで使ってきたため、「 DateTime」ストラクチャの拡張メソッドを作成している。具体的に、筆者は「ToRelativeDateString () 」および「ToRelativeDateStringUtc () 」の2つのパブリックメソッドを追加している。これらは、現在の現地時間(あるいは現在の UTC 時間)と比較を行ったときに、「DateTime」の値に対する相対経過日時を返す。 拡張メソッドは、VB や C#の.NET Framework 3.5付属バージョンから登場した。きわめて簡潔に言うと、拡張メソッドを使えば特定のタイプで定義されたかのように扱われるメソッドを作成できるようになる。自分が作成した拡張メソッドを使うことで、筆者は以下のようなコードを書くことができる。
dt」という名前の変数を作成し、それを「April 7, 2010 9:50:30」の新しい「DateTime」オブジェクトに割り当てる。次に、「DateTime」構造本体で定義されたメソッドのようにコールすることで自分の「ToRelativeDateStringUtc () 」拡張メソッドを呼び出す。「ToRelativeDateStringUtc () 」メソッドは相対経過日時(「8分前など」)の入った文字列を戻す。拡張メソッドの作成と使用に関する詳細はExtending Base Type Functionality with Extension Methods(拡張メソッドによる基本タイプ機能の拡張n)を参照のこと。 拡張メソッドの定義が続き、これはデモプロジェクトの「 App_Code」フォルダにある。
GetRelativeDateValue」メソッドで行われ、これは拡張メソッドがコールされた「DateTime」の値と、渡された「comparedTo DateTime」の値(現在のローカルもしくは UTC 日時のいずれかにになる) の差を計算する処理から始まる。次に、さまざまな測定基準をチェックして、戻すのに適した文字列を判断する。この拡張メソッドは自分のプロジェクトで自由に使い、必要に応じて拡張していただいてかまわない。 JavaScript を使って訪問者の現地時間で日時を表示するJavaScript は日時の作成、操作、および表示のためのさまざまなファンクションを提供する(これらのファンクションを浮き彫りにするPatrick Hunlock 氏のブログには、素晴らしい参考資料がそそっている。Javascript の日付 - 完全ガイド)。JavaScript では、時間帯を指定して文字列を渡すことで日付を作成することができる。そして、「toLocaleString () 」ファンクションを使ってその時間をユーザーの現地時間帯に変換することができる。 この概念を例証するため、以下の短い JavaScript コードを考える。
上の内容をウェブページに入れて、そのページをブラウザで表示させると、その場所に来たときに JavaScript が即座に実行される。スクリプトは、新しい「 Date」オブジェクトに割り当てられた「dt」という変数の作成から処理を開始する。ご覧のように、「Date」オブジェクトを作成するときは、日時の値を文字列として指定することができ、それを JavaScript がパースする。また、この文字列には時間帯(この場合はUTC)が含まれることに注意する。 スクリプトの次の行では、この Date オブジェクトを取って「 toLocaleString () 」ファンクションをコールすると、それが日付/時間の値を現在の時間帯の文字列として返す。つまり、このページを訪れるユーザーが UTC より2時間早い時間帯にいる場合は、「toLocaleString () 」ファンクションが時間を「5:02 PM」として伝える。UTC から3時間遅れのユーザーの場合は、ファンクションが時間を「12:02 PM」として伝える。そして最後に、「document.write」ファンクションが「formattedDateTime」 変数の値をブラウザに送信する。上のコードを筆者のコンピュータ(UTC の7時間遅れ)で実行すると。以下のような朱 t ぐりょくが得られる。 Wednesday, April 07, 2010 8:02:00 AM.(ローカルのに付けもしくは時間だけを送信する場合は、「toLocaleDateString () 」もしくは「toLocaleTimeString () 」ファンクションを使う。) デモでは、ユーザーが来賓名簿の項目の日時を現地時間で見たい場合は以下のコードが実行される。
EntryDate」の値になっている。「formattedDateTime」の値は ListView の ItemTemplate に送られる。このデモをダウンロードすると、Alexander が入力した来賓名簿項目が見える。特定の来賓名簿項目の ListView で送信されたマークは以下のようになる。
まとめデータ駆動式 Web アプリケーションの多くは日時の値を格納および表示する必要がある。このようなアプリケーションにおける懸案事項懸念の1つが、サイトを訪問するユーザーがさまざまな時間帯にいることを考慮して、これらの日付/時間値の最適な格納方法だ。特定のアプリケーションでは時間をそのまま格納および表示するのが最適だ。航空券の予約やカンファレンスのスケジューリングなど、日時が地理的位置固有のものになるアプリケーションは、大抵はすべてが当てはまる。一方、日時の値が自動的に割り当てられる(たいていは現在の日時)場合は、日時を UTC で格納するのが最適な場合が多い。日時の値を表示するには、そのまま表示するか、相対経過日時を使うか、あるいは日時を訪問者の時間帯に合わせる。日時を訪問者の時間帯に合わせる場合は、UTC (あるいは日付/時間の値が格納された特定の時間帯)との時間差を指定させるか、JavaScript を使って格納された時間を訪問者の現地時間に自動変換する。プログラミングをお楽しみあれ。 著者紹介Scott Mitchell(Scott Mitchell)
japan.internet.comのウエブサイトの内容は全て、国際法、日本国内法の定める著作権法並びに商標法の規定によって保護されており、その知的財産権、著作権、商標の所有者はインターネットコム株式会社、インターネットコム株式会社の関連会社または第三者にあたる権利者となっています。
本サイトの全てのコンテンツ、テキスト、グラフィック、写真、表、グラフ、音声、動画などに関して、その一部または全部を、japan.internet.comの許諾なしに、変更、複製、再出版、アップロード、掲示、転送、配布、さらには、社内LAN、メーリングリストなどにおいて共有することはできません。 ただし、コンテンツの著作権又は所有権情報を変更あるいは削除せず、利用者自身の個人的かつ非商業的な利用目的に限ってのみ、本サイトのコンテンツをプリント、ダウンロードすることは認められています。 |