@JsonFormat日期少一天

解决办法:

@JsonFormat(pattern="yyyy-MM-dd")
private Date birth;

改成

@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date birth;

加上时区即可,中国是东八区

But   有时就算加上还是少一天,那就需要用到下面了:

数据信息入库后,重新编辑发现出生日期减少了一天,比如1987-08-04,转换到界面后却变成了1987-08-03;

问题发现过程
     1 先看前端是否有特殊处理,经过检查发现没有问题。
     2 找到获取专家基本信息接口,调用发现返回的日期的确是少了一天,这里可以判断问题出在后端
     3 然后断点,发现在接口最终返回的是一个date类型,这可以断定问题出在最后一步,就是数据转换这块
     4  经过spring-mvc.xml的配置文件,发现转换器是jackson,MappingJackson2HttpMessageConverter类,
        看了一下实体对象birthday,的定义,@JsonFormat都是jackson包的注解,然后就就基本上确定是这里出了问题
1     @JSONField (format="yyyy-MM-dd")
2     @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
3     @DateTimeFormat(pattern ="yyyy-MM-dd")
4     private Date birthday; 

5  然后就搜索 JsonFormat 少了一天的关键信息,发现搜索的结果是,他们没有加时区timezone造成的。这里我也加了的啊?然后就重新试了注释掉JsonFormat结果返回时毫秒数。然后把它加上。同样的问题我在160服务器上试了却发现结果是对的,而我本地和测试环境确都是有问题。然后我开始怀疑是不是数据库有时区。

     6  就开始搜索数据库有关时区的问题。结果尝试了,还是一样的结果,现在想想这应该更数据库时区没有多大的关系。因为它只是做一个存储,你存了什么时间就返回什么时间。又回到代码层面。
 7 就开始断点MappingJackson2HttpMessageConverter代码,通过层层的跟进。最后发现了日期转换的类
DateSerializer,customFormat.format(value), 其实就是SimpleDateFormat的format。
 1 @Override
 2     public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
 3         throws IOException, JsonGenerationException
 4     {
 5         if (_useTimestamp) {
 6             jgen.writeNumber(_timestamp(value));
 7         } else if (_customFormat != null) {
 8             // 21-Feb-2011, tatu: not optimal, but better than alternatives:
 9             synchronized (_customFormat) {
10                 jgen.writeString(_customFormat.format(value));
11             }
12         } else {
13             provider.defaultSerializeDateValue(value, jgen);
14         }
15     }
16 }
    用两个日期断点发现了一些端倪。
    1987-08-04T00:00:00.000+0900
    2017-12-19T00:00:00.000+0800
 
  经过猜测,最后+后面的应该是时区吧。这里觉得很奇怪,为什么明明设置了GTM+8时区的为什么这里不起作用了。
    8   最后就断点到getBirthDay()方法,发现这里返回的就是这个。所以问题就应该出现这里。两个日期为什么就不相同了。此刻 没找到答案,就在segmentfault上发了一个问题。结果没多久,发现就有个人回复我。
 是夏令时,给我了一个连接。发现也是遇到同样的问题。它给出了三个解决方案,我选择了第二种试试
 就是在取日期之前,加入如下代码:
TimeZone.setDefault(TimeZone.getTimeZone("GMT+08"));
1   public Date getBirthday() {
2         //这里由于有夏令营时间存在 这是要默认设置时区,@see http://www.cnblogs.com/memory4young/p/java-timezone.html
3         TimeZone.setDefault(TimeZone.getTimeZone("GMT+08"));
4         return birthday;
5
  经过测试,真的起了效果。反正这玩意也是稍微理解了下,发现问题都出现在1987到1991年的四月,(不仅仅从1987开始  ,194几也都有这个问题)
这期间,这是一个神奇的问题。
   
    问题原因 :
     经过思考我发觉应该是时区的问题,1987默认为东9区,我现在设置为东8区,应该是少一个小时。1987-08-04 00:00:00,少一小时 就是 1987-08-03,Oh yeah !
   上面就是问题的复盘:
  

    后记 :
    在搜索过程中发现了一个问题。就是timestap和datetime区别,其中一点就是有时区差。
        TIMESTAMP
  1、4个字储存(Time stamp value is stored in 4 bytes)
  2、值以UTC格式保存( it stores the number of milliseconds)
  3、时区转化 ,存储时对当前的时区进行转换,检索时再转换回当前的时区。
 
  datetime
  1、8个字节储存(8 bytes storage)
  2、实际格式储存(Just stores what you have stored and retrieves the same thing which you have stored.)
  3、与时区无关(It has nothing to deal with the TIMEZONE and Conversion.)
    
   我看了下birthday 数据库中是datetime ,而在Mapper映射文件中是JDBCTYPE="TIMESTAMP",
     所以在xml中,也修改了下,将JDBCTYPE改为DATETIME。是什么类型用什么类型。这里改变不了上面的 问题,但是应该要按照实际需求对应
 
to:https://blog.csdn.net/samz5906/article/details/79421051?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
原文地址:https://www.cnblogs.com/shenjiangwei/p/14181068.html