CodeZine(コードジン)

特集ページ一覧

PHPにおける日付と時刻の混乱

PEARライブラリ活用 (1)

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2007/11/06 14:00

目次

DateTime

 日時を表す3番目の形式DateTimeを紹介しましょう。

現在の日時

 まず、date_create()で現在の日時を取得します。

$now=date_create();

 表示の際、date()で使えたフォーマットをここでも使えます。

echo $now->format(DATE_ISO8601); // 2007-10-16T07:50:06+0000

日時の設定

 ISO 8601形式の文字列でDateTimeオブジェクトを生成することができます(ほかにもGNU Date Input Formatに準拠した形式ならば使えるようです)。エポックタイムスタンプがintの範囲外にある日時も扱えます。

$date=date_create('1901-12-13T20:45:51+0000'); 
echo $date->format(DATE_ISO8601); // 1901-12-13T20:45:51+0000
                                  //(正しい)

 DateTime::modify(string $modify)を使って日時の移動ができるはずなのですが、1582年10月4日の翌日は、かなりおかしなことになっています。

$date=date_create('1582-10-04T00:00:00+0000'); 
$date->modify('+1 day'); 
echo $date->format(DATE_ISO8601); // 1582-09-24T00:00:00+0000 (間違い)

 DateTimeは比較的新しく実装されたクラスのせいか(PHP 5.1以上)、このようなバグを含んでいます。また、PEAR Dateに比べて機能も劣っています。

ユリウス日

 結局、標準に用意されている方法で1582年10月4日の翌日を10月15日にするためには、JulianToJD()でユリウス暦の日付をユリウス日に変換し、そのユリウス日に1を追加、JDToGregorian()でグレゴリオ暦の日付に直すという作業が必要になります。

$jd=JulianToJD(10,4,1582); 
$jd++; 
echo JDToGregorian($jd); // 10/15/1582 (正しい)

 ユリウス日とは、ユリウス暦の紀元前4713年1月1日からの積算日のことです(ユリウス日とユリウス暦はまったく別物です)。ユリウス日を使えば、筆者の祖母が生まれてから今日で何日になるかも計算できます(PEARのDate_Spanでは扱えなかった期間です)。

$birthdayJD=GregorianToJD(3,8,1923); 
$now=date_parse(date_create()->format(DATE_ISO8601)); 
$nowJD=GregorianToJD($now[month],$now[day],$now[year]); 
echo $nowJD-$birthdayJD; // 30903 (正しい)

 このように、ユリウス日を基準に考えることで、日付の計算を正しく行えます。

 しかしながら、PHPのユリウス日には、それが「整数」だという致命的な欠点があります。1未満の数を使って時間を表すことができないのです。タイムゾーンのことを考えれば、日付と時刻を分離して管理してはいけないことは容易に想像できるでしょう。

 また、いちいちユリウス日に変換しなければならないのも面倒です(内部的に行って欲しいものです)。ちなみに、Javaの場合、日時を管理するクラスGregorianCalendarのデフォルトの動作が「1548年10月4日以前はユリウス暦、それより後はグレゴリオ暦」ということになっているので、一般的なユーザーが1582年10月15日の暦の切り替えを意識する必要はありません(拙著『初級プログラマのためのWebアプリケーション構築入門』を参照してください)。

まとめ

 PHPで日付と時刻を扱う標準的な方法を紹介しました。大きく分けて4つの方法があり、そのすべてに問題があることが分かりました。

  • エポックタイムスタンプ
  • 1901-12-13T20:45:52+0000から2038-01-19T03:14:07+0000までしか扱えない(2038年問題)。
  • PEAR Date
  • エポックタイムスタンプが扱えなかった日時も扱える。2147483647秒(約68年)を超える期間を扱えない。内部の暦がメソッドによって異なっている。
  • DateTime
  • エポックタイムスタンプが扱えなかった日時も扱える。日付の計算にバグがある。PEARのDateに比べて機能が少ない。新しいバージョン(PHP 5.1以上)でしか使えない。
  • ユリウス日
  • 日付の計算は正しく行えるが、日付と時間が分離してしまう。カレンダーの日付をユリウス日に変換するのが面倒。

 日付や時刻の処理を実装するのはとても大変なので、大抵の場合はプログラミング言語にあらかじめ備わっているライブラリやクラスを利用します。ライブラリには適用可能な範囲があり、その範囲内で使う分には問題ないのですが、その範囲を超えると結果はまったく信用できないものになってしまいます。

 悪いことに、PHPの場合は適用範囲外で利用しようとしても、分かりやすいエラーが返らないことがほとんどです。かつての2000年問題や、今起こりつつある2038年問題のような問題を再び生まないためにも、この記事で紹介したようなケースを心にとめて開発していただきたいと思います。



  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:PEARライブラリ活用

もっと読む

著者プロフィール

  • WINGSプロジェクト 矢吹 太朗(ヤブキ タロウ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XM...

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5