ERP按北京时间发送special_from_date/special_to_date,后台看到的日期或生效时刻却偏了8小时。日期字段在API、数据库UTC和Store时区之间转换,带不带时区的字符串混用最容易出错。

现象

我保留了一个失败样本,并用同样输入在无缓存或单节点条件下再跑一遍。这样可以把前端假象、缓存和真正的数据问题分开。

排除法

  • 记录ERP原始payload、API返回值和数据库值,三者分开比较。
  • 确认接口字段接受的是日期还是完整时间,不能想当然加Z。
bin/magento config:show general/locale/timezone
mysql -e "SELECT sku,special_price,special_from_date,special_to_date FROM catalog_product_entity_decimal d JOIN catalog_product_entity p ON p.entity_id=d.entity_id LIMIT 1;"
php -r '$d=new DateTimeImmutable("2026-06-23 00:00:00",new DateTimeZone("Asia/Shanghai")); echo $d->setTimezone(new DateTimeZone("UTC"))->format(DateTimeInterface::ATOM);'

这里的重点不是把报错暂时消掉,而是让同一操作再执行一次仍保持幂等;因此修复后一定要做第二次重复执行测试。

最终处理

在ERP适配层明确业务时区,再转换成接口约定格式;纯日期促销可统一在Store时区的00:00:00开始、23:59:59结束后转UTC。不要在Magento插件里对所有日期再无条件减8小时。修复后重新写入受影响SKU并重建价格索引。

修改范围

  • 先在单个SKU、单个订单或单个Store验证。
  • 保留可回退的旧配置,不覆盖原始证据。
  • 需要批量处理时分批提交并输出成功/失败清单。

检查收尾

  • 边界前一分钟未生效、到点生效
  • 后台显示日期与业务约定一致
  • API读回后转换可逆
  • 夏令时站点也用各自时区测试

验收不只看后台提示“成功”。我还会从前台/API再走一次真实链路,并在下一轮cron、索引或缓存命中后重复检查,确认不是短暂正常。

经验

数据库查询示例需按实际EAV attribute_id定位,别把其他decimal属性误当special_price。