1. 问题描述: 用java 自带的Calendar 类计算指定日期的前 n 天的日期;
2.现象: 设置当前日期为19870912, n 为-1, 即计算19870912 下一天的日期,计算出来的结果如下,没有得到预期的19870913。
default timeZone:Asia/ShanghaitestDate:19870912 nextDate:19870912
调用的getDate() 计算逻辑: 将日期转换成毫秒,然后加上n天的毫秒数(n*24*3600*1000L),即为n天后的日期,代码如下:
package com.ibeifeng.senior.kafka;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Date;import java.util.SimpleTimeZone;public class Test {public static void main(String[] args) throws ParseException {SimpleDateFormat yyyymmdd=new SimpleDateFormat( "yyyyMMdd" );String testDateString="19870912";//计算testDate的前一天Date testDate= yyyymmdd.parse(testDateString);Date resDate = getDate(testDate,-1);String res = yyyymmdd.format( resDate );System.out.println( "testDate:"+testDate +" nextDate:"+res );Date testDate1= yyyymmdd.parse(testDateString);System.out.println( "---testDate:"+testDate1 );Date resDateGM = getDateGM(testDate1,-1);String res1 = yyyymmdd.format( resDateGM );System.out.println( "testDate:"+testDate1 +" nextDate:"+res1 );Calendar calendar2 = Calendar.getInstance();String timeZone = calendar2.getTimeZone().getID();System.out.println( "default timeZone:"+timeZone );Date testDate2= yyyymmdd.parse(testDateString);Date resd=getAfterDate(testDate2,-1);String res11 = yyyymmdd.format( resd );System.out.println( "AddDate:"+testDate2 +" nextDate:"+res11 );}//计算在date日期之前的n天的日期public static Date getDate(Date date, int n){Calendar calendar1 = Calendar.getInstance();Calendar calendar2 = Calendar.getInstance();String timeZone = calendar1.getTimeZone().getID();System.out.println( "default timeZone:"+timeZone );calendar2.setTime( date );Long c1TimeInMillis = calendar2.getTimeInMillis() - n*24*3600*1000L;calendar1.setTimeInMillis(c1TimeInMillis);return calendar1.getTime();}public static Date getDateGM(Date date, int n){Calendar calendar1 = Calendar.getInstance();Calendar calendar2 = Calendar.getInstance();calendar2.setTime( date );Long c1TimeInMillis = calendar2.getTimeInMillis() - n*24*3600*1000L;calendar1.setTimeInMillis(c1TimeInMillis);return calendar1.getTime();}}
调试代码,1.发现计算后的日期 是1987-09-12 23:00:00 ,比预期的结果少一个小时,2.并且testDate 是CDT格式,很奇怪。如图:
查阅资料知:
CDT (中部夏令时间); CST:是中国标准时间;
发现是JVM的默认时区为:Asia/Shanghai,然后Asia/Shanghai 这个时区并不一定与GMT+08这个时区相等,他们是2种定义标准。Asia/Shanghai 这个代表的是中国的时区,但在历史中,有国家(包含中国)政策颁布了在1986-1991年等还存在夏令时。在这样的时间区间,夏季时,会将时间拨快1个小时(即东9区时间),夏季结束时会再次将时间拨回一个小时(即东8区时间)。
虽然Calendar 默认的时区是Asia/Shanghai,但是Asia/Shanghai 在1986-1991年间仍然存在夏令时,比正常时间多一个小时,这就导致在计算19870912下一天的时候,增加24小时,但日期仍然是 0912日,而不是预期的13日。
3. 解决方案:
方案1. 在计算日期时,Calendor指定时区为 GMT+8,如getDateGM()方法,但是指定时区后,进行set值的时候会将原来CDT时间转换成GMT时间,即 19780912 00:00:00 变为 19780911 23:00:00 ,即对这个0911这个日期进行加24小时,还是不能达到获取后一天的目地。
public static Date getDateGM(Date date, int n){String timeZone = Calendar.getInstance().getTimeZone().getID();System.out.println( "default timeZone:"+timeZone );TimeZone.setDefault( TimeZone.getTimeZone( "GMT+8" ) );Calendar calendar1 = Calendar.getInstance(TimeZone.getTimeZone( "GMT+8" ));Calendar calendar2 = Calendar.getInstance(TimeZone.getTimeZone( "GMT+8" ));calendar1.clear();calendar2.clear();String timeZone1 = calendar1.getTimeZone().getID();System.out.println( "after timeZone:"+timeZone1 );calendar2.setTime( date );Long c1TimeInMillis = calendar2.getTimeInMillis() - n*24*3600*1000L;calendar1.setTimeInMillis(c1TimeInMillis);return calendar1.getTime();}
方案2. 使用Calendar 的add() 方法,直接对天进行计算。
public static Date getAfterDate(Date date , int n){Calendar calendar1 = Calendar.getInstance();calendar1.setTime( date );calendar1.add( Calendar.DATE,n );return calendar1.getTime();}
结果:
default timeZone:Asia/Shanghai
testDate:Sat Sep 12 00:00:00 CDT 1987 nextDate:19870912
---testDate:Sat Sep 12 00:00:00 CDT 1987
default timeZone:Asia/Shanghai
after timeZone:GMT+08:00
testDate:Fri Sep 11 23:00:00 GMT+08:00 1987 nextDate:19870912
default timeZone:GMT+08:00
AddDate:Fri Sep 11 23:00:00 GMT+08:00 1987 nextDate:19870911
中国夏时制曾策参看:/name/xiashizhi.htm