1. 问题描述: 用java 自带的Calendar 类计算指定日期的前 n 天的日期;
2.现象: 设置当前日期为19870912, n 为-1, 即计算19870912 下一天的日期,计算出来的结果如下,没有得到预期的19870913。
default timeZone:Asia/Shanghai
testDate: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