js日期轉換工具類(仿oracle to_char,to_date等語法)

nayi_224發表於2018-07-29

摘要

js字串轉日期,js日期轉字串。

更新日誌

2018/08/21

  • 在字串轉日期中,修改“日”的預設值為1.
  • 可以識別單數字的“日”,”月“。比如 1-9-2018可以直接這樣轉換NayiUtil.to_date("1-9-2015", "dd-mm-yyyy")

2018/12/04

  • 新增add_days、add_months、add_years三個函式

2019/02/01

  • 修復to_date方法中的bug,例NayiUtil.to_date('20190131 12:13:14', 'yyyymmdd hh24:mi:ss');NayiUtil.to_date('20190131 2:13:14', 'yyyymmdd hh24:mi:ss');在之前會返回錯誤結果
  • 新增add_months_plus方法,轉換原則為

If date is the last day of the month or if the resulting month has fewer days than the day component of date, then the result is the last day of the resulting month. Otherwise, the result has the same day component as date.

2019/04/04

  • 繼續修復hh12與hh24的bug。。。
  • 新增last_day方法(傳入日期型別,獲取當月的最後一天)例:document.write(NayiUtil.last_day(new Date()));
  • 新增last_month_day方法(傳入日期型別,獲取當月最後一天的日期字串,兩位的,比如31,28,31)例:document.write(NayiUtil.last_month_day(new Date()));
  • 新增months_between方法(三個引數,前兩個為需要比較的日期型別,後一個為標識,1時不考慮dd,只做月份加減,永遠返回整數;0或者預設時,安下述規則計算)例document.write(NayiUtil.months_between(NayiUtil.to_date("20190228", "yyyymmdd"), NayiUtil.to_date("20190127", "yyyymmdd"), 0));

If date1 and date2 are either the same days of the month or both last days of months, then the result is always an integer. Otherwise Oracle Database calculates the fractional portion of the result based on a 31-day month and considers the difference in time components date1 and date2.

背景

一般來說,應該是要儘量避免在js中做時間操作的,它可能會導致很奇葩的bug產生。但是有時候又確實需要這麼做。這個時候就會被蹩腳的語法噁心一臉。為了避免再次發生這種情況,我寫了一個時間操作的工具類,在此分享。

我個人比較喜歡oracle中的to_char與to_date函式,所以具體的使用方法與在oracle中是一樣的。雖然只實現了最基本的用法,不過熟悉oracle的朋友可以直接把文章最後的程式碼粘過來用。不熟悉的往下看用法。

用法

to_char

用法示例
NayiUtil.to_char(new Date(2018,0,31,12,30,40), "yyyymmdd");
返回20180130

NayiUtil.to_char(new Date(2018,0,31,12,30,40), "yyyy-mm-dd");
返回2018-01-30

NayiUtil.to_char(new Date(2018,0,31,12,30,40), "y,yyy-yyyy-yyy-yy-y-mm-dd hh12:hh24:mi:ss**sssss***q 季度:qcn 星期d__dc_day");
返回 2,018-2018-018-18-8-01-31 00:12:30:40**45040***1 季度:第一季度 星期3__3_星期三

NayiUtil.to_char(new Date(2018,0,31,12,30,40), "Aa(YyYy)yyyy");
返回aaYyYy2018

NayiUtil.to_char(new Date(2018,0,31,12,30,40), "Aa\\(YyYy\\)yyyy\\\\");
返回aa(2018)2018\

NayiUtil.to_char(new Date(2018,0,31,12,30,40), "fmmm");
返回1

基本憑感覺用就對了,我只解釋幾點

  • 格式化引數會自動轉為小寫,除非寫在圓括號中。
  • 圓括號中的字元會直接原樣輸出,其他沒有匹配上的字元會小寫格式輸出。
  • \是轉義符號。只有兩個作用,輸出圓括號和他自己。
  • 優先最長匹配規則。比如ddd 會解析為dd+d,而不會是d+dd或d+d+d。
  • fm,去除多餘的前導零。

####to_date
未進行匹配的部分會預設為當前時間

可以匹配的字元(很少用到的沒寫)
yyyy mm dd hh24 hh mi ss

用法示例
NayiUtil.to_char(NayiUtil.to_date("01-2015-15", "mm-yyyy-dd"), 'yyyy-mm-dd hh24:mi:ss')
返回2015-01-15 19:36:03

NayiUtil.to_char(NayiUtil.to_date("01-aaaaaa2015", "mm-nayinayyyy"), 'yyyy-mm-dd hh24:mi:ss')
返回2015-01-29 19:37:58

解釋
不需要匹配的部分可以用任意無關字元佔位。

完整程式碼

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

var NayiUtil = function(){

    var fill_zreo = function(str, length){
        str = str.toString();
        var l = str.length;
        if (str.length >= length) {return str;}

        for(var i = 0; i < length - l; i++){
            str = "0" + str;  
        }
        return str; 
    }


    var to_char = function(dateObj, fmtSrc) {

        if(!(dateObj instanceof Date)){
            return "now, only Datetype is supported";
        }

        if(!dateObj || !fmtSrc){
            return dateObj || "null";
        }

        var fmtModel = "";
        var result = "";

        var init = function(){
            var res = "";
            var inner_flag = false;

            for(var i = 0; i < fmtSrc.length; i++){

                if(fmtSrc.charAt(i) == "\\"){
                    res += fmtSrc.charAt(i) + fmtSrc.charAt(i + 1);
                    i += 1;
                    continue;
                }

                if(fmtSrc.charAt(i) == "("){
                    inner_flag = true;
                }

                res += inner_flag ? fmtSrc.charAt(i) : fmtSrc.charAt(i).toLowerCase();

                if(fmtSrc.charAt(i) == ")"){
                    inner_flag = false;
                }
            }

            fmtSrc = res;
            fmtModel = fmtSrc.match(/fm/g);
            fmtSrc = fmtSrc.replace(/fm/g, "");

        }

        init();

        var validFmt = {
            "yyyy": function() {
                if(fmtModel == "fm"){
                    return dateObj.getFullYear();
                }else {
                    return fill_zreo(dateObj.getFullYear(), 4);
                }
            },
            "mm": function() {

                if(fmtModel == "fm"){
                    return dateObj.getMonth() + 1;
                }else {
                    return fill_zreo(dateObj.getMonth() + 1, 2);
                }
            },
            "dd": function(){
                if(fmtModel == "fm"){
                    return dateObj.getDate();
                }else {
                    return fill_zreo(dateObj.getDate(), 2);
                }
            },
            "yyy": function(){
                if(fmtModel == "fm"){
                    return dateObj.getFullYear().toString().substr(-3);
                }else {
                    return fill_zreo(dateObj.getFullYear().toString().substr(-3), 3);
                }
            },
            "yy": function(){
                if(fmtModel == "fm"){
                    return dateObj.getFullYear().toString().substr(-2);
                }else {
                    return fill_zreo(dateObj.getFullYear().toString().substr(-2), 1);
                }
            },
            "y": function(){
                if(fmtModel == "fm"){
                    return dateObj.getFullYear().toString().substr(-1);
                }else {
                    return fill_zreo(dateObj.getFullYear().toString().substr(-1), 1);
                }
            },
            "y,yyy": function(){
                var temp = fill_zreo(dateObj.getFullYear(), 4);
                var yResult = temp.substr(0, 1) + "," + temp.substr(1, 3);
                return yResult;
            },
            "hh": function(){
                if(fmtModel == "fm"){
                    return (dateObj.getHours()) % 12;
                }else {
                    return fill_zreo((dateObj.getHours()) % 12, 2);
                }
            },
            "hh12": function(){
                if(fmtModel == "fm"){
                    return (dateObj.getHours()) % 12;
                }else {
                    return fill_zreo((dateObj.getHours()) % 12, 2);
                }
            },
            "hh24": function(){
                if(fmtModel == "fm"){
                    return dateObj.getHours();
                }else {
                    return fill_zreo(dateObj.getHours(), 2);
                }
            },
            "mi": function(){
                if(fmtModel == "fm"){
                    return dateObj.getMinutes();
                }else {
                    return fill_zreo(dateObj.getMinutes(), 2);
                }
            },
            "ss": function(){
                if(fmtModel == "fm"){
                    return dateObj.getSeconds();
                }else {
                    return fill_zreo(dateObj.getSeconds(), 2);
                }
            },
            "sssss": function(){
                return dateObj.getSeconds() + dateObj.getHours() * 3600 + dateObj.getMinutes() * 60;
            },
            "ff": function(){
                return dateObj.getMilliseconds();
            },
            "d": function(){
                return fill_zreo(dateObj.getDay(), 1);
            },
            "ddd": function(){
                var dateArr = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
                var date = new Date();
                var day = date.getDate();
                var month = date.getMonth(); //getMonth()是從0開始
                var year = date.getFullYear();
                var result = 0;
                for ( var i = 0; i < month; i++) {
                    result += dateArr[i];
                }
                result += day;
                if (month > 1 && (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
                    result += 1;
                }
                return result;
            },
            "dc": function(){
                return fill_zreo(dateObj.getDay() == 0 ? 7 : dateObj.getDay(), 1);  //中文星期數字(js是0-6,0是星期日)
            },
            "day": function(){
                var arr = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
                return arr[dateObj.getDay()];
            },
            "q": function(){
                var arr = ["1", "1", "1", "1", 
                            "2", "2", "2", "2", 
                            "3", "3", "3", "3", 
                            "4", "4", "4", "4"]
                return arr[dateObj.getMonth()];
            },
            "qcn": function(){
                var arr = ["第一季度", "第一季度", "第一季度", "第一季度", 
                            "第二季度", "第二季度", "第二季度", "第二季度", 
                            "第三季度", "第三季度", "第三季度", "第三季度", 
                            "第四季度", "第四季度", "第四季度", "第四季度"];
                return arr[dateObj.getMonth()];
            }

        }



        var temp = "";
        var bracket_num = 0;
        for(var i = 0; i < fmtSrc.length; ){
            if(fmtSrc.charAt(i) == "("){
                bracket_num++;
                i++;
                continue;
            }
            if(fmtSrc.charAt(i) == ")" && bracket_num > 0){
                bracket_num--;
                i++;
                continue;
            }
            if(fmtSrc.charAt(i) == "\\"){
                result += fmtSrc.charAt(i + 1);
                i += 2;
                continue;
            }
            if(bracket_num > 0){
                result += fmtSrc.charAt(i);
                i++;
                continue;
            }

            var maxLength = 0;
            var j = i + 1;
            for(; j <= fmtSrc.length; j++){
                if(validFmt[fmtSrc.substr(i, j - i)]){
                    maxLength = j - i;
                }
            }
            if(maxLength){
                result += validFmt[fmtSrc.substr(i, maxLength)]();
            }else {
                result += fmtSrc.charAt(i);
            }
            i += maxLength || 1;
        }

        return result;

    }   

    var to_date = function(dateStr, fmt){

        var dateStr_bk = dateStr;

        var init = function(){
            fmt = fmt.toLowerCase();
        }()

        var default_date = new Date();

        var year = default_date.getFullYear();
        var month = default_date.getMonth();
        //var day = default_date.getDate();
		var day = 1;
        var hour = default_date.getHours();
        var minute = default_date.getMinutes();
        var second = default_date.getSeconds();;

        var validFmt = {
            "yyyy": function(str, s_index, length){
				var temp = str.substr(s_index, length);
				year = temp.match(/^[0-9]+/g)[0];
                return temp.length - year.length;
            },
            "mm": function(str, s_index, length){
				var temp = str.substr(s_index, length);
				month = temp.match(/^[0-9]+/g)[0] - 1;
                return temp.length - temp.match(/^[0-9]+/g)[0].length;
            },
            "dd": function(str, s_index, length){
				var temp = str.substr(s_index, length);
				day = temp.match(/^[0-9]+/g)[0];
				return temp.length - day.length;
            },
			"hh12": function(str, s_index, length){
                //hour = str.substr(s_index, length);
				
				var temp = str.substr(s_index, length);
				hour = temp.match(/^[0-9]+/g)[0] ;
				hour = hour.substr(0, 2);
				var returnLength = temp.length - hour.length;
				hour = parseInt(hour) % 12;
				return returnLength;
            },
            "hh24": function(str, s_index, length){
                //hour = str.substr(s_index, length);
				
				var temp = str.substr(s_index, length);
				hour = temp.match(/^[0-9]+/g)[0];
				hour = hour.substr(0, 2);
				return temp.length - hour.length;
            },
            "hh": function(str, s_index, length){
                //hour = str.substr(s_index, length);
				
				var temp = str.substr(s_index, length);
				hour = temp.match(/^[0-9]+/g)[0];
				return temp.length - hour.length;
            },
            "mi": function(str, s_index, length){
                //minute = str.substr(s_index, length);
				
				var temp = str.substr(s_index, length);
				minute = temp.match(/^[0-9]+/g)[0];
				return temp.length - minute.length;
            },
            "ss": function(str, s_index, length){
                //second = str.substr(s_index, length);
				
				var temp = str.substr(s_index, length);
				second = temp.match(/^[0-9]+/g)[0];
				return temp.length - second.length;
            }
        }

        var temp = "";
        var bracket_num = 0;
		var axis = 0;		//主要為處理2018-8-21這種特殊情況(2018-08-21為正常情況)
        for(var i = 0; i < fmt.length; ){
            var maxLength = 0;
            var j = i + 1;
            for(; j <= fmt.length; j++){
                if(validFmt[fmt.substr(i, j - i)]){
                    maxLength = j - i;
                }
            }
            if(maxLength){
                axis += validFmt[fmt.substr(i, maxLength)](dateStr, i - axis, maxLength) || 0;
            }else {
            }
            i += maxLength || 1;
        }
        //alert(year+"_"+ month+"_"+ day+"_"+ hour+"_"+ minute+"_"+ second);
        var resultDate = new Date(year, month, day, hour, minute, second);
        return resultDate;
    }

    var add_days = function(oldDate, addDays){
    	return new Date(oldDate.setDate(oldDate.getDate() + addDays));
    }
    
    var add_months = function(oldDate, addMonths){
    	return new Date(oldDate.setMonth(oldDate.getMonth() + addMonths));
    }
    
    var add_years = function(oldDate, addYesrs){
    	return new Date(oldDate.setFullYear(oldDate.getFullYear() + addYesrs));
    }

	var add_months_plus = function(oldDate, addMonths){
		var dateArr = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
		
		var oldYear = fill_zreo(oldDate.getFullYear(), 4);
		var oldMonth = fill_zreo(oldDate.getMonth(), 2);
		var oldDay = fill_zreo(oldDate.getDate(), 2);
		
		var newYear = parseInt(oldYear) + Math.floor((parseInt(oldMonth) + 1 + parseInt(addMonths)) / 12);
		var newMonth = (parseInt(oldMonth) + 1 + parseInt(addMonths)) % 12;
		var newDay;
		
		var currentLastDay; 
		if((oldMonth == 1) && (oldYear %  400 == 0 || (oldYear % 4 == 0 && oldYear % 100 != 0))){
			currentLastDay = 29;
		}else {
			currentLastDay = dateArr[oldMonth];
		}
		
		if(oldDay == currentLastDay){
			newDay = dateArr[newMonth];
		}else if(oldDay > dateArr[(newMonth - 1) % 12]){
			if(newMonth == 2 && (oldYear %  400 == 0 || (oldYear % 4 == 0 && oldYear % 100 != 0))){
				newDay = 29;
			}else {
				newDay = dateArr[(newMonth - 1) % 12];
			}
		}else{
			newDay = oldDay;
		}
		//alert(to_date("2019-04-30 14:34", 'yyyy-mm-dd hh24:mi'));
		//alert("old : " + oldDay + "__"+newYear+"-"+newMonth+"-"+newDay);
		//alert(""+newYear+"-"+newMonth+"-"+newDay+to_char(oldDate, '-hh24-mi-ss'));
		return to_date(""+newYear+"-"+newMonth+"-"+newDay+" "+to_char(oldDate, 'hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss');
	}

    return {
        to_char: to_char,
        to_date: to_date,
        add_days: add_days,
        add_months: add_months,
        add_years: add_years,
        add_months_plus: add_months_plus
    };

}();            

;
                          //"2018","0","31","1","31","13"
//var test = NayiUtil.to_char(new Date(), "yyyy(年)mm月dd(Ri) \\(yyyy-mm-dd\\)  dd-mm-yyyy hh24:mi:ss:ff d dc dcn qcn q ddd sssss y,yyy ---");
//alert(test);
//var ttt = NayiUtil.to_char(new Date(2018,0,31,12,30,40), "fmmm");
//alert(ttt);
//document.write(ttt);
//document.write(NayiUtil.to_char(NayiUtil.to_date("01-2015-15", "mm-yyyy-dd"), 'yyyy-mm-dd hh24:mi:ss'));
//document.write("\r\n  ");
document.write(NayiUtil.to_char(NayiUtil.to_date("1-9-2015", "dd-mm-yyyy"), 'yyyy-mm-dd hh24:mi:ss'));


        </script>
    </head>
    <body>
    </body>

</html>

相關文章