
本文详解如何安全、准确地将基于 year×365 + day-of-year 编码的整数(如 7759)逆向解析为标准 DateTime 对象,重点解决 DateTime::createFromFormat('y-z', ...) 返回 false 的根本原因及可靠修复方案。
本文详解如何安全、准确地将基于 `year×365 + day-of-year` 编码的整数(如 7759)逆向解析为标准 datetime 对象,重点解决 `datetime::createfromformat('y-z', ...)` 返回 `false` 的根本原因及可靠修复方案。
在旧系统中,开发者常采用 $year = date("y") * 365 + date("z") 的方式将日期压缩为整数存储(例如 2021-04-14 → 21 * 365 + 104 = 7769)。但这种编码存在两个关键缺陷,直接导致 DateTime::createFromFormat('y-z', '21-104') 失败或结果错误:
? 问题根源分析
"y" 格式仅表示两位年份(00–99),无法唯一映射到四位年份
DateTime::createFromFormat('y-z', '21-104') 会将 '21' 解释为 1921 或 2021(取决于 PHP 的年份推断规则),而 104 作为 z(年中第几天)在 1921 年是合法的(1921 年 4 月 14 日),但在 2021 年也成立——但 PHP 默认可能选择非预期年份,且更严重的是:z 的取值范围是 0–365,而 104 是合法值;但 94 和 104 都合法,为何一个成功一个失败?
真正原因是:当 z = 104 时,若 PHP 尝试将其解析为 1921-104(即 1921 年第 104 天 → 1921-04-14),这本身没问题;但若内部解析逻辑因时区/默认时间叠加导致边界溢出(如尝试 1921-01-01 + 103 days 超出范围),或 z 值被误判为无效(某些 PHP 版本对高位 z 处理不一致),就会返回 false。未使用重置格式符 !,导致时间部分污染解析结果
createFromFormat('y-z', ...) 默认保留当前系统时间(如 14:35:22),而非归零为 00:00:00。这在计算过期时间时会引入最多 24 小时误差。365 作为年因子不严谨——忽略闰年
2020 是闰年(366 天),但 date("y") * 365 强制按 365 天计算,导致 2020-12-31(z=365)被编码为 20*365+365 = 7665,而逆向解析时 7665 % 365 = 365 → z=365,但 z=365 在非闰年无效(最大为 364),从而触发解析失败。
✅ 正确解决方案:使用足够大的唯一因子 + 强制重置格式
推荐改用 1000 作为年因子(远大于 366,确保 z 部分永不溢出),并强制使用 ! 重置时间:
// 原始编码(建议未来迁移时采用)
$expire = (int)(date('y') * 1000) + (int)date('z') + $duration;
// 安全逆向解析(关键:使用 '!y-z' 和因子 1000)
$base = $expire - $duration; // 先减去 duration 还原原始编码值
$yearPart = (int)($base / 1000);
$dayOfYear = $base % 1000; // 保证 0–999,完全覆盖 z(0–365)
$date = DateTime::createFromFormat('!y-z', $yearPart . '-' . $dayOfYear);
if (!$date) {
throw new InvalidArgumentException("Invalid encoded date: {$base}");
}
// $date 现在精确对应原始日期的 00:00:00(UTC 时间)
⚠️ 注意事项:
- ! 符号至关重要——它将所有未指定的时间组件(时、分、秒、微秒、时区)重置为 0,避免时间偏移。
- 因子 1000 确保 z 部分(% 1000)始终在 0–999 范围内,而 z 实际只需 0–365,因此绝无冲突。
- 若需兼容四位年份(如区分 1921/2021),应彻底重构为 Y(四位年)+ z,并用 !Y-z 格式,但需修改历史数据编码逻辑。
? 总结
不要依赖 365 因子和 y-z 格式逆向解析旧数据。唯一可靠的方式是:采用 ≥366 的安全因子(如 1000),配合 !y-z 格式强制时间归零,并在解析前验证 DateTime 实例是否创建成功。 此方案消除闰年歧义、规避 z 值越界风险,并确保时间精度,是处理此类遗留编码的工业级实践。











