如果你曾发布过一篇文章,结果在前端网站上看到它被标记为“57 年前”,而你的控制面板却淡定地显示它是过了整点十分钟发布的,那么你一定体会过这种 Bug 带来的挫败感。它不会破坏布局,也不会触发致命错误。它只是让你的内容瞬间“老”了半个世纪,而且盯着你的主题文件看也无法让这个数字发生变化。

这个问题往往能躲过常规检查。你使用 grep 搜索 single.phparchive.phpfunctions.php,试图寻找错误的 get_the_date() 调用。一切看起来都很标准。你刷新前端页面,1968 年的幽灵依然徘徊在你的署名栏中。到那时,问题几乎与你的模板无关,而完全在于时间是如何在 WordPress 技术栈中传递的。

为什么总是出现这个特定的数字

“57 年前”这个数字并非随机生成的。WordPress 主题通常通过 human_time_diff() 函数显示相对时间,该函数将文章的时间戳与当前时刻进行比较,并打印出类似“2 小时前”的友好短语。为了使该计算生效,PHP 需要一个有效的 Unix 时间戳——即自 1970 年 1 月 1 日(Unix 纪元)以来的秒数。

当时间戳在到达计算环节之前被剥离或损坏时,该函数经常会将零(或类似的无效起始点)与当前时间进行比较。结果就是产生了一个跨度约为五个半十年(55 年)的时间差,并随着时间推移逐年增加。这是时间值缺失或读取错误的一种症状,而不是你的数据库里真的存满了复古博客文章。

控制面板的误导

这个 Bug 最令人沮丧的地方之一在于管理面板的“人格分裂”。在 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 级别的偏移可能会导致竞态条件,即 WordPress 核心认为时间是本地时间,而 PHP 认为它是通用时间。当这些偏移在时间戳转换过程中发生冲突时,返回给模板的结果可能会归零。

服务器时区配置

你的 MySQL 服务器有其自身的本地时间概念。WordPress 为每篇文章写入两个日期:站点本地时间的 post_date 和通用时间的 post_date_gmt。如果数据库的全局 time_zone 发生了变化——例如在迁移后从 SYSTEM 变为 +00:00——PHP 的 strtotime() 可能无法将存储的字符串与预期的值进行协调。数据库中仍然存储着 2024-03-15 14:30:00,但其周围的上下文发生了变化,导致 PHP 解析出的输出变为 falsenull

实际案例:托管商将你的数据库从一个运行 MySQL 5.7 且使用 SYSTEM 时间的节点,迁移到了一个运行 MariaDB 10.11 且设置为严格 UTC 的集群。你的主题没有变,你的 WordPress 设置也没有变。然而,get_the_time( 'U' )(Unix 时间戳格式)突然对某些文章返回了空值,导致相对时间函数回退到基于纪元零点的计算。解决方法是使应用程序时区、数据库时区和操作系统时钟保持一致,这样 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