JAVA自然周解析

作者: Fearon | 来源:发表于2020-03-18 16:26 被阅读0次

背景

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

ISO自然周算法标准

自然周的标准很多,其中以ISO8061 为准。


image.png

大概的意思也就以下几点:
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;
    }

突发奇想的设计,欢迎大家评论,并指正错误。

相关文章

网友评论

    本文标题:JAVA自然周解析

    本文链接:https://www.haomeiwen.com/subject/oixcyhtx.html