最近在业务中遇到这种状况:要求根据输入的起止日期,计算其日期间隔。例如:
起日 | 止日 | 期限 |
---|---|---|
2020-12-29 | 2021-01-20 | 23天 |
2020-12-29 | 2021-02-04 | 1个月零7天 |
2020-01-20 | 2020-03-25 | 2个月零6天 |
2020-12-29 | 2021-03-31 | 3个月零3天 |
2020-01-05 | 2020-02-04 | 1个月 |
2020-01-01 | 2020-01-31 | 1个月 |
2020-01-01 | 2020-04-30 | 4个月 |
2020-01-01 | 2020-02-29 | 2个月 |
class Moment{
constructor(date){
this.date = this.initDate(date);
}
// 初始化日期为数组格式
initDate(date){
if(date instanceof Date){
return [date.getFullYear(), date.getMonth()+1, date.getDate()];
} else if(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(date)){
return date.split('-').map(item=>Number(item));
}else if(date == undefined){
var cur = new Date();
return [cur.getFullYear(), cur.getMonth()+1, cur.getDate()];
}else{
throw Error('Invalid Date')
}
}
// 是否为闰年:
// 能被4整除,且不能被100整除
// 能被400整除
isLeapYear = function(year) {
year = year || this.date[0];
if (typeof year == "number" || Number(year) !== NaN) {
if ((year % 4 == 0 && year % 100 !== 0) || year % 400 == 0) {
return true;
}
return false;
} else {
throw Error("Invalid Date");
}
}
// 判断传入的日期与当前日期哪个在前,哪个在后
isSameOrBefore = function(dateStr){
const [year, month, day] = this.date;
const [curYear, curMonth, curDay] = this.initDate(dateStr);
if(year < curYear){
return true;
} else if(year > curYear){
return false;
} else if(year == curYear){
if(month < curMonth){
return true;
} else if(month > curMonth){
return false
} else if(month == curMonth){
if(day <= curDay){
return true;
}else if(day > curDay){
return false;
}
}
}
}
// 获取某个月的最后一天(或理解为:获取某个月共有多少天)
endOfMonth = function (year = this.date[0], month = this.date[1]) {
const mons = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if (month == 2 && this.isLeapYear(year)) {
return 29;
}
return mons[month - 1];
}
// 在当前日期的基础上减去几个月/几天
subtract = function (num, type ) {
let [year, month, day] = this.date;
let lastYear = year, lastMonth = month;
if(type == 'day'){
return this.subtractDay(lastYear, lastMonth, day - num, );
} else if(type == 'month'){
return this.subtractMonth(lastYear, lastMonth - num, day)
}
}
subtractDay = function(year, month, day){
if(day <= 0){
if(month - 1 <= 0) {
month = 12;
year --;
}else{
month -= 1;
}
const lastEndDay = this.endOfMonth(year, month)
day = lastEndDay - (- day);
return this.subtractDay(year, month, day);
}else{
return [year, month, day]
}
}
subtractMonth = function(year, month, day){
if(month <= 0){
year --;
return this.subtractMonth(year, 12 - (-month), day)
}else{
const lastEndDay = this.endOfMonth(year, month)
if(lastEndDay > day){
return [year, month, day]
}else{
return [year, month, lastEndDay]
}
}
}
}
const diff = function(date1, date2){
const moment1 = new Moment(date1);
const moment2 = new Moment(date2);
const isSameOrBefore = moment1.isSameOrBefore(date2);
let minuYear, minuMonth, minuDay, // 两日期直接相减的差值
endDay, // 较大日期的最后一天
lastEndDay; // 较大日期的上个月的最后一天
if(isSameOrBefore){
[minuYear, minuMonth, minuDay] = moment1.date.map((n,i)=> moment2.date[i] - n);
endDay = moment2.endOfMonth();
lastEndDay = new Moment(date2.slice(0,8) + '01').subtract(1, 'day')[2];
}else{
throw Error('止日不得小于起日!')
}
minuDay ++;
if(minuDay < 0){
minuMonth --;
minuDay = lastEndDay - moment1.date[2] + 1 + moment2.date[2];
} else if(minuDay == endDay){
minuMonth += 1;
minuDay = 0;
}
if(minuMonth < 0){
minuMonth += 12;
minuYear --;
}
// 整理返回值 格式
minuYear = minuYear ? `${minuYear}年` : '';
minuMonth = minuMonth ? `${minuMonth}个月` : '';
minuDay = minuDay ? ((minuMonth || minuYear) && minuDay < 10 ? `零${minuDay}天` : `${minuDay}天`) : '';
return minuYear + minuMonth + minuDay;
}
接下来按照一开始给出的示例进行测试:
console.log(diff('2020-12-29', '2021-01-20'))
console.log(diff('2020-12-29', '2021-02-04'))
console.log(diff('2020-01-20', '2020-03-25'))
console.log(diff('2020-12-29', '2021-03-31'))
console.log(diff('2020-01-05', '2020-02-04'))
console.log(diff('2020-01-01', '2020-01-31'))
console.log(diff('2020-01-01', '2020-04-30'))
console.log(diff('2020-01-01', '2020-02-29'))