日期按月、按周次分组
今天接到需求,要求按月份分开周次
12月31号和新一年的1月1号拆开两个组:[{‘2020-12 第五周 ’:['2020-12-28','2020-12-31']},{‘2021-01 第一周 ’:['2021-01-01','2021-01-03']}]
咋一看,这不搞事情嘛。。。
先把最基本的工具类的函数熟悉一下:
calendar.get(Calendar.WEEK_OF_MONTH) // 这个月在第几周
calendar.getActualMaximum(Calendar.DAY_OF_MONTH) //当月最大天数
calendar.get(Calendar.DAY_OF_WEEK) //周几
calendar.setFirstDayOfWeek(Calendar.MONDAY); //设置周一为一周的开始
好像该有的函数都有了,那就开始撸了
噼里啪啦整了一顿之后,终于整出了一个初版,测试多次,暂无发现有逻辑BUG,如有望及时提出
测试按月分组一年的数据,耗时约16ms
暂时收工,源码奉上:
/**
* 根据时间刻度 转化成对应的周
* eg: 2021-01-01 - 2020-03-05
*
* @return {
* "2021-01 第一周":['2021-01-01','2021-01-03']
* "2021-01 第二周":['2021-01-04','2021-01-10']
* ....
* "2021-03 第一周":['2021-03-01','2021-03-05']
* }
*/
public static Map<String, Date[]> getWeekOfMonthStartAndEnd(String startDate, String endDate) throws ParseException {
final int dayTimeOffset = 24 * 60 * 60 * 1000;
final String formatKey = "%s 第%d周";
checkNotEmpty(startDate, endDate);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat keyFormat = new SimpleDateFormat("yyyy-MM");
Date _start = format.parse(startDate);
Calendar calendar = Calendar.getInstance();
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setTime(_start);
if (startDate.equals(endDate)) {
//同一天
int idx = calendar.get(Calendar.WEEK_OF_MONTH);
return Collections.singletonMap(String.format(formatKey, startDate.substring(0, 7), idx), new Date[]{_start, _start});
}
Date _end = format.parse(endDate);
//日期偏移量 (日)
long dayOffset = (_end.getTime() - _start.getTime()) / dayTimeOffset + 1;
if (dayOffset < 0) {
throw new IllegalArgumentException("结束时间必须大于开始时间");
}
// --------------------------------处理数据-------------------------------//
long spanDay = 0; // 累加日期
int last;
Map<String, Date[]> result = new LinkedHashMap<>((int) (spanDay / 7) + 1);
do {
//result Key 值
int idx = calendar.get(Calendar.WEEK_OF_MONTH); //当前在这个月的第几周
String key = String.format(formatKey, keyFormat.format(calendar.getTime()), idx);
// 0 星期天 1 周一 2 周二 3 周三 4 周四 5 周五 6 周六
int week = calendar.get(Calendar.DAY_OF_WEEK) - 1;// 周几
int maxDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); // 当前月最大的天数
int days = calendar.get(Calendar.DAY_OF_MONTH);
last = maxDays - days;
//选择开头的一天
Date date1 = calendar.getTime(); // value[0]
if (week > 0) {
//这周剩下的几天
int i = 7 - week;
//添加剩余的几天 30 31 / 01 ==> add 2 days
if (i > last) {
calendar.add(Calendar.DAY_OF_MONTH, last);
result.put(key, new Date[]{date1, calendar.getTime()});
calendar.add(Calendar.DAY_OF_MONTH, 1); // 到达下一个月
spanDay = spanDay + last + 1;
} else if (i + spanDay >= dayOffset) {
//最后一轮
result.put(key, new Date[]{date1, _end});
spanDay = dayOffset;
} else {
//添加剩余一整周
calendar.add(Calendar.DAY_OF_MONTH, i);
result.put(key, new Date[]{date1, calendar.getTime()});
spanDay = spanDay + i + 1;
calendar.add(Calendar.DAY_OF_MONTH, 1); // 到达下一周周一
}
} else {
result.put(key, new Date[]{date1, date1});
calendar.add(Calendar.DAY_OF_MONTH, 1); // 到达下一周周一
spanDay++;
}
} while (spanDay < dayOffset);
return result;
}
private static void checkNotEmpty(String... args) {
for (String arg : args) {
if (StringUtils.isEmpty(arg)) {
throw new IllegalArgumentException("argument is empty!");
}
}
}
注意:SimpleDateFormat会涉及线程安全的问题,如果需要频繁调用的情景下,建议使用ThreadLocal对象