最近遇到一个问题,前端接口在往后端接口传值的时候,时间字符串(比如:2019-01-01 00:00:00)转 timestamp 出错了,导致接口调用失败,同样的代码,在windows上运行是OK的,但是到linux或者mac上就会出现异常,初步怀疑是Windows和linux/Mac的时区不一致导致的,于是在mac和Linux上输出系统语言和系统区域,结果都是中文:
说明系统语言设置是没问题的,于是接着使用debug模式一步一步找到异常的地方,终于发现了问题出现在这里:
根据msf4j的源代码所呈现时间转换逻辑:
- 在类
DateTypeAdapter
中,声明了3个用于转换的DateFormat 实例,分别是:
- enUsFormat 美国语言
- localFormat 本地语言
- iso8601Format iso8601标准
- 接下来调用转换方法
private synchronized Date deserializeToDate(String json)
将string
转换为Date
对象,完成转换
不过很不幸的是,在linux 和 mac中,全都转换失败,只有在windows下才会转换成功,通过在debug模式下查找,发现原因在创建DateFormat
实例的时候DateFormat.getDateTimeInstance()
,mac模式下获取到的locale是en_CN
:
windows模式下获取到的locale是zh_CN
:
所以问题就在en_CN
和zh_CN
这里,其中,en
和zh
表示的是系统语言,en
表示英语(一般指美国英语),zh
表示中文,CN
表示国家-中国,换个方式看可能更好理解:
Locale locale = Locale.getDefault();
System.out.println(locale.getLanguage());//输出系统语言代码
System.out.println(locale.getCountry());//输出系统国家代码
System.out.println(locale.getDisplayLanguage());//输出系统语言名称
System.out.println(locale.getDisplayCountry());//输出系统国家名称
输出结果为:
不过即使输出为中文和中国,但是也还有可能出错,因为在实例 DateFormat localFormat = DateFormat.getDateTimeInstance(2, 2)
创建的过程中,并不需要指定locale,该方法会去使用一个系统默认语言Local.Category.Format
:
public final static DateFormat getDateTimeInstance(int dateStyle,
int timeStyle)
{
return get(timeStyle, dateStyle, 3, Locale.getDefault(Locale.Category.FORMAT));
}
这时候如果mac系统中的语言是中文
加上英文
,像这样:
Locale.Category.Format
获取到的language就会是en
,所以就变成了en_CN
(英文_中国),然后转换就是失败了
所以解决办法就是在创建DateFormat
实例之前,给jvm
设置Locale.setDefault(Locale.CHINA)
,这样就避免了jvm
在查找系统语言的过程中找到除了中文语言之外的其他语言,造成转换失败的异常
当然,如果把英文
删除,只保留中文
,则不用设置Locale
为CHINA
也没问题了,所以linux 上也是如此,在系统语言中,设置了不止一个中文语言,还包括英语,虽然第一语言是中文。