SQLのdatetimeをPHPで扱うには
昨日に続いて時間関係の話です。
なんとなく気づいてましたが、昨日の書き方だと結局time型もdatetime型も同じような計算してて何が簡単なのかが全然分かりませんでした。
やってることほとんど変わってねーじゃねーか、と先輩に言ったらdatetime型なのはSQL内だけの話、ということ。
つまりPHP上でDateTimeというクラスを使わなくても計算は出来る、ということが言いたかったらしいです。
同じ名前の型のようなものがあるから勘違いしちゃったようです・・・
で、今回はSQLのdatetime型の中身を使ってPHPで何とかしようという流れにします。
改めてSQLのdatetime型とは何なのかまとめます。
簡単に言うとdate型とtime型を半角スペースで区切って一緒に格納している感じです。
date型は yyyy-mm-dd time型は hh:mm:ss の形でSQLには保存されます。
time型はカンマ下の部分も入るみたいです。
これを合わせてdatetime型は yyyy-mm-dd hh:ii:ss と言う形になっています。
月(Month)と分(Minute)は英単語のイニシャルが被ってるので分の方は二文字目のiが使われてます。
今日の日付がdatetimeに入る場合は '2013-11-27 10:00:00' と言う感じに入ります。
時間部分を代入しなかった時は勝手に 00:00:00 になります。
逆に時間のみを代入した場合は・・・恐らくSQL上では 1753-01-01 が入ると思います。
調べたらdatetimeに入る数値の範囲の最低値が 1753-01-01 00:00:00 だったので。
さて、ここからPHP。
PHP上ではこのdatetime型の内容は基本文字列として扱われています。
が、そもそもPHPの変数と言うのは型というものがありません。
なので文字列同士で四則計算してもエラーが起こらなかったりします。
$datetime1 = '2013-11-27 10:00:00';
$datetime2 = '2013-11-27 18:00:00';
echo ( $datetime1 + $datetime2 );
こうすると4026が返ってきます。
変数の中身が文字列の場合、文字が入る手前部分までは数値として読み取り、その部分だけを計算の対象とするようです。
この場合はどちらの変数も2013までを数値とみて、この部分を加算してます。
ちなみに最初から数字以外の文字が入っている場合は0になります。
そして、おもしろいことに比較については想像している通りの結果が出るという・・・
$datetime1 = '2013-11-27 10:00:00';
if( $datetime1 > '2013-11-27 09:59:59' )
{
echo 'true';
}
else
{
echo 'false';
}
この様に書くと true が表示されます。
普通に考えたら大小比較は数値だけでできるものなので2013と2013だけを比較して == とかじゃないと真の処理にはならなそうなのですが・・・
ちなみに比較の部分なのですが、 if( $datetime1 > '09:59:59' ) のように時間だけの比較も可能です。
右辺の省略した日付は自動的に左辺の日付と同じものが入るようです。
その逆はダメでした。
$datetime1 に時刻のみを入れ、 if( $datetime1 > '2013-11-27 09:59:59' ) をすると偽の処理に入ります。
話がそれました、時間計算の話に戻ります。
PHPには時間型と判断される文字列を数値に直す関数が用意されています。
それがstrtotime()です。
1秒刻みで数値化してくれる関数ですが、この関数で直された数値には基準値と言うものが存在します。
その基準値を元に引数で入れたdatetimeの文字列がどれだけ差が出ているかを計算していると思われます。
その基準値と言うのが 1970-1-1 09:00:00 です。
日付はともかく、なぜ9時なのかは・・・多分子午線の都合だと思います。
パソコンの初期設定で時計の設定をした時のGMTが勝手に加算されてるんじゃないでしょうか。
恐らく時計の設定で日本が指定されているなら strtotime( '1970-1-1 09:00:00' ) で0が表示されます。
ここを基準にして2013-11-27 10:00:00を引数にすると 1385514000 が返ってきます。
この様にstrtotime()を使うことで完全に数値化されるため、この状態なら四則演算もちゃんとした結果を返せるようになります。
が、その結果を出すための元となる数値自体は基準値との差であるため、望んだ結果は返ってこないです。
というより時刻と時刻を計算して出すべき値というのはどのくらいの時間差があったか以外にそうは無い気もしますが・・・
さて、strtotimeで数値化した数値を見てもパッと何年何月何日何時何分何秒と答えられる人はそういないと思います。
この数値化した値を日付に戻す関数としてdate()とgmdate()があります。
中身自体はほぼ同じなのですが違いとしてGMTに対応してるかどうか、というのがあります。
例えば時刻と時刻の差を計算する文として
$dtime1 = '2013-11-27 10:00:00';
$dtime2 = '2013-11-27 11:00:00';
$calc = strtotime( $dtime2 ) - strtotime( $dtime1 );
echo $calc;
こういう文があったとします。
この場合、$calcには1時間差を示す3600(秒)が入り、それがそのまま表示されます。
これをdate()でdatetime型に直してみます。
$calc = date( 'H:i:s' , $calc );
echo $calc;
今回は時間差のみを表示したいので'H:i:s'という表示フォーマットを使用します。
すると出てくる結果は 10:00:00 になります。
本当なら01:00:00と表示してほしいところですね。
これはdate()関数も0の基準が 1970ー1ー1 09:00:00 になっているからです。
これに3600秒が追加されて10:00:00が表示されてしまうわけですね。
0の基準が 1970-1-1 00:00:00 になっているのがgmdate()になります。
date()部分をgmdate()にすると 01:00:00 が表示されます。
これで時間差は表示できますが、この時間はあくまでdatetime型で、時間の部分だけを切り出して表示しているだけです。
なので24時間を越えれば1日として繰り上がってしまうので時間のみを表示する手段としてはあまり向いてません。
57:31:22のように24時間を越えても時間のみで表示したい場合は、残念ながら今のPHPではそんな関数は用意されてないようなので自力で関数を作るしかなさそうです。
単に時間が秒単位になっているだけなので変換にそんなに苦労はしないと思います。
public function timetohis( $timesecond )
{
$hour = floor( $timesecond / 3600 );
$minute = floor( $timesecond / 60 ) % 60;
$second = $timesecond % 60;
return $hour.':'.$minute.':'.$second;
}
0詰めしないならこんな書き方で十分でしょう。
昨日もちらっとfloor()を使った気がしますが、これは小数点切り捨ての関数です。
簡単に調べた程度ですがデータベースから時間を取得し、計算して結果を表示するまでの流れにもいろいろあるんですよね・・・
1.SQLの時点で時間の計算をし、計算結果を取得してそれをそのまま表示。
2.SQLで値だけを持ってきてプログラム上で時間計算用の関数やクラスを使って計算して表示。
3.SQLで値だけを持ってきて秒の数値に直し、計算してから再び時間の表記に戻して表示。
SQL文は長くなればなるだけ分かりにくさが上がるのでプログラム側の操作に慣れている人は1.は選ばない方がいいでしょう。
プログラム側で計算手段を考えるのが面倒な人にはいいかもしれません。
2.と3.はその逆でSQLが簡単になりますが、プログラムの行数が増えます。
ただ、処理を関数化できるので同じ処理が何度も出てくる場合は有効と言えます。
特にクラスを作るのが得意な人には向いているんじゃないでしょうか。