https://4aiur.github.io/2018/03/26/gson-dateformat-pattern/

问题描述:

线上的日志里报了一个JsonSyntaxException的异常:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  1. Exception in thread "main" com.google.gson.JsonSyntaxException: 2018-03-26 22:22:35
    at com.google.gson.internal.bind.DateTypeAdapter.deserializeToDate(DateTypeAdapter.java:74)
    at com.google.gson.internal.bind.DateTypeAdapter.read(DateTypeAdapter.java:59)
    at com.google.gson.internal.bind.DateTypeAdapter.read(DateTypeAdapter.java:41)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:129)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:220)
    at com.google.gson.Gson.fromJson(Gson.java:888)
    at com.google.gson.Gson.fromJson(Gson.java:853)
    at com.google.gson.Gson.fromJson(Gson.java:802)
    at com.google.gson.Gson.fromJson(Gson.java:774)
    at web.service.JsonTest.main(JsonTest.java:25)
    Caused by: java.text.ParseException: Failed to parse date ["2018-03-26 22:22:35']: Invalid time zone indicator ' '
    at com.google.gson.internal.bind.util.ISO8601Utils.parse(ISO8601Utils.java:274)
    at com.google.gson.internal.bind.DateTypeAdapter.deserializeToDate(DateTypeAdapter.java:72)
    ... 9 more
    Caused by: java.lang.IndexOutOfBoundsException: Invalid time zone indicator ' '
    at com.google.gson.internal.bind.util.ISO8601Utils.parse(ISO8601Utils.java:245)
    ... 10 more

生产场景:

RPC框架是hessian, 这个错误发生在调用hessian接口时, 本地一切正常, 放到线上就报错.

解决办法:

最后发现调用时反序列化时Gson是这样实例化的

  1. 1
  1. Gson gson = new Gson()

改为工厂模式构建出的Gson就好了

  1. 1
  1. Gson builderTime = (new GsonBuilder()).setDateFormat("yyyy-MM-dd HH:mm:ss").create();

复现问题:

实体类里只有java.util.Date类型, 按理说序列化之后应该是Mon Mar 26 21:17:43 CST 2018才对, 这个时间格式明显是被格式化了,
于是发现在hessian的服务端, GsonBuilder格式化了这个时间,

  1. 1
    2
  1. Gson gson = (new GsonBuilder()).setDateFormat("yyyy-MM-dd HH:mm:ss").create();
    gson.toJson(new Foo());

把实体类格式化成了这种样子

  1. {"name":"foo","startTime":"2018-03-26 21:17:43"}

而在另一边, 在hessian的客户端, 是这样反序列的:

  1. 1
    2
  1. Gson gson = new Gson();
    Foo foo = gson.fromJson(barJson, Foo.class);

在win10简体中文版环境下一切正常, 结果到了线上CentOS环境下,
这样Gson gson = new Gson()得到的gson不能正常的将yyyy-MM-dd HH:mm:ss格式的时间转换为GMT格式.

分析源码:

根据报错信息打开出错的源头com.google.gson.internal.bind.DateTypeAdapter.java类中的deserializeToDate()方法, 这个方法是这样的

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  1. private synchronized Date deserializeToDate(String json) {
    try {
    return localFormat.parse(json);
    } catch (ParseException ignored) {
    }
    try {
    return enUsFormat.parse(json);
    } catch (ParseException ignored) {
    }
    try {
    return ISO8601Utils.parse(json, new ParsePosition(0));
    } catch (ParseException e) {
    throw new JsonSyntaxException(json, e);
    }
    }

这是一个很暴力的适配器模式, localFormat, enUsFormat, ISO8601Utils挨个尝试转换,那么这三个值具体是什么呢, 打个断点来看看:

ISO8601:

首先是ISO8601,Google的大佬直接hard code 成Locale.US.

ISO8601

这是个UTC时间, T标识是UTC时间,Z标识时区, 北京时间比UTC快的8个小时, 会被记作UTC+8, 这就是东八区的由来, 如下:

UTC例子

EnUsFormat:

再看enUsFormat,

enUsFormat

美国的时间会标记出上午和下午, 时间格式是MMM dd, yyyy hh:mm:ss a, 例如Sep 16, 2015 10:34:23 AM.

LocalFormat:

最后是localFormat,

cn-localFormat

注意到这个java.text.DateFormat#getDateTimeInstance(int, int)这个方法,
不同地区规定的常用日期格式是不一样的, 查询WIKI百科各地日期和时间表示法,得知这个日期格式是台湾的常用日期格式,
问题它怎么知道我是在美国还是在中国台湾, 看getDateTimeInstance方法的源码

getDateTimeInstance()

发现这个方法在Java7之后, 根据操作系统的语言, 判断用户所在的时区,
那现在将WIN10控制面板=>时钟、语言和区域=>添加语言=>更改windows显示语言为英语-美国, 再来看localFormat变成了什么:

us-localFormat

坑爹啊Gson, 使用locale命令查一查线上CentOS7的语言环境

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  1. [root@VM_0_9_centos ~]# locale
    LANG=en_US.utf8
    LC_CTYPE="en_US.utf8"
    LC_NUMERIC="en_US.utf8"
    LC_TIME="en_US.utf8"
    LC_COLLATE="en_US.utf8"
    LC_MONETARY="en_US.utf8"
    LC_MESSAGES="en_US.utf8"
    LC_PAPER="en_US.utf8"
    LC_NAME="en_US.utf8"
    LC_ADDRESS="en_US.utf8"
    LC_TELEPHONE="en_US.utf8"
    LC_MEASUREMENT="en_US.utf8"
    LC_IDENTIFICATION="en_US.utf8"
    LC_ALL=

果然是英文环境, 这样的话yyyy-MM-dd HH:mm:ss,
既无法被UTC时间yyyy-MM-dd'T'HH:mm:ss'Z'适配,
无法被美国默认时间MMM dd, yyyy hh:mm:ss a适配, 最后抛出了JsonSyntaxException.

根治:

改成中文, 并重启系统

  1. 1
    2
  1. // 查看系统拥有语言包
    locale -a

zh_CN.UTF-8是简体中文,如果没有zh_CN.UTF-8,就安装语言包,如果存在可以直接设置

  1. 1
    2
  1. // 安装简中语言包
    yum install kde-l10n-Chinese

永久修改系统语言

  1. 1
  1. localectl set-locale LANG=zh_CN.UTF8

Gson centos日期转换失败的更多相关文章

  1. SAP字段带空格,导致日期转换失败,提示not a vaild month

    执行此节点会报以下错误,ORA-01843,no a valid month,提示月份转换异常 尝试增加条件也仍然提示错误:and VBEP.EDATU<>'00000000' and V ...

  2. 使用gson将字符串转换成对象

    Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); System.out.pr ...

  3. gson日期转换问题

    转:http://blog.csdn.net/liao_leo/article/details/44593095 今天遇到个很奇怪的问题,gson解析日期字符串,本地执行可以,服务器上执行就报错. 这 ...

  4. allow zero datetime=true导致datetime转换失败:MySql.Data.Types.MySqlDateTime”的对象无法转换为类型“System.Nullable`1[System.DateTime]

    allow zero datetime=true导致datetime转换失败:MySql.Data.Types.MySqlDateTime”的对象无法转换为类型“System.Nullable`1[S ...

  5. 大型运输行业实战_day09_1_日期转换与My97DatePicker插件使用

    1.日期转换 1.1字符串类型转换成时间Date类型 /** * 给定字符串 转变 为 Date 类型 * @param date 时间 * @param format 时间格式 如:yyyy-MM- ...

  6. 解决fastjson反序列化日期0000-00-00失败的方案

    解决fastjson反序列化日期0000-00-00失败的方案 22 Jul 2016 一.案例场景复原 示例场景里涉及两个class:TestDemo.java, DateBeanDemo.java ...

  7. springMvc的日期转换之二

    方式一:使用@InitBinder注解实现日期转换 前台页面: 后台打印: 方式二:处理多种日期格式类型之间的转换 采用方式:由于binder.registerCustomEditor(Date.cl ...

  8. Struts2中Date日期转换的问题

      今天跑程序的时候莫名其妙的出现了下面的一个异常: java.lang.NoSuchMethodException:com.ca.agent.model.mybatis.ApprovalInforC ...

  9. Js 日期转换函数(UTC时间转换及日期想加减)

    IOS上Js日期转换中new Date("yyyy-mm-dd")不能正常工作,必须使用new Date("yyyy/MM/dd"); 日期相加减: Date. ...

随机推荐

  1. 《剑指Offer》第1题(Java实现):在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

    一.题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该 ...

  2. composer 安装thinkphp

    //tp 项目文件夹名称 composer create-project topthink/think tp //图形验证码包 composer require topthink/think-capt ...

  3. mkfs.ext4快速格式化大容量硬盘

    安装ext4: yum -y install e4fsprogs 使用如下命令可以快速格式化大容量硬盘: mkfs.ext4 -T largefile /dev/xxx

  4. windows安装oracle遇INS 30131 错误

    win2008 64位安装oracle 11.2.0.4也遇到INS 30131 错误 描述: 原因 - 无法访问临时位置. 操作 - 请确保当前用户具有访问临时位置所需的权限. 附加信息: - 所有 ...

  5. jQuery基础方法:each(),map(),index(),is()

    jQuery的each()方法和forEach()的区别: each()返回调用自身的jQuery对象,可用于链式调用 $('div').each(function(idx){ //找到所有div元素 ...

  6. HDU1060

    #include <bits/stdc++.h> using namespace std; int main() { int n; long long x; double t,ans; c ...

  7. Python学习心得--变量类型篇

    1.Python允许同时为多个变量赋值.例如:a = b = c = 1 2.Python允许多个对象指定多个变量.例如:a1, b1, c1 = 1, 2, "john" 3.使 ...

  8. linux学习第十六天 (Linux就该这么学)

    今生讲了邮件的产生和解决和实际问题,把前两天的和这节邮箱系统统一布置,又统一复习和学习了一下,

  9. Spring Boot 异常处理

    Spring Boot 异常处理 本节介绍一下 Spring Boot 启动时是如何处理异常的?核心类是 SpringBootExceptionReporter 和 SpringBootExcepti ...

  10. Vmware Workstation实现CentOS6.10_x64 下ORACLE RAC 11.2.0.4的搭建

    想必大家在学习ORACLE 11g时,都想搭建一个RAC的实验环境.在搭建RAC实验环境时,会碰到诸如IP怎么规划.虚拟机环境下怎么共享磁盘.ASM磁盘创建,以及安装过程中会遇到这样那样的问题.搭建一 ...