WordPress5.3では日付/取得関数が大きく変わります。これまでの関数は非推奨になり将来なくなります。それに代わって新しい関数が追加されました。
PHPではすでにDateTimeを使うのが当たり前だったので、やっとWPもやってくれた印象。
公式ガイドからサンプルコードつきでかんたんに解説します。
開発者向けの話です。テーマ・プラグインを作っている人は要チェック。
WPタイムスタンプの段階的廃止
これまで、WordPressの日付/時刻コンポーネントはWordPressタイムスタンプに依存していました。
WPタイムスタンプ = Unixタイムスタンプ(≒ UTC) + タイムゾーンオフセット
UNIXタイム
POSIXタイム。
UTCの1970年1月1日午前0時0分0秒を基準に何秒進んだかで表現する時間。
イギリスのグリニッジ(経度0)の時間がベースで、時差(秒)をプラマイ(+,-)して対象地域の時間を計算する。
(タイムゾーン、ローカライズ)
UNIXタイムはタイムゾーンを入れない時間をいう。
(経度0のUNIXタイム)
多くのシステムで採用されているためUNIX OSの時間とはかぎらない。また、閏秒(うるう秒)が正確でない。
秒数で表現するため値に限界があり、2038年1月19日12時14分8秒になると計算が狂うという問題がある。(2038年問題)
UTC(協定世界時)
Coordinated Universal Time
イギリス・ロンドンのグリニッジを基準にしたグリニッジ標準時(GMT)から発展させた世界の標準時。
(グリニッジは経度の基準でもある。経度0。)
通信分野などUTCとGMTは同じものとして扱うこともある。
ロンドンと日本の時差は9時間で、日本標準時は『+0900 (JST)』と書く。
(日本はロンドンより9時間早い。)
タイムゾーンオフセットはローカライズとも呼ばれ、UTCの基準(グリニッジ)からの時差です。WordPressでは、管理画面で設定されたタイムゾーンを整数値に変換します。
日本の時差9時間の秒 = タイムゾーンオフセット値
9(時間) = 540(分) = 32400(秒)
WPタイムスタンプを基準にしたことが、多くのバグを発生させ上流のPHPや外部システムとの相互運用性が欠如していました。
またドキュメントでは、これらを誤ってUnixタイムスタンプと呼んでいました。
公式ガイドでは、下位バージョンとの互換性を保ったままWPタイムスタンプを廃止することは不可能だと言い切っています。
(あきらめてる?)
と同時に、これらの作業をしていくそうです。
コア(WP本体)での使用を減らしていく。
ドキュメントを修正する。
UNIXタイムスタンプを使った新しいAPIを提供する。
WordPress5.3では新しい関数が追加されています。
新旧API比較
WP5.3で新しいAPI(関数)が追加されたことで、いままで慣れ親しんだAPIが非推奨になりました。その対応表です。
新 | 旧 | ||
---|---|---|---|
wp_timezone_string() | サイトのタイムゾーン取得。 文字列を返す。 | - | タイムゾーン取得関数なし。 get_option()で取ることはできた。 |
wp_timezone() | サイトのタイムゾーン取得。 DateTimeZoneオブジェクトを返す。 | - | |
wp_date() | ローカルタイム取得。 引数にタイムゾーンが追加され、任意のローカルタイムの取得可能。 | date_i18n() | WP5.3以降、wp_date()のレガシーラッパーになった。 (内部でwp_date()を使っている。) 使い方は以前のまま。 |
current_datetime() | サイトのタイムゾーンの現在日時取得。 DateTimeImmutableオブジェクトを返す。 引数がないので、シンプル。 | current_time() | 引数で、GMT、MySQLのフォーマットなど指定できた。 引数で結果が変わるのがややこしい。 |
get_post_datetime() | パラメータ指定を直感的に分かりやすくした。 更新日時の取得可。 | get_post_time() | WP5.3以降、get_post_datetime()のレガシーラッパーになった。 パラメータがややこしい。 $d = 'U' or 'G' って何? $gmt = false or true でどうなるの? 更新日時が取れない。 |
get_post_timestamp() | get_post_datetime()のタイムスタンプ版。 |
非推奨
いままで使いなれた関数で、WP5.3以降、非推奨になったものです。
WPタイムスタンプで日時を取得しない
current_time( 'timestamp' )
get_post_time( 'U' )
基準値を取っているように見えますが、じっさいはタイムゾーンも含みます。
WPタイムスタンプをもとにローカライズしない
date_i18n( DATE_RFC3339, $timestamp + $offset )
タイムゾーンオフセットを足してパラメータに渡す人が多かった? みたいです。
(これをするとタイムゾーン × 2が足される。)
その他
そのほか注意事項があります。
WPタイムスタンプを永続的に保存しないでください。
WPタイムスタンプを比較しないでください。
date_default_timezone_set()でPHPのタイムゾーンを変更しないでください。(正しいコア操作のためのハード要件です!)
(公式ガイドの直訳)
推奨
じゃあどうすりゃいいのか?
代わりの関数がWordPress5.3で追加されました。それを使いましょうってこと。
UnixタイムスタンプまたはDateTimeImmutableオブジェクトで日時を取得する。
PHPでの日時操作はクラスオブジェクトを使うようになっています。WordPressでもやっとそれに合わせました。
time() | UTCの現在日時タイムスタンプを取るならPHPの関数を使う。 |
current_datetime() | current_time()の代わり。 タイムゾーンに合わせた時間を返すのは同じだが、パラメータで色々指定できない。 (パラメータなし。シンプルにした。) |
get_post_datetime() | get_post_time()の代わり。 パラメータ指定を直感的に分かりやすくした。 |
get_post_timestamp() | get_post_datetime()のタイムスタンプ値の取得版。 |
Unixタイムスタンプをもとにローカライズする
wp_date( DATE_RFC3339, $timestamp );
その他
そのほかの注意事項です。
DATE_RFC3339などの正確なUnixタイムスタンプまたは形式を保存する。
UNIXタイムスタンプ、DateTimeInterfaceオブジェクトで比較する。また、同じタイムゾーンの日付の文字列を比較する。
異なるタイムゾーンで操作するときは、DateTimeZone, DateTimeImmutableオブジェクトを使う。(ローカライズにはwp_date()を使う)
新しい関数を使ってみた
非推奨・推奨をつらつらと並べてきましたが何のこっちゃです。じっさいに使いながら見ていきましょう。
統一されたタイムゾーン取得
UNIXタイムスタンプ基準にしたことでタイムゾーンが必要だろうと、新しく取得関数が追加されました。
タイムゾーンを取得する唯一の方法だと公式ガイドは言っています。
(これを使ってくれってこと。)
wp_timezone_string()
return | string PHPタイムゾーン文字列。または±HH:MMオフセット。 |
管理メニューの『設定 -> 一般』のタイムゾーンから文字列を取得します。
echo wp_timezone_string();
Asia/Tokyo
DateTimeZoneクラスを返すものも追加されてます。
wp_timezone()
return | DateTimeZone タイムゾーンオブジェクト。 |
$tz = wp_timezone();
echo $tz->getName() . PHP_EOL;
echo var_dump($tz->getLocation());
Asia/Tokyo
array(4) { ["country_code"]=> string(2) "JP" ["latitude"]=> float(35.65444) ["longitude"]=> float(139.74472) ["comments"]=> string(0) "" }
現在日時取得
current_datetime()
return | DateTimeImmutable 日時オブジェクト |
新旧同時に実行してみましょう。
echo current_time('Y/m/d H:i:s') . PHP_EOL;
echo current_datetime()->format('Y/m/d H:i:s');
2019/12/20 13:24:10
2019/12/20 13:24:10
結果は同じです。ただ、新関数はパラメータ無しでシンプルに使えます。
また、リターン値がオブジェクトなので、フォーマット変換にはオブジェクトのformat()メソッドを使います。
任意の日時取得
date_i18n()が非推奨になってwp_date()が追加されました。
wp_date( $format, $timestamp, $timezone )
format | string 日時書式 |
timestamp | int 任意のタイムスタンプ。 デフォルト: 現在日時 |
timezone | DateTimeZone ローカルのタイムゾーン。 デフォルト: 管理メニュー『設定 -> 一般』で設定されたタイムゾーン |
return | string | false 日時の文字列。エラーのときfalse。 |
echo date_i18n('Y/m/d H:i:s') . PHP_EOL;
echo wp_date('Y/m/d H:i:s');
2019/12/19 17:01:55
2019/12/19 17:01:55
date_i18n()は内部でwp_date()を使っている(レガシーラッパー)ので結果は同じです。
ただwp_date()では、タイムゾーンが指定できます。(デフォルトが設定値なだけ。)
UNIXタイムスタンプを基準にしたのでタイムゾーン設定値を足しません。
公式ガイドでも言っています。
Note that, unlike date_i18n(), this function accepts a true Unix timestamp, not summed with timezone offset.
(date_i18n()とは異なり、この関数はUnixタイムスタンプを受け入れ、タイムゾーンオフセットは加算されないことに注意してください。)
公式ガイドより。筆者訳
と思ったのですが、ここがポイントです。たとえば、このサンプルを見てください。
echo date_i18n('Y/m/d H:i:s P e', strtotime('20151015 13:22:43')) . PHP_EOL;
echo wp_date('Y/m/d H:i:s P e', strtotime('20151015 13:22:43'));
指定日時のフォーマット変換として使ったとします。結果はこうなります。
2015/10/15 13:22:43 +09:00 Asia/Tokyo
2015/10/15 22:22:43 +09:00 Asia/Tokyo
wp_date()の時刻が進んでしまいました。デフォルトのタイムゾーンが足されるからです。
date_i18n() | ローカルタイムのタイムスタンプを指定。 (タイムゾーン含む) |
wp_date() | UTCのタイムスタンプを指定。 (タイムゾーン抜き) |
ここがWordPress5.3の変更点の肝です。非推奨・推奨のところでいろいろな注意点がありましたが、ぜんぶここで解決しましょう。
wp_date()の結果にタイムゾーンを足さないのではなく、タイムスタンプにタイムゾーンを足さないことに注意。
date_i18n()の問題点
今度は、PHP関数date()もいっしょに実行してみましょう。
$format = 'Y/m/d H:i:s P e';
$time = '20151015 13:22:43';
echo date_i18n($format, strtotime($time)) . ' <--- date_i18n()' . PHP_EOL;
echo wp_date($format, strtotime($time)) . ' <--- wp_date()' . PHP_EOL ;
echo date($format, strtotime($time)) . ' <--- date()';
2015/10/15 13:22:43 +09:00 Asia/Tokyo <--- date_i18n()
2015/10/15 22:22:43 +09:00 Asia/Tokyo <--- wp_date()
2015/10/15 13:22:43 +00:00 UTC <--- date()
ひとつだけおかしいです。どれだと思いますか?
答えはdate_i18n()です。PHPにかぎらずプログラミングではタイムスタンプとタイムゾーンは分けて考えます。
(タイムスタンプ + タイムゾーン = 日時)
このなかでは、date_i18n()だけが『指定タイムスタンプがそのままローカル時間』になっています。
ちなみに、date()とwp_date()の結果は同じです。ただ、ロンドン時間と日本時間になっているだけで。
(タイムスタンプは同じでタイムゾーンがちがう。)
これが『WordPressとPHPやシステムの互換性がない』です。
date_default_timezone_set()を使うな!
ここで、サンプルコードを修正しました。
$format = 'Y/m/d H:i:s P e';
$time = '20151015 13:22:43';
echo date_i18n($format, strtotime($time)) . ' <--- date_i18n()' . PHP_EOL;
date_default_timezone_set( 'Asia/Tokyo' );
echo wp_date($format, strtotime($time)) . ' <--- wp_date()' . PHP_EOL ;
echo date($format, strtotime($time)) . ' <--- date()';
2015/10/15 13:22:43 +09:00 Asia/Tokyo <--- date_i18n()
2015/10/15 13:22:43 +09:00 Asia/Tokyo <--- wp_date()
2015/10/15 13:22:43 +09:00 Asia/Tokyo <--- date()
すべて同じになりましたね? でもプログラムとして失敗です。
もし、世界を相手にする企業のシステムを作っていて、ロンドン・ニューヨーク、ドバイ、香港、東京の時間を表示するとしたら?
date_default_timezone_set()を連発しますか?ってなりますし、マルチスレッド処理があって、ほぼ同時に日時を取得したら、別の処理のタイムゾーンまで変わってしまいます。
だから、WordPressの公式ガイドでも『date_default_timezone_set()でタイムゾーンを変更するな!』って言ってるんですね?
WordPressの新しい日時は、『タイムスタンプとタイムゾーンを分ける』が基本です。これを忘れると間違いなくバグが出ます。
新しいWordPress時刻関数のタイムスタンプにタイムゾーンは入れない。
(パラメータの$timestampはUTC。)
WordPressでは、プログラムの最初のセッティングでdate_default_timezone_set('UTC')をコールしています。
基準値を『ローカル抜き』にしているんですね?
これでWordPressは、php.iniやOSのロケール(タイムゾーン)で'Asia/Tokyo'にしても効きません。
WordPressのカスタムで
date_default_timezone_set('Asia/Tokyo')を入れれば日時が直る
というのを見かけますがアウトです。WordPress全体の日時の基準をイジるのでリスクしかありません。
(リスクを分かってやるのは自由ですが。)
PHP公式ドキュメント
非推奨
投稿日時・更新日時取得
get_post_time()で投稿日時を取得していましたが、残念なところがありました。
パラメータがややこしい。
更新日時が取得できない。
WordPressではそれに代わる関数が追加されました。ついでに投稿日時のタイムスタンプ取得関数まで。
get_post_datetime( $post, $field, $source )
post | int | WP_Post 投稿ID or 投稿オブジェクト。 デフォルト: null。グローバル$postを使う。 |
field | string 'date': 投稿日時 'modified': 更新日時。 デフォルト: 'date' |
source | string 'local': ローカルタイム(タイムゾーン含む) 'gmt': UTCタイム(タイムゾーン抜き) デフォルト: 'local' |
return | DateTimeImmutable | false 日時オブジェクト。エラーのときfalse。 |
パラメータなし(get_post_datetime())は、投稿のくり返し(for)処理の中でのみ使用可。
($postはグローバルの$postを使う。更新日時は取得できない。)
get_post_timestamp( $post, $field )
post | 同上。 |
field | 同上。 |
return | int | false UTCタイムスタンプ(タイムゾーン抜き)。エラーのときfalse。 |
パラメータなし(get_post_timestamp())は、投稿のくり返し(for)処理の中でのみ使用可。
($postはグローバルの$postを使う。更新日時は取得できない。)
$format = 'Y/m/d H:i:s P e';
$date = get_post_datetime(13);
$modified = get_post_datetime(13, 'modified');
echo $date->format($format) . ' <--- date' . PHP_EOL;
echo $modified->format($format) . ' <--- modified' . PHP_EOL;
echo PHP_EOL;
$tz = new DateTimeZone('UTC');
$date_ts = get_post_timestamp(13);
$modified_ts = get_post_timestamp(13, 'modified');
echo wp_date($format, $date_ts, $tz) . ' <--- date utc' . PHP_EOL;
echo wp_date($format, $modified_ts, $tz) . ' <--- modified utc' . PHP_EOL;
echo PHP_EOL;
$tz = new DateTimeZone('Australia/Sydney');
echo wp_date($format, $date_ts, $tz) . ' <--- date sydney' . PHP_EOL;
echo wp_date($format, $modified_ts, $tz) . ' <--- modified sydney' . PHP_EOL;
2017/05/26 19:30:42 +09:00 Asia/Tokyo <--- date
2017/07/09 16:01:18 +09:00 Asia/Tokyo <--- modified
2017/05/26 10:30:42 +00:00 UTC <--- date utc
2017/07/09 07:01:18 +00:00 UTC <--- modified utc
2017/05/26 20:30:42 +10:00 Australia/Sydney <--- date sydney
2017/07/09 17:01:18 +10:00 Australia/Sydney <--- modified sydney
投稿日時をかんたんに各国の現地日時に変更できます。記事の更新日時もかんたんに取れます。
WordPress.orgリファレンス
PHP公式ドキュメント
非推奨
WordPress日時の仕様変更でやりたいことはこれ!
ここまで、非推奨だ、推奨だ、なんだと言ってきましたが、これからのWordPressの日時でやりたいことは、たったこれだけです。
PHPの時刻の扱い方に合わせる。
PHP5.2から、日時はクラスオブジェクトで扱うようになりました。
このオブジェクトは、タイムゾーンとタイムスタンプを分けて管理して、いろいろな変更ができるようになっています。
WordPressタイムスタンプだと、タイムゾーンを含めて管理するのでどうしても扱いづらくなっていました。
(他地域の時刻に変換するとか。)
PHP5.2は2011年リリースなので、いまPHPでプログラミングしている人は当たり前のようにクラスオブジェクトを使っているでしょう。
むしろ新しいWordPressの日時の考え方のほうがやりやすくなるはずです。
WPでも時刻はクラスオブジェクトを使う。
(PHPと同じ。)