You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot原生查询如何将Java Date从服务器时区转换为用户时区查询数据

问题根因
  • 你在pgAdmin中执行查询能得到正确结果,是因为between的两个参数是Asia/Calcutta时区下的本地时间字符串,和左边rr.created_date at time zone 'utc' at time zone 'Asia/Calcutta'的输出类型(不带时区的Asia/Calcutta本地时间)完全匹配,比较逻辑符合预期。
  • 代码中无论怎么设置Calendar的时区,最终调用getTime()得到的Date对象本身不携带时区信息,仅代表1970年1月1日以来的毫秒数。当你把Date对象作为参数传给JDBC时,驱动会默认按数据库会话的时区把这个毫秒数转换为不带时区的timestamp,和左边转换后的Asia/Calcutta本地时间比较自然会出现偏差,这也是只有用户时区早上5:30之后查询结果才正确的原因。
解决方案

推荐两种可直接落地的修改方式,优先选第一种,性能更好:

方案1:修改SQL(推荐,可走created_date索引)

直接在SQL层完成时区转换,避免对表字段做函数转换,性能更高,同时不需要修改Java代码的参数生成逻辑:

SELECT * FROM records rr 
WHERE rr.created_date >= ?1::DATE AT TIME ZONE 'Asia/Calcutta' AT TIME ZONE 'utc'
AND rr.created_date < (?1::DATE + INTERVAL '1 day') AT TIME ZONE 'Asia/Calcutta' AT TIME ZONE 'utc'

参数?1直接传用户请求的日期字符串即可,比如2021-10-16,不需要处理时分秒。

方案2:不改SQL,修改参数传递逻辑

沿用你现有的SQL,把参数从Date类型改为Asia/Calcutta本地时间的字符串,和你在pgAdmin中执行的逻辑完全一致:

  1. 首先生成Asia/Calcutta时区下的起止时间字符串,推荐用Java 8+的java.time包(比Calendar更不容易出错):
import java.time.LocalDate;
import java.time.ZoneId;

// 用户请求的目标日期,比如2021-10-16
LocalDate targetDate = LocalDate.of(2021, 10, 16);
ZoneId userZone = ZoneId.of("Asia/Calcutta");

// 生成起止时间字符串
String startParam = targetDate.atStartOfDay().toString();
String endParam = targetDate.plusDays(1).atStartOfDay().toString();
  1. 把startParam和endParam作为字符串参数传入你的原SQL,同时调整SQL的边界判断更稳妥:
SELECT * FROM records rr 
WHERE rr.created_date AT TIME ZONE 'utc' AT TIME ZONE 'Asia/Calcutta' >= ?1
AND rr.created_date AT TIME ZONE 'utc' AT TIME ZONE 'Asia/Calcutta' < ?2

内容的提问来源于stack exchange,提问作者Sachinthana Aluvihare

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 15:06:03