一、背景
最近改项目中的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
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。