基于时间偏差思路下的时间周期度量

一、背景

最近改项目中的bug,遇到一类问题:当月起始日可以设置的情况下(1日到28日),需要计算出对应的月、季度、年等相关的时间范围,以及对应的如上月、上季、去年等,和给定时间戳的所属年、季度、月份等各种时间求取。

为简单起见,举个栗子: 如如果月起始日设置成了5,那么本月的时间范围是:

2019.10.05 00:00:00 - 2019.11.04 23:59:59
复制代码

本季度的时间范围是:

2019.10.05 00:00:00 - 2020.01.04 23:59:59
复制代码

项目中大量的地方需要用到此类计算。因此,相应的时间获取被封装成了工具类。这本身并没有什么问题,但问题在于封装的工具类中每个时间周期的获取写法多种多样,例如获取上月起始时间,下个季度起始时间等等方法思路各不相同,加上不同的人都有去实现自己需要的方法,因此,整个工具类看下来,一是不好理解,二是各种潜藏的bug。

见得最多的,是判断各种边界情况,然后对应各种处理逻辑。。

有没有简单些的方法呢?

其实是有的。


二、思路与实现

2.1 思路

无论月起始日设置成哪一天,在做月起始日相关的任何逻辑计算时,其实都可以采取一种通用的实现思路,即时间偏差的修正与回归。例如,不管当前时间戳是多少,要计算本季度的起始时间,直接将时间戳先修正,然后计算出对应的自然时间概念下的季起始时间,最后再回补上对应的时间偏差即可。

2.2 实现

Talk is cheap. Show me the code.
复制代码

/**
 *  根据月起始日,计算对应时间戳的季度开始时间
 *
 * @param monthStart
 * @param timeStamp
 * @return
 */
public static long getFixedQuarterBeginTimeBySetting(int monthStart, long timeStamp){
    Calendar c = Calendar.getInstance();
    c.setTimeInMillis(timeStamp);

    // 修正偏移时间
    c.add(Calendar.DAY_OF_MONTH, -monthStart + 1);

    // 获得修正后的自然季度开始时间
    long fixedNatureQuarterBeginTime = DateUtils.getNatureQuarterBeginTimeInMillis(c.getTimeInMillis());
    c.setTimeInMillis(fixedNatureQuarterBeginTime);

    // 回补月起始日
    c.add(Calendar.DAY_OF_MONTH, monthStart - 1);

    return c.getTimeInMillis();
}

/**
 *  根据月起始日,计算对应时间戳的季度结束时间
 *
 * @param monthStart
 * @param timeStamp
 * @return
 */
public static long getFixedQuarterEndTimeBySetting(int monthStart, long timeStamp){
    // 获取季度开始时间
    long fixedQuarterBeginTime = getFixedQuarterBeginTimeBySetting(monthStart, timeStamp);

    Calendar c = Calendar.getInstance();
    c.setTimeInMillis(fixedQuarterBeginTime);
    // 月份加3
    c.add(Calendar.MONTH, 3);

    // 时间戳退1
    return c.getTimeInMillis() - 1;
}

/**
 *  根据月起始日,计算对应时间戳的上个季度开始时间
 *
 * @param monthStart
 * @param timeStamp
 * @return
 */
public static long getFixedLastQuarterBeginTimeBySetting(int monthStart, long timeStamp){
    long fixedQuarterBeginTime = getFixedQuarterBeginTimeBySetting(monthStart, timeStamp);

    return getFixedQuarterBeginTimeBySetting(monthStart, fixedQuarterBeginTime - 1);
}

/**
 *  根据月起始日,计算对应时间戳的上个季度结束时间
 *
 * @param monthStart
 * @param timeStamp
 * @return
 */
public static long getFixedLastQuarterEndTimeBySetting(int monthStart, long timeStamp){
    long fixedQuarterEndTime = getFixedQuarterBeginTimeBySetting(monthStart, timeStamp);

    return fixedQuarterEndTime - 1;
}

/**
 * 根据月起始日,计算对应时间戳的年开始时间
 *
 * @param monthStart
 * @param timeStamp
 * @return
 */
public static long getFixedYearBeginTimeBySetting(int monthStart, long timeStamp) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(timeStamp);

    // 修正偏差
    calendar.add(Calendar.DAY_OF_MONTH, -monthStart + 1);

    // 获取修正偏差后的自然时间
    long fixedNatureYearBeginTime = DateUtils.getNatureYearBeginTime(calendar.getTimeInMillis());

    calendar.setTimeInMillis(fixedNatureYearBeginTime);

    // 回补月起始日
    calendar.add(Calendar.DAY_OF_MONTH, monthStart - 1);

    return calendar.getTimeInMillis();
}
复制代码

很轻松的,我们将原本可能需要的复杂的时间范围计算方式,统统基于同样的思路,即时间偏差的修正与回归,转变成了求得修正后的时间戳后的自然时间周期,最后再回归到最终想要的结果。

这样一个最明显的好处是,整个工具类实现思路是完全一致的,且在求取时间周期时,一般情况下,也不会有什么bug产生,理解了这种思路后,无论求月起始日对应的什么时间周期,代码实现也都很简单。


三、结语

月起始日引起的时间周期的变化,原因在于月起始日发生了变更。所谓解铃还须系铃人,与其封装形式和逻辑各样,做各种边界计算的处理,甚至弄不好还会出现各种莫名的bug。还不如将思路更多集中在月起始日本身,寻求更加通用的计算方式。

end~


作者:HappyCorn
链接:https://juejin.im/post/5db188586fb9a020531fab1d
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2019-12-05 20:43  HappyCorn  阅读(315)  评论(0编辑  收藏  举报