很多表单只需要“日期”而不是“时间”。但前端用日期选择器后,提交给后端常常出现日期被偏移一天的问题(典型表现:后端收到的值比用户选择的少/多一天)。

本文记录在使用 Element/Element Plus 的 DatePicker 时,由时区引发的日期偏移问题,以及简单可靠的解决办法。

问题复现

最常见的用法如下(仅需要年月日):

1
2
3
4
5
6
7
<template>
<el-date-picker
v-model="new_birthDate"
type="date"
value-format="YYYY-MM-DD"
/>
</template>
  • 当仅依赖组件默认行为并把 new_birthDate 直接当作 Date 或 ISO 字符串传给后端时,序列化过程会参照本地时区→UTC 转换,从而产生偏移。
  • 后端如果按 UTC 解析并存储,最终渲染回前端时,就可能出现日期相差一天。

根因简析

  • format:控制“显示给用户”的格式。
  • value-format:控制“v-model 绑定/发出的值”的格式;设置后,v-model 会是对应格式的字符串,未设置时则通常为 Date 对象。
  • 日期一旦以 Date 对象或带时区的 ISO 时间串在网络中流转,就会发生本地时区和 UTC 的转换,导致“日期型需求”出现偏差。

推荐做法(最简单、最稳妥)

让交互展示与提交值都使用“纯日期字符串”,从源头避免时区参与:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<el-date-picker
v-model="new_birthDate"
type="date"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
placeholder="选择日期"
/>
</template>

<script setup lang="ts">
// new_birthDate 将是形如 '2025-12-19' 的字符串
import { ref } from 'vue'
const new_birthDate = ref<string>('')

// 提交时直接把该字符串传给后端
function submit() {
// 例如:axios.post('/api', { birthDate: new_birthDate.value })
}
</script>

要点:

  • 同时设置 value-formatformatYYYY-MM-DD
  • 以“纯字符串”在前后端之间传递“日期”,避免 Date/ISO 时间串引入的时区换算。

如果历史代码里已经是 Date 对象

若当前 v-model 仍为 Date,提交前先手动格式化为日期字符串再传:

1
2
3
4
5
6
import dayjs from 'dayjs'

// 假设 value 是一个 Date 对象
function toDateString(value: Date | null) {
return value ? dayjs(value).format('YYYY-MM-DD') : ''
}

后端配合建议

  • 字段类型选用“日期型”(例如 SQL 的 DATE)而非“日期时间型”,避免无意义的时区换算。
  • 如果存储为字符串,统一使用 YYYY-MM-DD 并在业务层校验。

小结

“只要日期不要时间”的场景,尽量用“纯日期字符串(YYYY-MM-DD)”在前后端之间传输。Element(Plus) 的 DatePicker 同时设置 value-formatformatYYYY-MM-DD,即可从根源规避时区导致的日期偏移问题。