根据经验,先来思考两个题目
题一:尧尧2018年11月25日出生
问:1、尧尧满月是哪天?2、2019年4月10日尧尧多大?
题二:小明2018年11月29日入职新公司,试用期三个月
问:小明哪天转正?
间隔计算基本规则(参考iOS和Python的计算规律)
已知参数:开始日期startDate 和 结束日期endDate
分别对应的年月日为sYear,sMonth,sDay 和eYear,eMonth,eDay;
-
间隔的天数
eDay-sDay+1 结果就是间隔的天数
例如 20190101与20190103 就是3-1+1=3天
-
间隔满月的概念(天数达到满月,月份+1)
sMonth+1;sDay-1 的日期 即为满月。(对于日期比较,大家可以把下个月理解为结束日期的那个月,eMonth)
简单的可以理解为:下个月的sDay的前一天为满月,例如20190110的满月就是20190209。不过,在计算的时候满月并不是那么简单,有一些边界情况需要特殊处理
- sDay 大于下个月的天数
此时满月应该是下个月的倒数第二天,例如1月31日的满月是2月27(闰年是2月28),因为2月不可能有31天,那2月的最后一天可以理解为下个月的sDay,那满月就是下个月的的倒数第二天 - sDay=1,即:月初第一天,按照我们定义的逻辑,则应该是下个月月初一号的前一天,即当月的月末是满月
代码实现
iOS
iOS系统有日期间隔计算的API,直接调用即可
+(NSDateComponents *)calculateDeltaYMDWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate{
BOOL flag = [startDate compare:endDate];
if (nil == startDate||nil == endDate || flag > 0) {
return nil;
}
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSCalendarUnit calendarUnit = NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay;
NSDateComponents *deltaComponents = [calendar components:calendarUnit fromDate:startDate toDate:endDate options:0];
return deltaComponents;
}
Android
Android 系统没有日期间隔计算的API,需要自己计算
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public final class DateUtil extends Object {
public static DateComponents calculateDateDeltaYMD(Date startDate, Date endDate){
DateComponents deltaComp = new DateComponents();
int flag = startDate.compareTo(endDate);
if (null == startDate || null == endDate || flag > 0)
return deltaComp;
//1、 先转化成Calendar对象计算
Calendar startCalendar = Calendar.getInstance(Locale.CHINA);
startCalendar.setTime(startDate);
Calendar endCalendar = Calendar.getInstance(Locale.CHINA);
endCalendar.setTime(endDate);
//2、提取 年月日数据
int sYear = startCalendar.get(Calendar.YEAR);
int sMonth = startCalendar.get(Calendar.MONTH);
int sDay = startCalendar.get(Calendar.DAY_OF_MONTH);
int eYear = endCalendar.get(Calendar.YEAR);
int eMonth = endCalendar.get(Calendar.MONTH);
int eDay = endCalendar.get(Calendar.DAY_OF_MONTH);
int eMonthDays = endCalendar.getActualMaximum(Calendar.DAY_OF_MONTH);
//3、开始计算
int deltaYear = eYear-sYear;
int deltaMonth = eMonth-sMonth;
int deltaDay = eDay-sDay+1;
boolean eDayMonthEnd = eDay >= eMonthDays-1;
//4、特殊处理
// 1)先处理天
if (deltaDay <= 0 && eDayMonthEnd){ //计算日期月末的情况
deltaDay = eDay-eMonthDays+1;
}
if (deltaDay < 0){ //当前天小于比较天
//此时应该使用上月月末的日期计算差值,然后加上现在的天数
Calendar ePreMonthCalendar = Calendar.getInstance(Locale.CHINA);
ePreMonthCalendar.setTime(endDate);
ePreMonthCalendar.add(Calendar.MONTH, -1);
ePreMonthCalendar.set(Calendar.DAY_OF_MONTH,ePreMonthCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
DateComponents preComp = calculateDateDeltaYMD(startDate, ePreMonthCalendar.getTime());
deltaYear = preComp.year;
deltaMonth = preComp.month;
deltaDay = preComp.day+eDay;
} else if (deltaDay >= eMonthDays){ //超月的情况 开始是月初一号,结束是月末的情况
deltaDay -= eMonthDays;
deltaMonth ++;
}
// 2)再处理月
if (deltaMonth < 0){ //月份小于的情况
deltaMonth += 12;
deltaYear --;
} else if (deltaMonth > 12){ //月份超出的情况
deltaMonth -= 12;
deltaYear ++;
}
//5、组装数据
deltaComp.year = deltaYear;
deltaComp.month = deltaMonth;
deltaComp.day = deltaDay;
return deltaComp;
}
public static class DateComponents{
int year,month,day;
public DateComponents(){
year=month=day=0;
}
}
}
写在最后
- 最开始两个问题大家应该有答案了吧
- 如有错误欢迎指正,如有更好的实现方式,欢迎留言讨论
Android实现代码,部分参考如下文章 ,其中借位计算的逻辑是不对的
https://blog.csdn.net/qq_23940659/article/details/50731398
有兴趣的同学也可以看下Python的工具类实现,和iOS系统API一样,支持更细粒度的时间间隔计算
https://github.com/dateutil/dateutil/blob/70b4b78b38b6c0e48cab81a545b5051b1d38b462/dateutil/relativedelta.py