一: 夏令时基础知识
夏令时有什么用?
把时间往前调,充分利用白天提高效率,以及节约能源
RPC夏令时范围:
PRC解放前几年在部分地区也曾实行过夏令时。1986年4月,中央有关部门发出“在全国范围内实行夏时制的通知,
具体做法是:每年从四月中旬第一个星期日的凌晨2时整(北京时间),将时钟拨快一小时,即将表针由2时拨至3时,夏令时开始;
到九月中旬第一个星期日的凌晨2时整(北京夏令时),再将时钟拨回一小时,即将表针由2时拨至1时,夏令时结束。
从1986年到1991年的六个年度,除1986年因是实行夏时制的第一年,从5月4日开始到9月14日结束外,其它年份均按规定的时段施行。
1992年起,夏令时暂停实行。
PRC夏令时时间段:
1935年至1951年,每年5月1日至9月30日。
1952年3月1日至10月31日。
1953年至1954年,每年4月1日至10月31日。
1955年至1956年,每年5月1日至9月30日。
1957年至1959年,每年4月1日至9月30日。
1960年至1961年,每年6月1日至9月30日。
1974年至1975年,每年4月1日至10月31日。
1979年7月1日至9月30日。
1986年4月13日至9月14日,
1987年4月12日至9月13日,
1988年4月10日至9月11日,
1989年4月16日至9月17日,
1990年4月15日至9月16日,
1991年4月14日至9月15日。
为什么PRC要停止夏令时?
个人理解主要是所处的地理位置,不会出现昼夜差过大的,没必要搞这么复杂,做好个人作息控制就可以.
GMT+08 和 Asia/Shanghai的区别:
GMT+08 : 东8区标准时间,不能确定具体哪个国家,无法处理各个国家规定的夏令时,所以没有夏令时的区分;
Asia/Shanghai: 每个国家是有自己的特殊timezone, PRC用的就是这个;
使用Asia/Shanghai时会处理夏令时, 使用GMT+8时不会处理夏令时, 因为GMT+8不能确定是哪个国家,也就无法使用各个国家规定的夏令时了。
二: Springboot处理夏令时
执行过程描述:
字符串日期数据->反序列化为内存Date对象(JVM timezone)->序列化json串给客户端(配置文件timezone)
SpirngMVC对时间处理:
默认使用的Jackson进行的序列化,当前配置成 timezone: GMT+08, 则需要保证JVM的时区一致,夏令时才能正确处理。
确认操作系统和java进程时区:
操作系统: date -R
timedatectl
java进程: jinfo {pid} | grep user.timezone
三: 验证
取样: 1990-07-26 00:00:00 ,该时间为夏令时时间
1. Springboot controller 测试:
--公共配置:
// jackson配置序列化
spring.jackson.time-zone: GMT+08
// 请求代码
@RequestMapping(value = "/demo/test1", method = RequestMethod.POST)
@ResponseBody
public ResultBase<DemoTime> test1() throws InterruptedException {
Calendar calendar1 = Calendar.getInstance();
calendar1.set(1990, Calendar.JULY, 26, 0, 0, 0); //1990-07-26 00:00:00
Date birthday1 = calendar1.getTime();
log.info("Case {} ,birthday:{},json birth:{}", ZoneId.systemDefault(), birthday1, JSON.toJSONString(birthday1, true));
DemoTime demoTime = new DemoTime();
demoTime.setDate1(birthday1);
return ResultBaseExt.success(demoTime);
}
--场景测试:
场景1:JVM timezone 使用默认Asia/Shanghai
-DDEPLOY_ENV=dev -DCONFIG_URI=http://XXX:XXX:XXX:XXXX:8888
(1)http 请求
POST http://localhost:8080/demo/test1
HTTP/1.1 200 OK
Date: Fri, 13 Aug 2021 13:07:34 GMT
Content-Type: application/json;charset=utf-8
Transfer-Encoding: chunked
{
"value": {
"date1": "1990-07-25 23:00:00" // 结果比实际早了一个小时
},
"reality": true,
"additionalInfo": {},
"success": true,
"getMsgByCode": false
}
(2) 后台日志:
[DemoController.java:51] - Case Asia/Shanghai ,birthday:Thu Jul 26 00:00:00 CDT 1990,json birth:648918000704
场景2:JVM timezone指定GMT+08 ,和spring.jackson.timezone 保持一致
-DDEPLOY_ENV=dev -DCONFIG_URI=http://XXX:XXX:XXX:XXXX:8888 -Duser.timezone=GMT+08
POST http://localhost:8080/demo/test1
(1)http 请求
HTTP/1.1 200 OK
Date: Fri, 13 Aug 2021 13:03:50 GMT
Content-Type: application/json;charset=utf-8
Transfer-Encoding: chunked
{
"value": {
"date1": "1990-07-26 00:00:00" // 时间获取正常
},
"reality": true,
"additionalInfo": {},
"getMsgByCode": false,
"success": true
}
(2) 后台日志,
[DemoController.java:51] - Case GMT+08:00 ,birthday:Thu Jul 26 00:00:00 GMT+08:00 1990,json birth:648921600925
2. Main 方法 测试:
// 测试代码
public static void main(String[] args) throws Exception {
TimeZone.setDefault(TimeZone.getTimeZone(args[0]));
Calendar calendar = Calendar.getInstance();
calendar.set(1990, Calendar.JULY, 26, 0, 0, 0);
// 序列化
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(calendar.getTime());
System.out.println("时区: " + TimeZone.getDefault().getID() + ",date=" + calendar.getTime() + ",date-json=" + json);
TimeZone.setDefault(TimeZone.getTimeZone(args[1]));
//反序列化
ObjectMapper mapper2 = new ObjectMapper();
Date afterDeDate = mapper2.readValue(json, Date.class);
System.out.println(afterDeDate);
}
注:这里把字符串转对象称为序列化,对象转字符串称为反序列化。
// 测试场景
场景1:Asia/Shanghai 序列化,GMT+08 反序列化,日志:
时区: Asia/Shanghai,date=Thu Jul 26 00:00:00 CDT 1990,date-json=648918000386
Wed Jul 25 23:00:00 GMT+08:00 1990
场景2:GMT+08 序列化,Asia/Shanghai 反序列化,日志:
时区: GMT+08:00,date=Thu Jul 26 00:00:00 GMT+08:00 1990,date-json=648921600533
Thu Jul 26 01:00:00 CDT 1990
场景3:Asia/Shanghai 序列化,Asia/Shanghai 反序列化,日志:
时区: Asia/Shanghai,date=Thu Jul 26 00:00:00 CDT 1990,date-json=648918000385
Thu Jul 26 00:00:00 CDT 1990
场景4:GMT+08 序列化,GMT+08 反序列化,日志:
时区: GMT+08:00,date=Thu Jul 26 00:00:00 GMT+08:00 1990,date-json=648921600152
Thu Jul 26 00:00:00 GMT+08:00 1990
// 测试结论
日期转换处理需要保证序列号和反序列timezone一致才不会出现问题。
在springboot工程下,序列化方式为jvm的timezone,反序列化为jackson。