SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

PEARライブラリ活用

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

PEARライブラリ活用 (1)


  • X ポスト
  • このエントリーをはてなブックマークに追加

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年問題のような問題を再び生まないためにも、この記事で紹介したようなケースを心にとめて開発していただきたいと思います。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
PEARライブラリ活用連載記事一覧

もっと読む

この記事の著者

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

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

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

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

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/1816 2007/12/13 16:10

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング