给卡帕多奇亚民宿做预订页,我把时区算错了一整天

卡帕多奇亚那家民宿的老板是去年在格雷梅镇上认识的,今年春天他让我帮他做个小预订页,说 Booking 抽成太狠。我接了这个外包小单,前端用 Vite + React,后端直接挂在他已经有的 WordPress 上,用 REST API 拉房态。环境很土:Node 18,Vite 4.5,部署在一个每月三美元的共享主机上,没有 CI,靠 FTP 传 dist。

坑出在日期上。房态接口返回的是 checkin_date 字符串,格式 2025-04-18,我直接 new Date(str) 再跟用户选的日期比大小。伊斯坦布尔是 UTC+3,卡帕多奇亚同属一个时区,但浏览器在别的时区打开就出事了。一个德国游客选 4 月 18 日入住,页面显示「已满」,老板那边看却是空的。

我改成只比日期字符串,不碰 Date 对象:

const toKey = (d) =>
  `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;

const isFree = (room, picked) =>
  !room.booked.some((b) => b.checkin_date <= toKey(picked) && toKey(picked) < b.checkout_date);

入住当天算占用,退房当天不算,这个半开区间是跟老板对过的:他早上十点前要打扫,下午两点才能进新房客。

为什么不用 Date 做比较?因为 new Date('2025-04-18') 会被当成 UTC 零点解析,在 UTC-5 的浏览器里就变成 4 月 17 日晚上七点,日期直接滑一天。改成字符串比较之后,时区完全不参与,代价是我失去了「按小时算半天房」的能力——老板本来也不做这个,他只有整晚。

失效的地方我也踩过:如果以后要加「连住三晚打折」,字符串比较就不够了,得先统一成当地日期的序数;另外土耳其 2025 年没有夏令时切换,但如果有,toKey 只取本地年月日,跨时区用户选的「今天」可能跟老板的「今天」差一天。我在页面上加了一行小字,显示「以卡帕多奇亚当地时间为准」,并直接调 Intl.DateTimeFormat('tr-TR', { timeZone: 'Europe/Istanbul' }) 给用户看当前当地日期。

还有一次是我自己挖的坑:测试时把系统时区改成 America/New_York,发现房态列表整个错位,以为是接口坏了,查了两小时才发现是 Date 的锅。啊这,救命。

现在页面能跑,但老板问我能不能加「提前七天取消免费」的规则。你们做这种跨时区日期比较时,是统一转成 UTC 序数,还是干脆全程用字符串?我想听听别人的做法。

0
赞同度 100% · 0 条评论 · 4 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。