问题背景在小程序开发中日期处理是一个看似简单却暗藏陷阱的领域。很多开发者会遇到一个奇怪的现象同样的代码在开发工具和安卓机上运行正常但在 iOS 设备上却出现异常。最近在开发学习培训功能时就遇到了这个问题学习日历接口明明返回了数据但页面始终显示本月暂无学习计划。经过排查发现问题出在日期解析上。问题现象开发工具 / 安卓 ┌─────────────────────────────────────┐ │ 12月学习计划 │ │ ┌─────────────────────────────────┐ │ │ │ 12-04 安全培训课程 │ │ │ │ 12-10 消防知识培训 │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────┘ iOS 设备 ┌─────────────────────────────────────┐ │ 12月学习计划 │ │ │ │ 本月暂无学习计划 │ │ │ └─────────────────────────────────────┘接口返回的数据完全相同但 iOS 上就是不显示。问题根因JavaScript 日期解析的浏览器差异JavaScript 的Date构造函数对日期字符串的解析并没有统一标准不同平台的实现存在差异// 后端返回的日期格式常见constdateStr2025-12-04 15:00:00// 在不同平台上的解析结果newDate(dateStr)// Chrome / 安卓 WebView// → Wed Dec 04 2025 15:00:00 GMT0800 ✓// Safari / iOS WebView// → Invalid Date ✗为什么 iOS 不支持iOS 的 Safari 引擎对日期格式的解析更加严格只支持以下格式格式示例iOS 支持ISO 86012025-12-04T15:00:00✅RFC 2822Wed, 04 Dec 2025 15:00:00 GMT✅斜杠分隔2025/12/04 15:00:00✅横杠空格2025-12-04 15:00:00❌问题就出在最后一种格式——横杠分隔日期 空格 时间这是后端最常返回的格式但 iOS 不支持问题代码分析// 获取月度学习记录constfetchMonthlyStudyRecordsasync(){constresawaitapi.getStudyRecords({month:currentMonth.value})// 过滤当月的学习记录constrecordsres.filter(item{constitemDatenewDate(item.studyTime)// ❌ iOS 上返回 Invalid DatereturnitemDate.getMonth()currentMonth.value.getMonth()})studyRecords.valuerecords}当new Date(item.studyTime)返回Invalid Date时getMonth()返回NaN比较结果永远为false所有记录都被过滤掉了解决方案方案一统一日期解析函数推荐封装一个跨端兼容的日期解析函数/** * 跨端安全的日期解析 * 支持多种常见日期格式 * param {string|Date} dateStr - 日期字符串或 Date 对象 * returns {Date} - Date 对象 */constparseDate(dateStr){if(!dateStr)returnnewDate(NaN)if(dateStrinstanceofDate)returndateStr// 如果是标准 ISO 格式直接解析if(dateStr.includes(T)){returnnewDate(dateStr)}// 将 yyyy-MM-dd HH:mm:ss 转换为 yyyy/MM/dd HH:mm:ss// iOS Safari 支持斜杠格式constnormalizedStrString(dateStr).replace(/-/g,/)returnnewDate(normalizedStr)}// 使用constdateparseDate(2025-12-04 15:00:00)// 所有平台都能正确解析方案二转换为 ISO 格式/** * 将常见日期格式转换为 ISO 8601 格式 * param {string} dateStr - yyyy-MM-dd HH:mm:ss 格式 * returns {Date} */constparseToISO(dateStr){if(!dateStr)returnnewDate(NaN)// yyyy-MM-dd HH:mm:ss → yyyy-MM-ddTHH:mm:ssconstisoStrString(dateStr).replace( ,T)returnnewDate(isoStr)}方案三手动解析最可靠对于复杂场景手动解析最可靠/** * 手动解析日期字符串 * param {string} dateStr - yyyy-MM-dd HH:mm:ss 格式 * returns {Date} */constmanualParseDate(dateStr){if(!dateStr)returnnewDate(NaN)// 正则匹配各部分constmatchString(dateStr).match(/(\d{4})-(\d{2})-(\d{2})(?:\s(\d{2}):(\d{2}):(\d{2}))?/)if(!match)returnnewDate(NaN)const[,year,month,day,hour0,minute0,second0]matchreturnnewDate(parseInt(year),parseInt(month)-1,// 月份从 0 开始parseInt(day),parseInt(hour),parseInt(minute),parseInt(second))}完整的修复示例template view classstudy-calendar view classcalendar-header text{{ formatMonth(currentMonth) }} 学习计划/text /view view v-ifstudyRecords.length 0 classstudy-list view v-forrecord in studyRecords :keyrecord.id classstudy-item text classstudy-date{{ formatDate(record.studyTime) }}/text text classstudy-title{{ record.courseName }}/text /view /view view v-else classempty-tip 本月暂无学习计划 /view /view /template script setup import { ref, onMounted } from vue const currentMonth ref(new Date()) const studyRecords ref([]) /** * 跨端安全的日期解析 */ const parseDate (dateStr) { if (!dateStr) return new Date(NaN) if (dateStr instanceof Date) return dateStr // 统一转换为斜杠格式 const normalizedStr String(dateStr).replace(/-/g, /) return new Date(normalizedStr) } /** * 判断是否同一个月 */ const isSameMonth (date1, date2) { const d1 parseDate(date1) const d2 parseDate(date2) return d1.getFullYear() d2.getFullYear() d1.getMonth() d2.getMonth() } /** * 格式化日期显示 */ const formatDate (dateStr) { const date parseDate(dateStr) if (isNaN(date.getTime())) return const month String(date.getMonth() 1).padStart(2, 0) const day String(date.getDate()).padStart(2, 0) return ${month}-${day} } /** * 格式化月份显示 */ const formatMonth (date) { const d parseDate(date) return ${d.getFullYear()}年${d.getMonth() 1}月 } /** * 获取月度学习记录 */ const fetchMonthlyStudyRecords async () { try { const res await api.getStudyRecords({ month: formatMonth(currentMonth.value) }) // 使用安全的日期解析进行过滤 const records (res || []).filter(item { return isSameMonth(item.studyTime, currentMonth.value) }) // 补充显示所需的字段 studyRecords.value records.map(item ({ ...item, title: item.courseName // 确保 title 字段存在 })) } catch (error) { console.error(获取学习记录失败:, error) studyRecords.value [] } } onMounted(() { fetchMonthlyStudyRecords() }) /script其他常见的日期陷阱1. 时区问题// ISO 格式不带时区会被解析为本地时间newDate(2025-12-04T15:00:00)// 本地时间 15:00// 带 Z 后缀表示 UTC 时间newDate(2025-12-04T15:00:00Z)// UTC 15:00 北京时间 23:002. 月份从 0 开始// 创建 2025年12月4日newDate(2025,11,4)// 月份是 11不是 12// 获取月份constdatenewDate(2025/12/04)date.getMonth()// 返回 11不是 123. 日期比较// 错误直接比较 Date 对象date1date2// 永远为 false比较的是引用// 正确比较时间戳date1.getTime()date2.getTime()// 或者转换为字符串date1.toDateString()date2.toDateString()工具函数封装建议在项目中统一封装日期工具// utils/date.js/** * 安全解析日期 */exportconstparseDate(dateStr){if(!dateStr)returnnullif(dateStrinstanceofDate)returndateStrconstnormalizedString(dateStr).replace(/-/g,/)constdatenewDate(normalized)returnisNaN(date.getTime())?null:date}/** * 格式化日期 */exportconstformatDate(dateStr,formatYYYY-MM-DD){constdateparseDate(dateStr)if(!date)returnconstyeardate.getFullYear()constmonthString(date.getMonth()1).padStart(2,0)constdayString(date.getDate()).padStart(2,0)consthourString(date.getHours()).padStart(2,0)constminuteString(date.getMinutes()).padStart(2,0)constsecondString(date.getSeconds()).padStart(2,0)returnformat.replace(YYYY,year).replace(MM,month).replace(DD,day).replace(HH,hour).replace(mm,minute).replace(ss,second)}/** * 判断是否同一天 */exportconstisSameDay(date1,date2){constd1parseDate(date1)constd2parseDate(date2)if(!d1||!d2)returnfalsereturnd1.toDateString()d2.toDateString()}/** * 判断是否同一月 */exportconstisSameMonth(date1,date2){constd1parseDate(date1)constd2parseDate(date2)if(!d1||!d2)returnfalsereturnd1.getFullYear()d2.getFullYear()d1.getMonth()d2.getMonth()}最佳实践统一日期解析入口项目中所有日期解析都使用统一的工具函数后端配合建议后端返回 ISO 8601 格式yyyy-MM-ddTHH:mm:ss前端转换如果后端无法修改前端统一做格式转换多端测试日期相关功能必须在 iOS 真机上测试使用成熟的库复杂项目建议使用dayjs或date-fns// 使用 dayjs轻量级importdayjsfromdayjsdayjs(2025-12-04 15:00:00).format(MM-DD)// 自动处理兼容性总结问题本质iOS Safari 对日期格式解析更严格不支持yyyy-MM-dd HH:mm:ss格式核心解决方案将横杠-替换为斜杠/或转换为 ISO 格式预防措施封装统一的日期解析函数所有日期操作都通过它进行测试要求日期相关功能必须在 iOS 真机上验证本文源于实际项目中的问题修复经验这个问题曾导致学习日历在 iOS 设备上完全无法显示数据。希望能帮助其他开发者避免类似的坑。