TypeScript Vue3组件:不同浏览器上月最后工作日显示不一致问题
Vue 3 + TypeScript:date输入框跨浏览器显示日期不一致的原因及解决方法
问题场景
我开发了一个简单的TypeScript Vue.js 3组件,使用input日期控件(也尝试过日期选择器,结果一致),本应默认渲染上月的最后一个工作日:
<script setup lang="ts"> import { ref, computed } from "vue"; const lastWorkingDayOfLastMonth = computed(() => { const today = new Date(); let lastDayOfMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1); let lastWorkingDayOfMonth = new Date(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth() + 1, 0); while ( lastWorkingDayOfMonth.getDay() === 0 || lastWorkingDayOfMonth.getDay() === 6 ) { lastWorkingDayOfMonth.setDate(lastWorkingDayOfMonth.getDate() - 1); } return lastWorkingDayOfMonth.toISOString().substr(0, 10); }); const selectedDate = ref(lastWorkingDayOfLastMonth.value); </script> <template> <div> <input type="date" id="date" v-model="selectedDate" /> </div> </template>
在同一设备上,Firefox能正确显示2023年2月28日,但Chrome/Edge却显示2023年2月27日。尝试通过调整时区偏移修复,结果依旧无效。
原因分析
问题核心出在toISOString()的时区特性:
toISOString()会把本地时区的日期转换为UTC时区的ISO格式字符串,而你计算的lastWorkingDayOfMonth是基于本地时区的日期。- 比如你处于东八区(UTC+8),本地时间的2023-02-28 00:00:00,对应的UTC时间是2023-02-27 16:00:00,此时
toISOString()生成的字符串前10位就是2023-02-27。 - Chrome/Edge严格按照ISO字符串的日期部分渲染input,而Firefox在处理时自动适配了本地时区,导致显示差异。
你之前的时区偏移调整逻辑错误:getTimezoneOffset()返回的是本地时间与UTC的分钟差(东八区返回-480),你用setMinutes(xxx + 偏移值)会把日期往UTC方向调整,反而让问题更严重。
解决方法
直接基于本地时间的年、月、日组件拼接成YYYY-MM-DD格式的字符串,彻底绕过时区转换的问题:
<script setup lang="ts"> import { ref, computed } from "vue"; const lastWorkingDayOfLastMonth = computed(() => { const today = new Date(); let lastDayOfMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1); let lastWorkingDayOfMonth = new Date(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth() + 1, 0); // 调整到上月最后一个工作日 while (lastWorkingDayOfMonth.getDay() === 0 || lastWorkingDayOfMonth.getDay() === 6) { lastWorkingDayOfMonth.setDate(lastWorkingDayOfMonth.getDate() - 1); } // 直接拼接本地时间的日期部分,避免时区干扰 const year = lastWorkingDayOfMonth.getFullYear(); const month = String(lastWorkingDayOfMonth.getMonth() + 1).padStart(2, '0'); // 月份是0基,需+1并补零 const day = String(lastWorkingDayOfMonth.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; }); const selectedDate = ref(lastWorkingDayOfLastMonth.value); </script> <template> <div> <input type="date" id="date" v-model="selectedDate" /> </div> </template>
这种方式生成的字符串完全基于本地时区的日期,所有浏览器都会一致显示正确的上月最后一个工作日。
内容的提问来源于stack exchange,提问作者staticdev
相关产品推荐
相关产品推荐

