美文网首页
彻底解决Solr日期类型的时区问题

彻底解决Solr日期类型的时区问题

作者: 荒野雄兵 | 来源:发表于2018-01-25 11:58 被阅读0次

声明

  1. 文档是基于Solr6.6写的
  2. Solr是部署在Tomcat上的
    3.Tomcat是部署在CentOS上的,不过Linux、Windows差不多
  3. 文章的问题的最终解决是用第四种方式,前三种想看看看,不想看可以直接看第四种方式
  4. 本文使用的最终解决方案适用于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的时区问题-Scham配置文件1.png
解决Solr的时区问题-Schema配置文件2.png

下面是往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里查出出来的结果 解决Solr的时区问题-Solr管理界面查询结果.png

通过Java API查询结果:


解决Solr的时区问题-Java API查询结果.png
对比发现work_time、import_time、capture_time与cur_date日期是一致的
问题解决

参考文章是:Hadoop技巧(04):简易处理solr date 时区问题

最后

看了下这篇博客的作者用的Solr版本是Solr4的,我用的Solr版本是6.6的,用同样的方式解决都没有问题,所以如果你用的是在这之间的Solr版本都是没有问题的
有用就点个赞吧

相关文章

  • 彻底解决Solr日期类型的时区问题

    声明 文档是基于Solr6.6写的 Solr是部署在Tomcat上的3.Tomcat是部署在CentOS上的,不过...

  • PHP基础 —— 日期与时间

    日期与时间 PHP文档 : 日期与时间 设置时区 默认时区是UTC 文档中的 时区列表,使用时进行参照 设置时区 ...

  • NSDate

    创建时间: 时间增减: 时间戳: 日期比较: 设置日期格式: 获取所有时区名称: 设置时区: 将时区配置给date...

  • docker 日期同步问题,不是时区问题

  • Android实践 -- 设置系统日期时间和时区

    设置系统日期时间和时区 设置系统的日期时间和时区,需要 系统权限和系统签名,android:sharedUserI...

  • linux

    CentOS 7 时区、日期时间查看以及设置杂烩 CentOS 7 中时区、日期时间的查看、设置。包括网络时间协议...

  • pandas 时区问题

    术语解释:naive(无时区类型数据), aware(有时区类型数据)pandas中, datetime和time...

  • JavaScript - 8.日期 Dates

    1 日期 Date ◆ 日期默认格式 ◆ 创建日期对象(4种方式) ◆ 显示日期 ◆ 时区 2 日期的格式

  • Joda-Time 时区

    背景 日期时间的存在,是需要时区的支撑的。比如北京时间 2018-11-29 10:12:00,换到其他时区,日期...

  • Android中的时区TimeZone

    设置系统日期时间和时区 设置系统的日期时间和时区,需要 系统权限和系统签名, 需要在manifest文件中添加相应...

网友评论

      本文标题:彻底解决Solr日期类型的时区问题

      本文链接:https://www.haomeiwen.com/subject/trhpaxtx.html