Calendar对象
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* 计算20年后当周的星期六,一周从星期日开始
* @author Administrator
*
*/
public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
Date date=sdf.parse("2006-02-14");
System.out.println(sdf.format(date));
Calendar c=Calendar.getInstance();
c.setTime(date);
c.add(Calendar.YEAR, 20);
//System.out.println(c.get(Calendar.DAY_OF_WEEK));//有就正确,没有就错误:输出2026-02-21
c.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY); //正确日期2026-02-14
System.out.println("入职20周年纪念日派对日期:"+sdf.format(c.getTime()));
}
}
感觉Calendar对象get方法后内部更新了一下set方法才正确设置星期几,不懂原因,有点小bug,求大神指点。
就是最终的
System.out.println("入职20周年纪念日派对日期:"+sdf.format(c.getTime()));
输出有两个结果,影响因素却是一个get方法是否运行在set方法前面。 展开
这个故事有点复杂,听我慢慢道来
1.如果 你注释 //System.out.println(c.get(Calendar.DAY_OF_WEEK));
即,你只有这几行代码:
c.setTime(date);
c.add(Calendar.YEAR, 20);
c.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
1.1 你会发现,你的时间如果设置的是 "2006-02-12" ,"2006-02-13" ,"2006-02-15" ,"2006-02-16" ,"2006-02-17","2006-02-18"
他们的输出结果都是:
入职20周年纪念日派对日期:2026-02-21
1.2 如果你设置的是 "2006-02-11"
那么结果是 :
入职20周年纪念日派对日期:2026-02-14
1.3 你会发现一个规律
"2006-02-12" ,"2006-02-13" ,"2006-02-15" ,"2006-02-16" ,"2006-02-17","2006-02-18" 这几个日期在当前月中第三周
对应的结果 2026-02-21 也是第三周的周六
而 "2006-02-11" 是当前月的第二周, 结果 2026-02-14 也是当前月的第二周
2. 原因
When computing time (milliseconds), GregorianCalendar leaves some fields inconsistent. Then, after the last set(DAY_OF_WEEK), an invalid (older) WEEK_OF_MONTH value is used in the last getTime() call.
When you set DAY_OF_WEEK, the calendar expects a week field (WEEK_OF_MONTH, DAY_OF_WEEK_IN_MONTH or WEEK_OF_YEAR) has also been set. So, avoid setting DAY_OF_WEEK without setting one of the week fields.
当你设置 DAY_OF_WEEK 的时候,
你需要设置 WEEK_OF_MONTH 或者 DAY_OF_WEEK_IN_MONTH 或者 WEEK_OF_YEAR,
否则会使用老的 WEEK_OF_MONTH 字段
这就是上述的
"2006-02-11" 是当前月的第二周, 结果 2026-02-14 也是第二周(old WEEK_OF_MONTH=2)的周六
"2006-02-12" ,..."2006-02-18" 在 当前月是第三周,结果 2026-02-21 也是第三周(old WEEK_OF_MONTH=3 )的周六
3.那么为毛 加上 System.out.println(c.get(Calendar.DAY_OF_WEEK)); 结果就对了呢?
嘿嘿嘿,
你换成 System.out.println(c.get(Calendar.ERA)); 结果也会对
你换成System.out.println("啦啦啦啦:" + sdf.format(c.getTime())); 结果也对
这个原因在于 ,当你调用了这些方法的时候
Calendar 会调用 java.util.Calendar.complete() 方法,
这个方法会把已经设置的参数,把 Calendar 的 17个字段都计算一下 (包括WEEK_OF_MONTH )
此时, "2006-02-14" 20年后的日期是 2026-02-14 , WEEK_OF_MONTH 是当月的第二周, 都算完成了
如果没有那句调用, 那么使用的WEEK_OF_MONTH 会使用 老的第三周 ,结果就会是 2026-02-21
4.最佳实践 (推荐指南)
真心不建议自己来写 Calendar SimpleDateFormat 来操作 ,坑比较多.
建议使用 apache commons-lang3 jar
示例: 对我来说就三步
public static void main(String[] args) throws ParseException{
//1.转成date
Date date = DateUtils.parseDate("2006-02-14", "yyyy-MM-dd");
//2.20年后的日期
Date d20 = DateUtils.addYears(date, 20);
//3.20年后的日期所在周的 周六 ,
Calendar calendar = DateUtils.toCalendar(d20);
calendar.set(DAY_OF_WEEK, SATURDAY);
System.out.println("入职20周年纪念日派对日期:" + DateFormatUtils.format(calendar.getTime(), "yyyy-MM-dd"));
}