JS字符串时间比较问题:时区偏移导致日期差值计算错误
问题描述
我开发了一款验证应用,流程是用户拍摄自拍后通过HTML表单上传至Node.js+Express.js+Formidable.js服务器,其中一项核心检测要求:自拍拍摄时间与上传时间的间隔不得超过120秒。
当前遇到的问题:
- 从照片EXIF元数据中获取的拍摄时间格式为
2023:09:03 16:48:55,这是设备本地时间 - 上传时间通过表单隐藏字段
lt在提交时设置为new Date(),返回格式为Sun Sep 03 2023 16:49:12 GMT+1000 (Australian Eastern Standard Time)(用户使用同一设备拍摄并上传) - 计算时间差时,预期差值约17秒,但实际得到约-600分钟的错误结果,根源是时区偏移导致的时间解析不一致
现有计算代码:
function parseDate(s) { var b = s.split(/\D/); return new Date(b[0], b[1] - 1, b[2], b[3], b[4], b[5]); } console.log(exifData.exif.CreateDate); CreateDate = parseDate(exifData.exif.CreateDate); console.log(CreateDate); console.log(fields.lt[0]); submitDate = new Date(fields.lt[0]); console.log(submitDate); age = (submitDate - CreateDate) / (1000 * 60); console.log(age); /* 输出结果: 2023:09:03 16:48:55 2023-09-03T16:48:55.000Z Sun Sep 03 2023 16:49:12 GMT+1000 (Australian Eastern Standard Time) 2023-09-03T06:49:12.000Z -599.7166666666667 */
解决方案
核心问题是EXIF时间解析时未匹配设备时区:EXIF的CreateDate是设备本地时间,但现有代码解析时被默认当作UTC时间;而上传时间的Date对象是带时区的本地时间,两者时区不统一导致差值计算错误。
推荐以下两种可靠解决方法:
方法1:用时间戳传递上传时间(最简洁可靠)
直接在前端传递UTC时间戳,避免字符串解析的时区歧义:
前端修改表单提交事件
<form action="/selfie" enctype="multipart/form-data" method="post" name="fselfie"> <input type="file" name="selfie" multiple="false" /> <input type="hidden" name="lt" id="lt" value="" /> <!--ADD FIELDS--> <input type="submit" value="Upload" onclick="document.fselfie.lt.value = Date.now();" /> </form>
Date.now()返回的是UTC时间的毫秒级时间戳,无时区问题。
后端计算时间差
// 解析上传时间戳为Date对象 const submitDate = new Date(Number(fields.lt[0])); // 解析EXIF本地时间为对应时区的Date对象 function parseLocalExifDate(s) { const [dateStr, timeStr] = s.split(' '); const [year, month, day] = dateStr.split(':').map(Number); const [hour, minute, second] = timeStr.split(':').map(Number); // 传入年/月/日/时/分/秒参数时,Date构造函数默认使用本地时区 return new Date(year, month - 1, day, hour, minute, second); } const createDate = parseLocalExifDate(exifData.exif.CreateDate); // 计算秒数差(取绝对值避免顺序问题) const diffSeconds = Math.abs(submitDate - createDate) / 1000; // 验证是否符合要求 if (diffSeconds > 120) { console.log("自拍拍摄时间超过120秒限制"); } else { console.log("自拍时间符合要求"); }
方法2:统一转换为UTC时间计算
如果必须保留字符串传递上传时间,可以通过时区偏移将EXIF时间转换为UTC时间,再与上传时间的UTC值对比:
// 解析上传时间 const submitDate = new Date(fields.lt[0]); // 获取设备时区偏移(单位:分钟,UTC - 本地时间) const timeZoneOffset = submitDate.getTimezoneOffset(); // 解析EXIF本地时间并转换为UTC时间 function parseExifToUTC(s, offsetMinutes) { const [dateStr, timeStr] = s.split(' '); const [year, month, day] = dateStr.split(':').map(Number); const [hour, minute, second] = timeStr.split(':').map(Number); // 先创建本地时间的Date对象 const localDate = new Date(year, month - 1, day, hour, minute, second); // 转换为UTC时间戳:本地时间戳 - 时区偏移对应的毫秒数 const utcTimestamp = localDate.getTime() + (offsetMinutes * 60 * 1000); return new Date(utcTimestamp); } const createDateUTC = parseExifToUTC(exifData.exif.CreateDate, timeZoneOffset); const submitDateUTC = new Date(submitDate.toUTCString()); // 计算秒数差 const diffSeconds = Math.abs(submitDateUTC - createDateUTC) / 1000;
内容的提问来源于stack exchange,提问作者Mark Andrew
相关产品推荐
相关产品推荐

