JavaScript——日期增加自然月

日期增加自然月

前言

最近接到這樣一個需求:

業務場景: 在線簽訂電子合同;
具體需求: 獲取合同的起始時間與終止時間,以用戶點擊簽約爲起始時間,計算用戶在頁面中選擇的合約生效時長(可選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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章