背景
这个工具的实现属于冷门的范围。毕竟现实生活会用到自然周的地方确实不多,同时,JDK中原本所携带的SimpleDateFormat类是支持直接解析周数据的。之所以要单独实现,原因一是因为LocalDate没有直接实现这个功能,原因二则是因为原本的Date类与SimpleDateFormat类是非线程安全的,出于多方考虑,决定单独实现。
ISO自然周算法标准
自然周的标准很多,其中以ISO8061 为准。

大概的意思也就以下几点:
1.一年的首个自然周必须包含周四
2.首个自然周必须包含1月4日
3.一年的首个自然周至少要有4天
4.首个自然周所处的日历周周一对应日历的日期必须是上年12月29日到当年1月4日之间的其中一天
程序算法设计
既然知晓计算标准,那么想要实现具体的程序算法也就不远了。
实现逻辑步骤:
1.先观察四条计算标准相互之间存在的联系——如果单纯去实现每一条标准,然后再做合并计算,也不失为一种稳扎稳打的算法实现。不过本文主要是介绍简易的算法实现,并且考虑实际运行效率,做了一定程度的优化。
2.将存在关联的标准相互合并,即标准1合并标准3、标准4合并标准2(为什么会进行这种合并,后边会解释)。
3.将步骤2中合并的结果作为算法实现中的判定标准条件
程序实现(基于JAVA8以上版本)
话不多说,直接上代码
// 获取指定年份的首个自然周起始日期
public static LocalDate getFirstWeekOfYearToDate(int year) {
LocalDate first = LocalDate.ofYearDay(year, 1);
// 获取指定年1月1日对应的星期日期(1到7)
int _day_of_week = first.getDayOfWeek().getValue();
// 计算该日期在日历上到下周一之间的间隔
long step = 7L - (long) _day_of_week;
/*
* 违反标准1、3,则跳转到日历上的下周一
* 注:合并标准1、3的依据在于,
* 当包含周四时,该周的周天数一定是大于等于4天,
* 反之,当1月1日的星期日期大于周四时,一定不满足标准1、3
*/
if (_day_of_week > 4) {
return first.plusDays(step + 1);
}
/*
* 违反标准2、4,则跳转到日历上的下周一
* 注:合并标准2、4的依据在于,
* 当1月1日所处日历周的周一未处于去年12月29到当年1月4日之间时,一定会违反标准4
* 同时,当且仅当满足标准4,标准2才能成立,两个标准在日历范围内互为命题
*/
int _day_of_month_monday = first.minusDays((long) _day_of_week - 1L).getDayOfMonth();
if (_day_of_month_monday > 4 && _day_of_month_monday < 29) {
return first.plusDays(step + 1);
}
// 满足标准,当年1月1日作为首个自然周的起始日期
return first;
}
实现了首个自然周解析的算法,辣么,要实现指定年份,指定自然周对应的日期范围的解析也不难了
实现思路和上面的算法相似,唯独在自然周跳转时运用了数列的思维。
即除开首个自然周,从第二个自然周开始,是一段等差数列
/**
* 获取指定年份指定自然周的对应日期区间
* 规则:(ISO算法标准)
* 1、第一个自然周要包含周四
* 2、首个自然周天数 >= 4
* 3、包含1月4日
* 4、首个自然周周一所对应的日期必须在去年12月29日到当年1月4日之间
*
* @param year 年份
* @param _week_of_year 一年中的自然周
* @return
*/
public static Map<String, LocalDate> getWeekOfYearToDate(int year, int _week_of_year) {
if (_week_of_year <= 0)
throw new IllegalArgumentException("自然周必须大于0");
Map<String, LocalDate> result = new HashMap<>();
LocalDate start = null;
LocalDate first = LocalDate.ofYearDay(year, 1);
int _day_of_week = first.getDayOfWeek().getValue();
int _day_of_month = first.getDayOfMonth();
long step = 7L - (long) _day_of_week;
if (_day_of_week > 4) {
first = first.plusDays(step + 1);
step = 6L;
}
int _day_of_month_monday = first.minusDays((long) _day_of_week - 1L).getDayOfMonth();
if (_day_of_month_monday > 4 && _day_of_month_monday < 29) {
first = first.plusDays(step + 1);
step = 6L;
}
if (_week_of_year == 1) {
result.put("start", first);
result.put("end", first.plusDays(step));
} else {
start = first.plusDays(step + ((_day_of_month + (((long) _week_of_year - 1L) - 1L) * 7L)));
result.put("start", start);
result.put("end", start.plusDays(6L));
}
return result;
}
突发奇想的设计,欢迎大家评论,并指正错误。
网友评论