`

你可能不知道的 Calendar 之 DAY_OF_WEEK

阅读更多

 

1.背景:

百度知道上有人咨询了 Calendar 设置 Calendar.DAY_OF_WEEK不正确的问题 参见 http://zhidao.baidu.com/question/748936560786113052

他的入职时间是 "2006-02-14",要计算 20年后的所在周的周六

理论上, 2026-02-14所在周的周六,正好就是 2026-02-14

他写的代码是

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);
        c.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
        //********************************************************************

        System.out.println("入职20周年纪念日派对日期:"+sdf.format(c.getTime()));
    }
}

看上去没毛病,但是结果死活就是 2026-02-21

但是,在中间插入一行代码 System.out.println(c.get(Calendar.DAY_OF_WEEK));

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()));
    }
}

结果就是正确的 2026-02-14

是不是很神奇?颠覆世界观,觉得不可思议?

2.原因

原因在于,当你设置 DAY_OF_WEEK 的时候, 你需要设置 WEEK_OF_MONTH 或者 DAY_OF_WEEK_IN_MONTH 或者 WEEK_OF_YEAR, 否则会使用老的WEEK_OF_MONTH 字段

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_MONTHDAY_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.

原先的这段代码

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);
        c.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
        //********************************************************************

        System.out.println("入职20周年纪念日派对日期:"+sdf.format(c.getTime()));
    }
}

由于只设置了 Calendar.DAY_OF_WEEK ,那么计算的时间结果是 20年后的第3周的周六 (因为没有设置WEEK_OF_MONTH,那么取的是老的"2006-02-14" 的WEEK_OF_MONTH )
而 "2006-02-14" 是所在月的第3周, 那么 20年后的第3周的周六 结果就是 2026-02-21 了

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 是当月的第二周, 都算完成了

4.最佳实践 (推荐指南)

真心不建议自己来写 Calendar SimpleDateFormat 来操作 ,坑比较多.

建议使用 apache commons-lang3 jar 或者 joda-time

示例: 对我来说就三步

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"));
}

你也可以使用 feilong-core jar

示例: 对我来说就三步

    public static void main(String[] args){
        //1.转成date
        Date date2 = DateUtil.toDate("2006-02-14", DatePattern.COMMON_DATE);

        //2.20年后的日期
        Date d20 = DateUtil.addYear(date2, 20);

        //3.20年后的日期所在周的 周六
        System.out.println("入职20周年纪念日派对日期:" + DateUtil.toString(getLastDateOfThisWeek(d20), DatePattern.COMMON_DATE));
    }

5.参考

分享到:
评论

相关推荐

    SYS_CALENDAR_DATE.sql

    `WEEK_OF_YEAR` int(10) NOT NULL COMMENT '获得指定日期是所在年份的第几周', `WEEK_OF_MONTH` int(10) NOT NULL COMMENT '获得指定日期是所在月份的第几周', `DAY_OF_MONTH` int(10) NOT NULL COMMENT '获得...

    java 获取时间 本周本年本月第一天最后一天

    c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek()); // Monday System.out.println(c.getTime()); c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek() + 6); // Sunday //本周第一天,以星期日开始 。。。...

    java时间处理工具类--CalendarUtil(java源码)

    int first_day_of_week = now.get(Calendar.DATE) + 2 - today; // 星期一 now.set(Calendar.DATE, first_day_of_week); return now.getTime(); } /** * 获得所在星期的最后一天 */ public static ...

    周日历选择插件jcalendar_week

    $(obj).addClass("calendar_day_act").siblings().removeClass("calendar_day_act"); } }); 点击上方显示当前年份和周的DOM部分可选择并跳转到指定年份和周。 插件提供的方法: //获取周第一天方法weekfirstdate...

    java时间处理工具类--DateUtils

    cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); cal.add(Calendar.DAY_OF_MONTH, 6); return cal.getTime(); } /** * 获取指定时间范围的第一天 * * @param dateRangeType * 时间范围类型 * ...

    Day-of-year-and-week.c.zip_This Is the Day

    a program to prompt for the Grogorian calendar day, (i) the total number of days January 1st, year 1 to this day, (ii) the total number of days January 1st of the year to the day, and (iii) the day of...

    Determining Day of Week

    In the Gregorian calendar, which is widely used now, the year AD 1 is directly preceded by the year 1 BC; a year 0 does not exist in this system. In contrast, astronomical reckoning indeed uses a year...

    calendar.java

    通过Calendar对象的get(Calendar.DAY_OF_WEEK)方法可以获得Calendar对象为星期几(星期天为1,星期六为7)。 注意: Calendar中的月份从0开始(1月为0,12月为11),日期从1开始(1日为1,28日为28); 12生肖的...

    java时间格式大全(算法源码)

    java时间格式大全(算法源码) java,date,时间,时间格式,算法源码 * * @return */ public static String getTimeShort() { SimpleDateFormat formatter = new ... if (cal1.get(Calendar.WEEK_OF_YEAR) == ...

    swift实现的Week & Day 日历控件.zip

    swift实现的Week & Day 日历控件.zip,Calendar Week & Day View in iOS Swift

    时间控件,个人喜欢用的js时间控件

    // table of short day names var ar = new Array(); for (var i = 8; i > 0;) { ar[--i] = Calendar._DN[i].substr(0, 3); } Calendar._DN3 = ar; // table of short month names ar = new Array(); for ...

    jar编写的日程管理系统

    int i=calendar.get(Calendar.DAY_OF_WEEK)-1; if(i==0) i=7; return i; } static boolean in_term(Date date){ int[] term=WeekInfStore.get_term_date(); Calendar c1=Calendar.getInstance()...

    如何打印2019年每个月的第一个星期天的日期

    但Calendar类Calendar.WEEK_OF_MONTH属性,即这一周在一个月中属于第几周这一属性设计的很不合我们的习惯,他是以周六作为一周的结束,比如19年的6月1日,恰好在周六,那么第一周就只有这一天,而作为周日的2号就在第...

    JSP万年历 JSP万年历

    int firstIndex = thisMonth.get(Calendar.DAY_OF_WEEK) - 1; int maxIndex = thisMonth.getActualMaximum(Calendar.DAY_OF_MONTH); for (int i = 0; i ; i++) { days[firstIndex + i] = String.valueOf(i + 1);...

    Android代码-Android-Week-View

    Android Week View is an android library to display calendars (week view or day view) within the app. It supports custom styling. Features Week view calendar Day view calendar Custom styling ...

    dhtmlxScheduler_v30_120111.zip

    The events can be displayed in different views (Day, Week, Month, Year, Agenda, etc.) Advanced drag-and-drop functionality allows users to change date and time of an event by easily moving or ...

    界面标准的JAVA日历记事本

    在得到日期的同时判断DAY OF WEEK星期几,在窗体上显示 5为各个按钮及标签添加监听 通过ActionPerform MousePressed实现其动作 6 保存日志 显示日志及删除日志,总过创建哈希表来进行文件的读写

    DS1306 Serial Alarm Real Time Clock (RTC)

    day of the week 1 Hz and 32.768 kHz clock outputs Serial interface supports Motorola Serial Peripheral Interface (SPI) serial data ports or standard 3-wire interface Burst Mode for reading/...

    calendar-converter.zip

    calendar-converter 是 JavaScript 版本的公历和农历的互转库。 示例代码: var cc =new CalendarConverter; cc.lunar2solar(new Date(2011, 0, 3)); ---> 2010,11,29 cc.solar2lunar(new Date(2010, 10, 29...

    Java电子日历设计.doc

    Java程序设计 课程设计报告 设计题目:电子日历设计 班级: 学号: 姓名: 1. 需求分析 设计并实现一个电子日历,当用户在下拉列表中选择年份... int week=date.get(Calendar.DAY_OF_WEEK)-1; int day=0; if(month==1

Global site tag (gtag.js) - Google Analytics