记录一次问题排查,前台传的日期参数到后台取到的时候少了一天。
文章目录
- 问题概述
- 一、核心原因分析
- 二、时区问题解决方案(分为改代码或者改服务器)
- 第一类方案 代码调整
- 方案 1:全局配置 Jackson 时区(推荐)
- 方案 2:在实体类字段上指定时区(精准控制)
- 方案 3:使用带时区的日期类型(避免歧义)
- 第二类方案,服务器调整
- 查看当前时区
- 方法一:使用 `date +%Z` 命令
- 方法二:使用 `timedatectl` 命令
- 临时修改时区
- 使用 `TZ` 环境变量
- 永久修改时区
- 方法一:使用 `timedatectl` 命令
- 方法二:手动修改 `/etc/timezone` 和 `/etc/localtime` 文件
- 时间问题解决方案
- 出现时区错误时的修改办法
- 日期时间设置错误
- 使用 `date -s` 命令临时设置时间
- 使用 `ntp` 同步时间
- 直接设置系统时间(永久生效,写入硬件时钟)
- 时区文件损坏
问题概述
我的问题是前台通过http请求,向后台发送请求,请求参数中有一个日期参数以json的形式传参给后台。
{
date:“2025-01-08”
}
我通过日志打印,可以看到后台将json转为对象的时候对象的date属性的值是2025-01-07。可以看到是少了一天的。即前台传过来的对象只进行了spring的类型转换就少了一天。
这里简单记录一下排查的思路:
首先将同样的代码和配置扔到其它测试环境的服务器上去部署,发现没有问题。这证明了代码和配置是没有问题的,问题就定位到了服务器相关的配置。问题大概率是由于服务器时区与前端时区不一致导致的日期转换偏差(例如前端发送的是本地时区日期,而服务器默认使用UTC时区处理,导致日期计算时减去时区差后跨天)
一、核心原因分析
- 时区差异:
前端发送的日期字符串(如2025-01-08
)默认是本地时区(如东八区 UTC+8)的日期,而 Spring 在反序列化 JSON 时,若未显式指定时区,会使用 服务器的默认时区(可能为 UTC) 进行转换。
例如:前端时间2025-01-08 00:00:00(UTC+8)
转换为 UTC 时间是2025-01-07 16:00:00
,若仅取日期部分(忽略时间),就会显示为2025-01-07
。
二、时区问题解决方案(分为改代码或者改服务器)
第一类方案 代码调整
(如果是正式环境不建议调整代码,还是建议第二类方案,调整服务器时区。)
方案 1:全局配置 Jackson 时区(推荐)
在 Spring Boot 中配置 Jackson 的全局时区,确保 JSON 反序列化时使用与前端一致的时区(例如中国标准时间 UTC+8):
# application.properties 或 application.yml
spring:jackson:time-zone: GMT+8 # 或 UTC+8,与前端时区一致date-format: yyyy-MM-dd # 显式指定日期格式(可选,默认已支持)
方案 2:在实体类字段上指定时区(精准控制)
若仅个别字段需要特殊处理,使用 @JsonFormat
注解指定时区和格式:
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;public class YourEntity {@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") // 与前端时区一致private Date date;// getters/setters
}
方案 3:使用带时区的日期类型(避免歧义)
建议前端发送 ISO 8601 格式的带时区日期字符串(如 2025-01-08T00:00:00+08:00
),后端使用 OffsetDateTime
或 ZonedDateTime
类型接收,再对时区进行相应的处理:
下面是前端示例:
// 假设 date 是前端获取的日期对象
const date = new Date('2025-01-08');
const utcDate = date.toISOString().split('T')[0];const data = {date: utcDate
};// 发送请求
fetch('/your-api-url', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => console.log(result))
.catch(error => console.error('Error:', error));
下面是后端示例:
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Map;@RestController
public class DateController {@PostMapping("/your-api-url")public String handleDate(@RequestBody Map<String, String> request) {String dateStr = request.get("date");LocalDate localDate = LocalDate.parse(dateStr);// 转换为 UTC 时间ZonedDateTime utcDateTime = localDate.atStartOfDay(ZoneId.of("UTC"));// 转换为服务器时区ZonedDateTime serverDateTime = utcDateTime.withZoneSameInstant(ZoneId.systemDefault());LocalDate serverDate = serverDateTime.toLocalDate();return "Received date: " + serverDate;}
}
第二类方案,服务器调整
下面是在 Linux 系统中调整时区的方法,以及出现时区调整之后服务器时间错误的修改办法,并给出了每个命令执行结果的大概的示例。由于linux服务器的区别比较大,考虑到在内网环境的同学可能有的命令没有办法获取到,所以每个功能都尽可能给出不止一种方法。
查看当前时区
方法一:使用 date +%Z
命令
- 命令:
date +%Z
- 结果示例:
UTC
这表明当前系统的时区为协调世界时(UTC)。
方法二:使用 timedatectl
命令
- 命令:
timedatectl
- 结果示例:
Local time: Wed 2025-04-16 12:30:00 UTCUniversal time: Wed 2025-04-16 12:30:00 UTCRTC time: Wed 2025-04-16 12:30:00Time zone: UTC (UTC, +0000)
System clock synchronized: yesNTP service: activeRTC in local TZ: no
该输出显示了详细的时间信息和当前的时区设置。
临时修改时区
使用 TZ
环境变量
- 命令:
export TZ=Asia/Shanghai
date +%Z
- 结果示例:
CST
此时当前会话的时区已临时修改为中国标准时间(CST),但退出当前会话或重启系统后,时区设置会恢复原状。
永久修改时区
方法一:使用 timedatectl
命令
- 命令:
sudo timedatectl set-timezone Asia/Shanghai
- 结果示例:
Local time: Wed 2025-04-16 20:30:00 CSTUniversal time: Wed 2025-04-16 12:30:00 UTCRTC time: Wed 2025-04-16 12:30:00Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: yesNTP service: activeRTC in local TZ: no
可以看到时区已成功永久设置为 Asia/Shanghai
。
方法二:手动修改 /etc/timezone
和 /etc/localtime
文件
- 编辑
/etc/timezone
文件:
sudo nano /etc/timezone
在编辑器中输入 Asia/Shanghai
,然后保存并退出。
- 更新
/etc/localtime
文件:
sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
date +%Z
- 结果示例:
CST
这表明时区已永久修改为 Asia/Shanghai
。
时间问题解决方案
对于我的问题而言到这里并没有结束,因为我的服务器时区的时间是错的,真的裂开。下面是修改时区的时间的方法。但是请注意建议先修改时区再修改时间。
出现时区错误时的修改办法
日期时间设置错误
使用 date -s
命令临时设置时间
- 命令:
sudo date -s "2025-04-18 14:30:00"
date
- 结果示例:
Fri Apr 18 14:30:00 CST 2025
特别需要注意的是,使用 date -s
命令手动设置时间只是临时的,系统重启后时间可能会恢复之前的设置。
使用 ntp
同步时间
- 安装
ntp
服务(以 Debian/Ubuntu 为例):
sudo apt-get update
sudo apt-get install ntp
- 启动
ntp
服务并设置开机自启:
sudo systemctl start ntp
sudo systemctl enable ntp
- 验证时间同步:
ntpq -p
- 结果示例:
remote refid st t when poll reach delay offset jitter
==============================================================================
*ntp1.example.com 192.168.1.1 2 u 23 64 37 0.234 0.123 0.012
此输出表明系统正在从 ntp1.example.com
服务器同步时间。
直接设置系统时间(永久生效,写入硬件时钟)
使用 timedatectl
修改系统时间
- 查看当前时间状态
timedatectl
输出示例(未同步 NTP 且时间可能错误):
Local time: Fri 2025-04-18 11:30:00 UTC # 本地时间(错误时区)Universal time: Fri 2025-04-18 11:30:00 UTC # UTC 时间RTC time: Fri 2025-04-18 11:30:00 # 硬件时钟(RTC)时间Time zone: UTC (UTC, +0000) # 当前时区(错误,应为 CST)
System clock synchronized: no # NTP 未同步NTP service: inactive # NTP 服务未运行RTC in local TZ: no
- 直接设置系统时间(永久生效,写入硬件时钟)
sudo timedatectl set-time "2025-04-18 14:30:00" # 格式:YYYY-MM-DD HH:MM:SS
执行后验证:
timedatectl
输出示例(时间已修正):
Local time: Fri 2025-04-18 14:30:00 UTC # 时间已更新,但时区仍错误Universal time: Fri 2025-04-18 14:30:00 UTCRTC time: Fri 2025-04-18 14:30:00Time zone: UTC (UTC, +0000) # 时区需额外修改!
System clock synchronized: noNTP service: inactiveRTC in local TZ: no
时区文件损坏
- 删除损坏的
/etc/localtime
文件并重新创建:
sudo rm /etc/localtime
sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
date +%Z
- 结果示例:
CST
这表明 /etc/localtime
文件已修复,时区设置恢复正常。