Java计算日期在当月是第几周
- 背景
- Java LocalDate API
- 每月第1个周一为该月第一周的做法
- 每月1号为第一周做法
背景
公司项目需求是统计周报,一开始我的做法是按每月1号开始为第一周统计周报,比如2020-05-01到2020-05-03属于5月第一周,2020-05-04到2020-05-10为5月第二周。
产品小姐姐看了觉得不对,账单周报统计,是以一周7天为维度,如果按照上面的做法,统计出来5月第一周的周报只有3天,4月最后一周只有4天,这种不符合账单统计型产品的需求。
后面又拿出支付宝的周报账单给我看
看了看支付宝的做法,发现这是以每月第一个星期一作为这个月一周的开始。(好吧,原来这就是所谓的自然周,又得重写打标了)
第一步当然先去Google百度,但是找了一段时间都没有想要的,不是这个抄那个,就是太杂太乱。总之最后决定还是自己写这个逻辑了。
Java LocalDate API
每月第1个周一为该月第一周的做法
其实知道了规则之后,做法和思路很简单。
- 获得日期的所在周的周一
- 获得日期这个月的第一个周一
- 根据两个周一判断是这个月的第几周
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | /** * 传入日期判断属于哪一年哪一月第几周 * * @apiNote 以每月的第一个周一所在的周作为每月第一周 * @param date 时间格式(yyyyMMdd) 20200701 * @return java.lang.String 返回字符串(2020-06-W5) 2020年6月第5周 * @author kiring */ public String getWeekOfMonthByDay(int date){ DateTimeFormatter dfDay = DateTimeFormatter.ofPattern("yyyyMMdd"); LocalDate localDate = LocalDate.of(date / 10000, (date / 100) % 100, date % 100); // 获得当前日期的所在周的周一(previousOrSame:如果当前日期是周一,就返回当前日期) LocalDate localDateMondy = LocalDate.of(date / 10000, (date / 100) % 100, date % 100).with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); LocalDate firstMonday = null; // 获得这个月的第一个周一(这个月的第一周) for(int day=1; day<=7; day++){ DayOfWeek dayOfWeek = LocalDate.of(date / 10000, (date / 100) % 100, day).getDayOfWeek(); if(DayOfWeek.MONDAY == dayOfWeek){ firstMonday = LocalDate.of(date / 10000, (date / 100) % 100, day); break; } } String outYear = null; String outMonth = null; String outWeek = null; // 根据两个周一判断是这个月的第几周 if(firstMonday.isBefore(localDateMondy) || firstMonday.isEqual(localDateMondy)){ //a. 如果当月第一个周一小于等于当前日期所在的周一 outYear = localDateMondy.format(dfDay).substring(0,4); outMonth = localDateMondy.format(dfDay).substring(4,6); outWeek = String.valueOf((localDateMondy.toEpochDay() - firstMonday.toEpochDay())/7 + 1); return outYear + "-" + outMonth + "-" + "W" + outWeek; } else { //b. 如果当月第一个周一比当前日期所在的周一还要大 // 计算上一个月最后一天所在周 LocalDate lastMonthDate = localDate.minusMonths(1).with(TemporalAdjusters.lastDayOfMonth()); Integer lastMonthDay = Integer.valueOf(lastMonthDate.format(dfDay)); return getWeekOfMonthByDay(lastMonthDay); } } |
这里注意考虑两个点,
一是传进来的日期如果刚好是周一的情况,
二是传来的日期如果是上一周的最后一周,也就是当月第一个周一大于当前日期所在的周一的情况,这里我直接使用的是递归来计算
每次传值进来来都会循环计算当月的第一个周一,读者可以自行做缓存处理。
每月1号为第一周做法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /** * 计算日期在月中的周 * @param day 时间格式如:20200606 * @return java.lang.Integer 日期在月中的周数 * @author kiring */ public Integer calculateWeekInMonth(Integer day) { if(day < 19700101 || day > 99999999){ throw new RuntimeException("时间不正确"); } // ISO算法:每个月的第一周至少4天,如果小于4天,算出来是第0周 // WeekFields weekFields = WeekFields.ISO; // 以周一作为一周的开始,每周至少一天 WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY,1); int monthInWeek = LocalDateTime.of(day / 10000, (day / 100) % 100, day % 100, 0, 0, 0).atZone(ZoneOffset.ofHours(8)).get(weekFields.weekOfMonth()); return monthInWeek; } |