最近改项目中的bug,遇到一类问题:当月起始日能够设置的状况下(1日到28日),须要计算出对应的月、季度、年等相关的时间范围,以及对应的如上月、上季、去年等,和给定时间戳的所属年、季度、月份等各类时间求取。bash
为简单起见,举个栗子: 如若是月起始日设置成了5,那么本月的时间范围是:工具
2019.10.05 00:00:00 - 2019.11.04 23:59:59
复制代码
本季度的时间范围是:ui
2019.10.05 00:00:00 - 2020.01.04 23:59:59
复制代码
项目中大量的地方须要用到此类计算。所以,相应的时间获取被封装成了工具类。这自己并无什么问题,但问题在于封装的工具类中每一个时间周期的获取写法多种多样,例如获取上月起始时间,下个季度起始时间等等方法思路各不相同,加上不一样的人都有去实现本身须要的方法,所以,整个工具类看下来,一是很差理解,二是各类潜藏的bug。spa
见得最多的,是判断各类边界状况,而后对应各类处理逻辑。。code
有没有简单些的方法呢?get
实际上是有的。ast
不管月起始日设置成哪一天,在作月起始日相关的任何逻辑计算时,其实均可以采起一种通用的实现思路,即时间误差的修正与回归。例如,无论当前时间戳是多少,要计算本季度的起始时间,直接将时间戳先修正,而后计算出对应的天然时间概念下的季起始时间,最后再回补上对应的时间误差便可。class
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产生,理解了这种思路后,不管求月起始日对应的什么时间周期,代码实现也都很简单。方法
月起始日引发的时间周期的变化,缘由在于月起始日发生了变动。所谓解铃还须系铃人,与其封装形式和逻辑各样,作各类边界计算的处理,甚至弄很差还会出现各类莫名的bug。还不如将思路更多集中在月起始日自己,寻求更加通用的计算方式。
end~