tzOffset 把非法时区文本算成有效偏移
@date-fns/tz 1.5.0 的 tzOffset 手动回退会接受带垃圾的偏移文本。按固定偏移和 IANA 时区分别校验,拒绝前后缀与超范围分量,保留真实时区规则。
传入 prefix+02:30,tzOffset() 居然返回 150;+24:00、+12:60 这样的文本也可能被算成偏移分钟数。若使用 @date-fns/tz 1.5.0,不能把“返回了有限数字”当成时区文本已经合法。
该函数先尝试 Intl.DateTimeFormat,失败后进入手动偏移解析。1.5.0 的 fallback 正则没有要求匹配整个字符串,也没有限制小时和分钟范围,于是前后带垃圾的文本仍可能截取到 +02:30。这是输入校验边界,不是给所有时间戳统一补八小时的问题。
将固定偏移与命名时区分开接收
接口如果只允许固定偏移,可以先验证完整字符串。以下函数保留该库文档采用的三种形态:±HH、±HHMM、±HH:MM;这里不接受无符号数字或其他时区别名。
function fixedOffsetMinutes(zone) {
if (typeof zone !== 'string') {
throw new TypeError('固定偏移必须是字符串');
}
const match = /^([+-])([01]\d|2[0-3])(?::?([0-5]\d))?$/.exec(zone);
if (!match || match[0] !== zone) {
throw new RangeError('固定偏移格式无效');
}
const magnitude = Number(match[2]) * 60 + Number(match[3] ?? 0);
return match[1] === '-' ? -magnitude : magnitude;
}
额外比较 match[0] 与完整输入,可以拒绝正则美元锚点之前的末尾换行;这里不先 trim 掉未知字符。严格匹配后独立计算分钟数,不再把文本交回宽松的 fallback。符号与小时数字分开读取,-00:30 得到 -30;1.5.0 的 calcOffset 还会在这个合法输入上丢失负号,因此只在外面加正则、里面仍调用原函数是不够的。零分钟可能返回负零,应用若要求统一表示,可在自己的序列化边界归一为 0。业务只允许更窄的偏移集合时,应继续收紧。
IANA 名称如 Asia/Shanghai 不属于这条固定偏移入口。需要同时支持时区名称时,使用单独字段或明确分支,并通过运行环境支持的 Intl 时区规则验证,不要把合法名称交给上述正则后统一拒绝。运行环境缺少所需时区数据时,也不能从字符串里随便提取一段数字作为替代。
不在原值上裁剪出一个看似正确的时区
不要先去掉未知前缀、截取最后六个字符,或只判断输入里包含加减号。那会把调用方错误变成另一个可接受的偏移,后续排期和日志日期可能继续偏移却不报错。
命名时区随日期可能采用不同夏令时偏移,固定偏移没有这种规则。将 America/New_York 一次算出的分钟数长期保存成时区配置,会丢失以后的变化;修复非法文本入口时应保留原来的数据模型。
截至 2026 年 10 月 26 日,正式 1.5.0 的手动回退仍有上述宽松匹配。验收应在调用前拒绝前后垃圾、孤立冒号及超范围分量,同时覆盖三种允许格式、正负偏移和零;IANA 分支另测命名时区及所需日期,不把一个有效返回值等同于全部时区语义已经正确。


