彻底解决Solr日期类型的时区问题
声明
- 文档是基于Solr6.6写的
- Solr是部署在Tomcat上的
3.Tomcat是部署在CentOS上的,不过Linux、Windows差不多 - 文章的问题的最终解决是用第四种方式,前三种想看看看,不想看可以直接看第四种方式
- 本文使用的最终解决方案适用于Solr4到Solr6.6,Solr7没有试过
问题描述
Solr的日期类型是基于UTC时间的,也就是英国的格林尼治天文台的时间,而我们中国在东八区,用的是GMT时间,比UTC时间多了8个小时。真不知道开发Solr的程序员是怎么想的,全世界的人用Solr,不管你是在哪儿的,都得用UTC时间,太TMD坑了。
这个问题前前后后搞了一周了,总结起来有有四种解决思路,本文最终采用的是第四种方案,这里前三种方案也说一下。
解决方案
方案1:修改配置文件的时区改为东8区
先说下这种方法不可行
这种方式网上搜到的最多,大致意思是说在Tomcat的setenv.sh
文件里添加配置时区为为东八区:
-Duser.timezone=Asia/Shanghai
我这边配置过以后重启Tomcat发现日志的时间是过来了,但是往Solr里写数据和从Solr里读数据不是一样少了8个小时,看下Solr的源码你就知道怎么回事儿了:org.apache.solr.response.TextResponseWriter
public void writeDate(String name, Date val) throws IOException {
writeDate(name, val.toInstant().toString());
}
这个是Solr写入日期类型的数据的方法,看源码我们就发现问题了:java.util.Date类的toInstant()方法返回的就是一个UTC时间,你在Tomcat的配置文件里改,当然不起作用啦
方案2:不用Solr的日期类型,直接用String代替
这种方式确实可以很多人用,我们之前的时候就是用这种方式,不过这不是我们讨论的范围,直接Pass掉
下面两种方式都是修改Solr的源码
方案3:修改数据类型源码,不使用UTC时间
修改TrieDateField类(DatePointField,DateRangeField)的源码
参考文章:
Solr Date类型的哪些你不得不了解的细节
文章的作者对Solr的日期类型讲解得非常详细,
按作者写的进行操作的时候,发现不太好操作,于是就联系到文章的作者,交流了一下,发现该博客的作者用的Solr是6.5的版本,源码有些不一样(我用的是Solr6.1),幸运的是作者人非常非常好,非常非常耐心(没错4个非常)地回复了我所有的疑问,直到把问题解决。这种方式这里就不再多说了,需要的话可以直接点开作者的博客
不过问题虽然解决了,但是要修改好几处地方,而且如果Solr再版本升级的话说不定需要修改的地方又变了,Solr6.1和Solr6.5两个版本需要修改的地方就不一样。
有没有更好的办法呢
方案4:修改工具类源码,使UTC时间与本地时间显示得一致(最终方案)
此方案只需要修改一个类的两个地方(读、写的方法),所有的日期类型全部搞定
大致思路是:
UTC时间不是比东八区少了8个小时吗?在往Solr里写数据的时候加上8个小时,从Solr里查数据的时候减去8个小时,这样UTC时间显示得就和咱大中国的一致了。
操作是:
修改JavaBinCodec工具类
具体路径是solr-core-6.6.jar包里的org.apache.solr.common.util.JavaBinCodec
把这个类的源码复制出来,包名类名不要变,修改readObject()和writePrimitive()方法具体如下:
readObject()方法:
switch (tagByte) {
case NULL:
return null;
case DATE:
//存储的时候solr的时间格式是utc的会少8个小时, 所以在writeVal方法里加上了8小时
// 这里再读取的时候就需要再减去8个小时
// 28800000l为8小时的毫秒数
return new Date(dis.readLong() - 28800000l);
case INT:
return dis.readInt();
...
writePrimitive()
else if (val instanceof Date) {
daos.writeByte(DATE);
//UTF时间比东8区少了8个小时,这里加8小时
daos.writeLong((((Date) val).getTime() / 1000) * 1000 + 28800000l);
return true;
} else if (val instanceof Boolean) {
...
然后打包,扔到Tomcat里,并重启Tomcat
Schema.xml的相关配置:
<!-- 当前时间与另外的三种类型做对比 -->
<field name="cur_date" type="string" indexed="false" stored="true"/>
<!-- DateField日期类型 -->
<field name="import_time" type="date" indexed="true" stored="false" docValues="true"/>
<!-- DatePointField日期类型 -->
<field name="capture_time" type="pdate" indexed="true" stored="false" docValues="true"/>
<!-- DateRangeField日期类型 -->
<field name="work_time" type="rdate" indexed="true" stored="true"/>
...
<fieldType name="date" class="solr.TrieDateField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="pdate" class="solr.DatePointField" docValues="true"/>
<fieldType name="rdate" class="solr.DateRangeField" docValues="false" />


下面是往Solr写测试数据的部分代码:
往Solr里写数据的代码:
SolrInputDocument doc = new SolrInputDocument();
doc.addField("cur_date", new SimpleDateFormat("yyyy-MM-dd HH:MM:SS").format(new Date()));
doc.addField("import_time", new Date());
doc.addField("capture_time", new Date());
doc.addField("work_time", new Date());
...
从Solr里读取数据的代码:
System.out.println("import_time \t" + doc.get("import_time"));
System.out.println("capture_time \t" + doc.get("capture_time"));
System.out.println("work_time \t" + doc.get("work_time"));
说明一下:
import_time的类型是TrieDateField
capture_time的类型是DatePointField
work_time的类型是DateRangeField
下面是从Solr里查出出来的结果

通过Java API查询结果:

对比发现work_time、import_time、capture_time与cur_date日期是一致的
问题解决
参考文章是:Hadoop技巧(04):简易处理solr date 时区问题
最后
看了下这篇博客的作者用的Solr版本是Solr4的,我用的Solr版本是6.6的,用同样的方式解决都没有问题,所以如果你用的是在这之间的Solr版本都是没有问题的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
2019-10-18 如何快速将磁盘的MBR分区方式改成GPT分区方式
2018-10-18 Solr中的日期/时间表示
2018-10-18 VMware网络桥接模式与NAT模式共存
2012-10-18 Razor 模板
2010-10-18 货币
2010-10-18 SmartScreen 筛选器带来的麻烦
2006-10-18 Windows API-GDI入门基础知识详解(1)