最近在做一个独立开发的小项目,需要处理一个很常见的需求:时区转换。这个需求听起来简单,但真正动手做的时候才发现,坑比想象中多得多。
为什么会需要这个东西
事情是这样的。我平时会接一些海外的外包项目,团队分布在纽约、伦敦、新加坡和北京。每次约会议或者定 deadline,都要在脑子里算一遍时差,算完还要跟对方确认:
"你说的是美国东部时间下午 3 点,对吧?不是北京时间吧?"
这种沟通成本真的很高。我试过在微信里搜"时区转换",出来的小程序要么要授权,要么广告满天飞。也用过一些在线工具,但要么界面太丑,要么功能太复杂------我就想输入一个时间,看到几个主要城市的对应时间,仅此而已。
后来我干脆想,自己写一个算了。反正我平时也做独立开发,自己的工具自己造,这不比到处找免费的好用?
用 Intl.DateTimeFormat 还是手动算偏移?
这是第一个技术选型的问题。老实说,一开始我差点就走错路了。
我的第一反应是:时区转换不就是加减几个小时吗?比如北京时间 UTC+8,纽约 UTC-5,那不就是相差 13 个小时吗?
但是 ,这个思路有个致命的问题:夏令时(DST)。纽约在夏季会变成 UTC-4,冬季才是 UTC-5。如果你手动维护一个 UTC 偏移表,每年夏令时切换的时候都要手动更新,而且不同国家的切换规则还完全不一样------美国是 3 月第二个周日,欧洲是 3 月最后一个周日,澳大利亚是 10 月第一个周日......这要是手动维护,等着从入门到放弃吧。
好在 JavaScript 早就内置了解决方案:Intl.DateTimeFormat 配合 timeZone 参数,可以直接把时间格式化到指定时区,完全不需要我们手动算偏移。浏览器会根据 IANA 时区数据库自动处理夏令时。
这个方案的好处是:
- 零依赖,纯 vanilla JS 就能搞定
- 自动处理 DST,永远不用担心夏令时切换的问题
- 准确性有保证,IANA 时区数据库是全世界最权威的
所以我的技术方案就定了:用 Intl.DateTimeFormat,绝不手动算偏移。
和 AI 结对编程的日常
接下来就是实现环节了。作为一个独立开发者,我平时写工具类的小项目都会用 AI 辅助。这次也不例外。
第一次对话:给 AI 描述需求
我用的提示词大概是这样的:
帮我写一个时区转换器,单文件 HTML,纯前端。功能包括:输入源时间和源时区,显示其他时区的对应时间。支持添加/移除参考时区。需要用 Intl.DateTimeFormat 实现,不要手动算偏移。
AI 很快就给了一版代码。第一版看起来挺完整的,有界面、有交互、有 i18n。但我发现了一个核心问题。
第一个坑:格式化出来的时间怎么解析?
AI 的第一版代码里,有个地方让我眉头一皱。它用 Intl.DateTimeFormat 格式化时间后,用正则去解析结果字符串:
javascript
// 错误示范:用正则解析格式化结果
var formatted = fmt.format(date);
var match = formatted.match(/(\d{2})\/(\d{2})\/(\d{4})/);
这个方案有个致命问题:Intl.DateTimeFormat 的输出格式和 locale 有关 。在 en-US 下是 MM/DD/YYYY,在 zh-CN 下是 YYYY/MM/DD,在某些欧洲语言下又是 DD.MM.YYYY。用正则去解析,换个语言环境就挂了。
我当时的想法是:这不是个 bug,这是个定时炸弹。所以我让 AI 改用 formatToParts() 方法,这个方法会返回一个结构化数组,把年月日时分秒拆开,不会受 locale 影响:
javascript
// 正确做法:用 formatToParts 获取结构化数据
var fmt = new Intl.DateTimeFormat('en-US', {
timeZone: tz,
hour12: false,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
var obj = {};
fmt.formatToParts(date).forEach(function(p) {
if (p.type !== 'literal') obj[p.type] = p.value;
});
这里有个小细节:hour12: false 在某些引擎上可能会输出 24:00:00 表示午夜,需要特殊处理一下:
javascript
var hh = obj.hour === '24' ? '00' : obj.hour;
第二个坑:怎么判断一个时区现在是否处于夏令时?
AI 第一版代码里,判断 DST 的方式是查一个硬编码的表格。这当然不行------DST 规则每年都在变,硬编码迟早会出错。
我让 AI 改用比较法:取 1 月和 7 月的 UTC 偏移量,如果不同,说明这个时区有夏令时;然后看当前偏移量是否等于较大的那个(即夏季偏移量),如果是,说明现在正处于夏令时。
javascript
function isDST(tz, date) {
var jan = new Date(date.getFullYear(), 0, 1, 12, 0, 0);
var jul = new Date(date.getFullYear(), 6, 1, 12, 0, 0);
var janOff = getOffset(tz, jan);
var julOff = getOffset(tz, jul);
var cur = getOffset(tz, date);
if (janOff !== julOff) {
return cur === Math.max(janOff, julOff);
}
return false;
}
这个方法有个小小的边界情况:南半球的时区 (比如悉尼)夏令时是在 1 月,所以判断逻辑要取 Math.max 而不是固定取 7 月。AI 第一版代码就踩了这个坑------它只判断了北半球的情况,悉尼的 DST 标记永远不对。我指出来之后,AI 改成了 Math.max(janOff, julOff),这才算对。
第三个坑:反向转换的 DST 边界问题
这是最隐蔽的一个坑。我们要做的是:输入一个"墙上时间"(wall clock time)和源时区,找到对应的 UTC 时间戳。
一开始 AI 的做法是:直接把输入的时间当作 UTC 时间,然后算出源时区的偏移量,再减去这个偏移量。这在大部分情况下是对的,但在 DST 切换的边界时刻会出错。
举个例子:假设源时区是纽约,用户在 3 月第二个周日凌晨 1:30 输入了一个时间。这个时间在 DST 切换的前后,偏移量可能不同(UTC-5 → UTC-4)。如果你用错误的偏移量去算,得到的结果可能差一个小时。
正确的做法是:先假设一个偏移量,算出一个近似的 UTC 时间,再验证偏移量是否在目标时刻发生了变化。如果变了,用新的偏移量重新计算:
javascript
// 先假设输入时间是 UTC,计算近似偏移量
var approxUTC = Date.UTC(y, mo, d, h, mi, s);
var srcOff = getOffset(srcTz, new Date(approxUTC));
// 用近似偏移量修正 UTC 时间
var utc = approxUTC - srcOff * 60000;
// 验证偏移量是否在目标时刻发生变化
var srcOff2 = getOffset(srcTz, new Date(utc));
if (srcOff2 !== srcOff) {
utc = approxUTC - srcOff2 * 60000;
}
这个迭代过程最多两次就能收敛,因为 DST 切换在一年中只发生两次,不会出现连续多次偏移量变化的情况。
踩坑记录:格式化输出的 locale 陷阱
还有一个让我印象深刻的坑。最初我用 Intl.DateTimeFormat('zh-CN') 来格式化时间,结果输出的格式是 2024/01/15 14:30:00。然后我用正则去解析这个字符串,一切正常。
但当我切换到 en-US 时,输出变成了 01/15/2024, 02:30:00 PM。正则直接挂了。
这个问题让我意识到:永远不要用正则去解析 Intl.DateTimeFormat 的输出 。一定要用 formatToParts() 获取结构化数据。这个教训值一个"CV 工程师狂喜"的表情包。
最终效果
经过几轮和 AI 的来回调优,最终的工具长这样:
- 输入任意时间 + 选择源时区,自动显示 6 个默认参考时区的对应时间
- 支持添加/移除参考时区(内置 25 个常用时区)
- 每个时区显示当前 UTC 偏移量(如 UTC+8)
- 正在使用夏令时的时区会标记
DST徽章 - 一键填入当前时间
- 支持中英文切换,自动跟随浏览器语言
- 深色模式自动适配
核心代码不到 300 行,全部内联在一个 HTML 文件里,没有任何外部依赖。
关于 AI 辅助编程的一些感受
这次开发过程中,AI 确实帮我省了不少时间。但我也发现了一些规律:
AI 擅长的是:把需求翻译成代码、搭骨架、处理常规逻辑。比如这个工具的整体框架、i18n 机制、UI 布局,AI 一次就写得差不多。
AI 不擅长的是 :处理边界情况和隐含约束。比如 DST 边界时刻的偏移量迭代修正、南半球的夏令时判断、formatToParts 比正则更可靠------这些都是需要"领域知识"才能发现的坑。
所以我的工作流变成了:让 AI 写 80% 的代码,我来补那 20% 的关键逻辑。这比从零开始写快多了,也比完全依赖 AI 靠谱多了。
适用场景
这个工具适合这些场景:
- 跨时区团队协作:约会议、定 deadline 的时候,一眼看到所有人的当地时间
- 海外旅行规划:出发前看看目的地现在是几点,方便倒时差
- 远程办公:和不同时区的同事对工作时间表
- 开发排障:看日志时间戳的时候,快速换算到本地时间
如果你也有类似的时区换算需求,可以直接在线体验:craftvo.app/zh/tool/tim...
反正我的团队现在都用这个,没人再问"你说的下午 3 点是哪个时区的下午 3 点"了。