前言
最近接到這樣一個需求:
業務場景: 在線簽訂電子合同;
具體需求: 獲取合同的起始時間與終止時間,以用戶點擊簽約爲起始時間,計算用戶在頁面中選擇的合約生效時長(可選3個月、6個月、1年等等)並推算出終止時間;
實現難點: 可能存在大月小月以及平年閏年時二月的天數轉換問題(例如:用戶在2020年1月31號簽約,選擇合約生效時長爲3個月,但是2020年4月份只有30天,按我接到的需求這裏應該設置終止時間爲2020-04-30);
代碼
核心
dateFormmat : function(date, format){
if(null==date){
return ''
}
if(typeof(date)=='string' || typeof date === 'number'){
date = (typeof date === 'number') ? new Date(date) : new Date((date || '').replace(/-/g, '/'))
}
var o = {
"M+" : date.getMonth()+1, //month
"d+" : date.getDate(), //day
"h+" : date.getHours(), //hour
"m+" : date.getMinutes(), //minute
"s+" : date.getSeconds(), //second
"q+" : Math.floor((date.getMonth()+3)/3), //quarter
"S" : date.getMilliseconds() //millisecond
}
if(/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length));
}
for(var k in o) {
if(new RegExp("("+ k +")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length));
}
}
return format;
},
/**
* 在線生成合約
* 當用戶點擊簽約按鈕時 就是合約的起始時間
* 截止時間爲 用戶選擇的月數(或者是年數)累加起始時間
* 要解決的問題 :
* 1.如在2020年2月29號簽約,簽約時間爲1年,那麼截止時間就只能返回2021年2月28號;
* 2.如在2020年1月31號簽約,簽約時間爲1個月,那麼截止時間就只能返回2020年2月29號;
* @param {*} date 要累加的時間
* @param {*} num 累加多少個月 1年就是12個月
*/
timeTransformation : function(date,num) {
var day = date.getDate(),
month = date.getMonth(),
year = date.getFullYear(),
dateArr = this.dateFormmat(date,'yyyy-MM-dd hh:mm:ss').split(' ');
year = year + parseInt((month + num) / 12);
month = (month + num) % 12;
//0-11 轉變爲 1-12
month += 1;
// 大月31天,小月30天,2月份只有28天(平年)或29天(閏年)
// 每年一﹑三﹑五﹑七﹑八﹑十﹑十二這七個月爲大月,均三十一天
switch(month){
case 1 :
case 3 :
case 5 :
case 7 :
case 8 :
case 10 :
case 12 :
day > 31 ? day = 31 : '';
break;
case 4 :
case 6 :
case 9 :
case 11 :
day > 30 ? day = 30 : '';
break;
case 2 :
this.isLeapYear(year) ? (day > 29 ? day = 29 : '') : (day > 28 ? day = 28 : '');
break;
default :
break;
}
month < 10 ? (month = '0' + month) : '';
day < 10 ? (day = '0' + day) : '';
return year + '-' + month + '-' + day + ' ' + dateArr[1];
},
/**
* 是否爲閏年
* @param {*} year
*/
isLeapYear : function(year) {
// 普通閏年:公曆年份是4的倍數的,且不是100的倍數,爲普通閏年。(如2004年就是閏年)
// 世紀閏年:公曆年份是整百數的,必須是400的倍數纔是世紀閏年(如1900年不是世紀閏年,2000年是世紀閏年)
return ((year % 4 == 0) && (year % 100 != 0)) ? true : ((year % 400) == 0) ? true : false;
}
測試代碼
運行環境:nodejs;
判定依據:以當前時間、2020-01-31 00:00:00、2020-02-29 00:00:00爲起始時間,合約生效時長涵蓋400年,計算出合約終止時間,將終止時間轉時間戳後再轉string並與轉換前進行對比;
例如:
var testDate = new Date('2021-02-29 00:00:00'.replace(/-/g,"/")),
timestamp = Date.parse(testDate);
console.log(dateUtils.dateFormmat(timestamp,'yyyy-MM-dd hh:mm:ss'));//2021-03-01 00:00:0000:00:0000:00:00
2021-02-29是不存在的 將它轉時間戳後 所生成的時間戳就是轉化2021-03-01 00:00:00的時間戳
具體代碼:
var assert = require('assert'),
dateUtils = require('../src/dateUtils').dateUtils;
(()=>{
var nowDate = new Date(),
monthCount = 400 * 12;//400年 這樣看方便點
for(var i = 1;i<=monthCount;i++){
var time = dateUtils.timeTransformation(nowDate,i),
timestamp = Date.parse(time);
assert.ok(time === dateUtils.dateFormmat(timestamp,'yyyy-MM-dd hh:mm:ss'),"出現不匹配時間");
}
console.log('end');
})();
(()=>{
var testDate = new Date('2020-01-31 00:00:00'.replace(/-/g,"/"));
monthCount = 400 * 12;//400年 這樣看方便點
for(var i = 1;i<=monthCount;i++){
var time = dateUtils.timeTransformation(testDate,i),
timestamp = Date.parse(time);
assert.ok(time === dateUtils.dateFormmat(timestamp,'yyyy-MM-dd hh:mm:ss'),"出現不匹配時間");
}
console.log('end');
})();
(()=>{
var testDate = new Date('2020-02-29 00:00:00'.replace(/-/g,"/"));
monthCount = 400 * 12;//400年 這樣看方便點
for(var i = 1;i<=monthCount;i++){
var time = dateUtils.timeTransformation(testDate,i),
timestamp = Date.parse(time);
assert.ok(time === dateUtils.dateFormmat(timestamp,'yyyy-MM-dd hh:mm:ss'),"出現不匹配時間");
}
console.log('end');
})();
這樣測試一下還是很有必要的,我也是通過這些測試代碼找出來一個bug,最開始我的timeTransformation 方法的month += 1是寫在switch後面的,所以我switch中用的是month+1,到後來我改動了month += 1的位置,但是忘記去掉switch中的month+1:
這裏是錯誤的代碼
timeTransformation : function(date,num) {
var day = date.getDate(),
month = date.getMonth(),
year = date.getFullYear(),
dateArr = this.dateFormmat(date,'yyyy-MM-dd hh:mm:ss').split(' ');
year = year + parseInt((month + num) / 12);
month = (month + num) % 12;
//0-11 轉變爲 1-12
month += 1;
// 大月31天,小月30天,2月份只有28天(平年)或29天(閏年)
// 每年一﹑三﹑五﹑七﹑八﹑十﹑十二這七個月爲大月,均三十一天
switch(month+1){
case 1 :
case 3 :
case 5 :
case 7 :
case 8 :
case 10 :
case 12 :
day > 31 ? day = 31 : '';
break;
case 4 :
case 6 :
case 9 :
case 11 :
day > 30 ? day = 30 : '';
break;
case 2 :
this.isLeapYear(year) ? (day > 29 ? day = 29 : '') : (day > 28 ? day = 28 : '');
break;
default :
break;
}
month < 10 ? (month = '0' + month) : '';
day < 10 ? (day = '0' + day) : '';
return year + '-' + month + '-' + day + ' ' + dateArr[1];
},
結尾
在此感謝js 當前日期增加自然月中的思路。
我把關於此博客所有代碼整合起來放在了timeTransformation。