投稿を公開した直後、公開サイトでは「57年前」と表示されているのに、ダッシュボードでは数分前に公開されたと平然と表示されている……そんな経験があれば、このバグがどれほど厄介なものか理解できるはずです。レイアウトが崩れるわけでも、致命的なエラーが発生するわけでもありません。ただ、コンテンツが半世紀も古くなってしまうだけです。テーマファイルをいくら調べても、その数字が変わることはありません。

この問題は、通常のチェックをすり抜けて生き残る傾向があります。single.phparchive.phpfunctions.phpgrep して、壊れた get_the_date() の呼び出しを探しても、すべては標準的に見えます。公開ページをリロードしても、1968年の亡霊が依然として署名(byline)に付きまとっています。その段階になると、問題はテンプレートとはほとんど関係がなく、WordPressのスタック内を時間がどのように流れているかという点に集約されます。

なぜその特定の数字が表示され続けるのか

「57年前」という数字はランダムに表示されているわけではありません。WordPressのテーマは多くの場合、human_time_diff() 関数を使用して相対的な時間を表示します。この関数は、投稿のタイムスタンプと現在の時刻を比較し、「2時間前」のような分かりやすいフレーズを出力します。この計算を成立させるには、PHPが有効なUnixタイムスタンプ(Unixエポックである1970年1月1日からの経過秒数)を必要とします。

計算に到達する前にタイムスタンプが削除されたり破損したりすると、関数は現在の時刻とゼロ(あるいは同様に無効な開始点)を比較することになります。その結果、約55年分という、カレンダー上で1年ずつ遡っていくような期間が算出されてしまうのです。これは、データベースがレトロなブログ記事で埋め尽くされているわけではなく、時間の値が欠落しているか、誤って読み取られていることによる症状なのです。

ダッシュボードの欺瞞

このバグの最も苛立たしい側面の一つは、管理パネルの「二重人格」的な挙動です。wp-admin内の投稿一覧では、正しい公開日が表示されます。これは、WordPressがMySQLの wp_posts テーブルから直接データを取得し、相対時間フィルターを通さずにサーバー側でフォーマットすることが多いためです。しかし、フロントエンドでは、human_time_diff() を呼び出すテーマやプラグインに依存している場合があり、あるいはデータベースの生の値が、バックエンドではバイパスされるPHPのタイムゾーン変換チェーンを通過している場合があります。

つまり、データ自体は通常、無事なのです。問題は、データベースからレンダリングされたHTMLに至るまでのパイプラインに、ねじれが生じていることにあります。

PHPのバージョンとタイムゾーン設定

まずはPHPを確認してください。ホスティングパネルでのバージョン変更は無害に見えますが、PHPはメジャーリリースごとに時間のオブジェクトを異なる方法で処理します。レガシーなPHP 7.4のコードを、突然PHP 8.1の環境で実行すると、空の文字列や不正な形式の文字列に対して DateTime オブジェクトが初期化される際の挙動に、微妙な変化が生じることがあります。もし php.ini ファイルで date.timezone が未定義の場合、PHPはデフォルトでUTCを使用し、サーバーの設定が最適であると想定します。しかし、WordPressはそれとは異なる判断を下すかもしれません。

wp-config.php の中に、タイムゾーンの宣言がハードコードされていないか確認してください。date_default_timezone_set( 'Asia/Kolkata' ); のような行は一見便利に思えますが、WordPressは 設定 > 一般 で設定された値を通じて、独自の時計を管理することを期待しています。PHPレベルでオフセットを強制すると、コアは時間をローカルだと信じ、PHPはそれがユニバーサル(UTC)だと信じるという、レースコンディション(競合状態)が発生する可能性があります。タイムスタンプの変換中にこれらのオフセットが衝突すると、テンプレートに返される結果がゼロに崩壊してしまうことがあります。

サーバーのタイムゾーン設定

MySQLサーバーも独自のローカルタイムを持っています。WordPressはすべての投稿に対して2つの日付を書き込みます。サイトのローカルタイムでの post_date と、協定世界時(UTC)での post_date_gmt です。もしデータベースのグローバルな time_zone が、例えば移行後に SYSTEM から +00:00 に切り替わった場合、PHPの strtotime() は、保存された文字列と期待される値を一致させることができなくなる可能性があります。データベースには依然として 2024-03-15 14:30:00 と保存されていますが、その周囲のコンテキストが変化するため、PHPでの解析結果が false または null になってしまいます。

実例を挙げると、マネージドホストがデータベースを、SYSTEM タイムゾーンを使用しているMySQL 5.7のノードから、厳格なUTCに設定されたMariaDB 10.11のクラスターへ移動させた場合などが考えられます。テーマもWordPressの設定も変更していません。それなのに、一部の投稿に対して Unixタイムスタンプ形式の get_the_time( 'U' ) が突然空の値を返し、相対時間関数がエポックゼロの計算にフォールバックしてしまうのです。解決策は、アプリケーションのタイムゾーン、データベースのタイムゾーン、そしてオペレーティングシステムの時計を一致させ、WordPressがどの基準を使用すべきか迷わなくて済むようにすることです。

データベースのタイムスタンプ形式

WordPress core stores post dates in MySQL DATETIME columns, not TIMESTAMP fields. That distinction matters because DATETIME holds a calendar value without_zone awareness in older MySQL versions, whereas TIMESTAMP converts everything to UTC behind the scenes. If a plugin or migration tool has altered your wp_posts schema, or if an old backup restored invalid zero dates into the table, WordPress may read a value like 0000-00-00 00:00:00 and quietly hand it off to your theme.

Modern MySQL modes, particularly NO_ZERO_DATE and STRICT_TRANS_TABLES, reject those zero placeholders. If a backup script inserted them during a migration, the database might have truncated or nulled them on import. You can verify this quickly with a direct SQL query:

SELECT ID, post_title, post_date, post_date_gmt 
FROM wp_posts 
WHERE post_status = 'publish' 
ORDER BY post_date DESC 
LIMIT 10;

If the raw columns look correct but the site still displays ancient history, the corruption is happening after the query. If the columns themselves contain zeroes, you have found the leak in the plumbing.

Plugin Conflicts with Date Functions

Plugins that filter date output are common culprits. Multilingual tools such as WPML or Polylang hook into get_the_date to translate month names. Caching plugins intercept the final HTML. Either layer can accidentally pass an unformatted string into a function expecting a Unix integer.

A translation plugin might replace “March 15, 2024” with its localized equivalent, but if a theme then pipes that localized string into strtotime() inside a custom function, the parser fails on non-English characters and returns false. That false value hits human_time_diff(), which compares zero against now, producing the infamous half-century gap.

Object cache layers complicate this further. Redis or Memcached can store a serialized WP_Post object from a request that briefly failed to parse the date. Every subsequent visitor receives that corrupted object straight from RAM, bypassing the database entirely. The issue persists on the live site because your production stack has cache infrastructure that your local install lacks.

A Practical Debugging Checklist

Because the symptom hides deep in the stack, you need to peel layers systematically until the dates snap back into place.

  1. Query the database directly. Open phpMyAdmin or run WP-CLI and inspect the raw post_date values. If they are correct, the database is not the problem.
  2. Switch to a default theme. Activate Twenty Twenty-Four or Twenty Twenty-Three. If the bug disappears, audit your active theme and child theme for custom functions wrapping time output.
  3. Silence the plugins. Rename the /wp-content/plugins folder temporarily, or disable all plugins from the dashboard. Re-enable them one by one, checking the frontend after each activation.
  4. Read the error log. Look for timezone warnings, DateTime errors, or strtotime() complaints. They often point to the exact