Gson centos日期轉換失敗

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

 

問題描述:

線上的日誌裏報了一個JsonSyntaxException的異常:git

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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接口時, 本地一切正常, 放到線上就報錯.github

解決辦法:

最後發現調用時反序列化時Gson是這樣實例化的web

1
Gson gson = new Gson()

 

改成工廠模式構建出的Gson就行了json

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格式化了這個時間,windows

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

把實體類格式化成了這種樣子centos

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

而在另外一邊, 在hessian的客戶端, 是這樣反序列的:框架

1
2
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格式.ui

分析源碼:

根據報錯信息打開出錯的源頭com.google.gson.internal.bind.DateTypeAdapter.java類中的deserializeToDate()方法, 這個方法是這樣的google

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[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
2
// 查看系統擁有語言包
locale -a

 

zh_CN.UTF-8是簡體中文,若是沒有zh_CN.UTF-8,就安裝語言包,若是存在能夠直接設置

1
2
// 安裝簡中語言包
yum install kde-l10n-Chinese

 

永久修改系統語言

1
localectl  set-locale LANG=zh_CN.UTF8
相關文章
相關標籤/搜索