1. 程式人生 > >MySQL:根據陽曆獲取農曆日期函式

MySQL:根據陽曆獲取農曆日期函式

begin
	DECLARE   v_OffSet         INT;
	DECLARE   v_Lunar          INT;          # 農曆年是否含閏月,幾月是閏月,閏月天數,其它月天數
	DECLARE   v_YearDays       INT;          # 農曆年所含天數
	DECLARE   v_MonthDays      INT;          # 農曆月所含天數
	DECLARE   v_LeapMonthDays  INT;          # 農曆閏月所含天數
	DECLARE   v_LeapMonth      INT;          # 農曆年閏哪個月 1-12 , 沒閏傳回 0
	DECLARE   v_LeapFlag       INT;          # 某農曆月是否為閏月  1:是  0:不是
	DECLARE   v_MonthNo        INT;          # 某農曆月所對應的2進位制數 如農曆3月: 001000000000
	DECLARE   i                INT;
	DECLARE   j                INT;
	DECLARE   k                INT;

	DECLARE   v_Year           INT;          # i_SolarDay 對應的農曆年
	DECLARE   v_Month          INT;          # i_SolarDay 對應的農曆月
	DECLARE   v_Day            INT;          # i_SolarDay 對應的農曆日

	DECLARE   o_OutputDate     VARCHAR(25); # 返回值  格式:農曆 ****年 **(閏)月 **日

	DECLARE   e_ErrMsg         VARCHAR(200);
	declare   v_j              int;
	declare   v_temp_m         varchar(100);
	declare   v_temp_d         varchar(100);

	# -e_ErrDate        EXCEPTION;
	#輸入引數判斷
	/* IF i_SolarDay<TO_DATE('1900-01-31','YYYY-MM-DD') OR i_SolarDay>=TO_DATE('2050-01-23','YYYY-MM-DD') THEN
	 RAISE e_ErrDate;
	 END IF ;
	*/

  # i_SolarDay 到 1900-01-30(即農曆1900-01-01的前一天) 的天數
  # v_OffSet := TRUNC(i_SolarDay, 'DD') - TO_DATE('1900-01-30', 'YYYY-MM-DD');

	DECLARE exit HANDLER FOR SQLEXCEPTION,SQLWARNING,NOT FOUND
	begin
		set o_OutputDate ='獲取農曆失敗';
		return o_OutputDate;
	end;

  set v_OffSet = DATEDIFF(i_SolarDay,str_to_date('1900-01-30', '%Y-%c-%d'));
  # 確定農曆年開始
  set i = 1900;
  WHILE i < 2050 AND v_OffSet > 0 DO
    set v_YearDays = 348;    #  29*12 以每年12個農曆月,每個農曆月含29個農曆日為基數
		set v_LeapMonthDays = 0;
    # 取出農曆年是否含閏月,幾月是閏月,閏月天數,其它月天數
    # 如農曆2001年: 0x0d954(16進位制) -> 55636(10進位制) -> 0 110110010101 0100(2進位制)
    # 1,2,4,5,8,10,12月大, 3,6,7,9,11月小, 4月為閏月,閏月小
    SELECT DataInt INTO v_Lunar FROM SolarData WHERE YearId = i;
    # 傳回農曆年的總天數
    set j = 32768;            #   100000000000 0000 -> 32768
															# 0 110110010101 0100 -> 55636(農曆2001年)

    # 依次判斷v_Lunar年個月是否為大月,是則加一天
    WHILE j > 8 Do       # 閏月另行判斷 8 -> 0 000000000000 1000
      set v_j = v_Lunar&j;
      #IF BITAND(v_Lunar, j) + 0 > 0 then
      if v_j+0 >0 then
        set v_YearDays = v_YearDays + 1;
        #v_YearDays := v_YearDays + 1;
      END IF;
      set j = j/2;            # 判斷下一個月是否為大
    END WHILE;

    # 傳回農曆年閏哪個月 1-12 , 沒閏傳回 0   15 -> 1 0000
    # v_LeapMonth := BITAND(v_Lunar, 15) + 0;
    set v_LeapMonth = v_Lunar&15 + 0;

    # 傳回農曆年閏月的天數 ,加在年的總天數上
    IF v_LeapMonth > 0 THEN
      # 判斷閏月大小 65536 -> 1 000000000000 0000
      #IF BITAND(v_Lunar, 65536)+0 > 0 THEN
      if v_Lunar&65536 + 0 > 0 then
				set v_LeapMonthDays = 30;
      ELSE
        set v_LeapMonthDays = 29;
      END IF;
				set v_YearDays = v_YearDays + v_LeapMonthDays;
    END IF;

		set  v_OffSet = v_OffSet - v_YearDays;
    set i = i + 1;
  END WHILE;

  IF v_OffSet <= 0 THEN
    # i_SolarDay 在所屬農曆年(即i年)中的第 v_OffSet 天
    set v_OffSet = v_OffSet + v_YearDays;
		set  i = i - 1;
  END IF;

  # 確定農曆年結束
  set v_Year = i;

  # 確定農曆月開始
  set i = 1;
  SELECT DataInt INTO v_Lunar FROM SolarData WHERE YearId = v_Year;

  # 判斷那個月是潤月
  # 如農曆2001年 (55636,15 -> 0 1101100101010100, 1111 -> 4) 即潤4月,且閏月小
  set v_LeapMonth = v_Lunar&15 + 0;
	set v_LeapFlag = 0;

  WHILE i < 13 AND v_OffSet > 0 DO
    # 判斷是否為閏月
		set v_MonthDays = 0;
    IF (v_LeapMonth > 0 AND i = (v_LeapMonth + 1) AND v_LeapFlag = 0) THEN
      # 是閏月
      set i = i - 1;
      set k = i;                # 儲存是閏月的時i的值
      set v_LeapFlag = 1;

      # 傳回農曆年閏月的天數
      #IF BITAND(v_Lunar, 65536)+0 > 0 THEN
      if v_Lunar&65536+0 > 0 then
				set  v_MonthDays = 30;
      ELSE
        set v_MonthDays = 29;
      END IF;
    ELSE
      # 不是閏月
			set  j = 1;
			set  v_MonthNo = 65536;
      # 計算 i 月對應的2進位制數 如農曆3月: 001000000000
      WHILE j<= i DO
        set v_MonthNo = v_MonthNo/2;
        set j = j + 1;
      END WHILE;
      # 計算農曆 v_Year 年 i 月的天數
      #IF BITAND(v_Lunar, v_MonthNo)+0 > 0 THEN
      if v_Lunar&v_MonthNo + 0 > 0 then
				set v_MonthDays = 30;
      ELSE
				set v_MonthDays = 29;
      END IF;
    END IF;

    # 解除閏月
    IF v_LeapFlag = 1 AND i = v_LeapMonth +1 THEN
			set  v_LeapFlag = 0;
    END IF;
    set v_OffSet = v_OffSet - v_MonthDays;
    set i = i + 1;
  END while;

  IF v_OffSet <= 0 THEN
    # i_SolarDay 在所屬農曆月(即i月)中的第 v_OffSet 天
		set  v_OffSet = v_OffSet + v_MonthDays;
		set  i = i - 1;
  END IF;

  # 確定農曆月結束
  set v_Month = i;
  # 確定農曆日結束
  set v_Day = v_OffSet;
  # 格式化返回值
	set o_OutputDate =CONCAT(getNumYear(v_Year),'年');

  IF k = i THEN
    #set  o_OutputDate := o_OutputDate || LPAD(TO_CHAR(v_Month), 2, '0')||'(潤)月 ';
    set v_temp_m = concat(getNumMonth(v_Month),'(潤)月 ');
  ELSE
    # o_OutputDate := o_OutputDate || LPAD(TO_CHAR(v_Month), 2, '0')||'月 ';
    set v_temp_m = concat(getNumMonth(v_Month),'月');
  END IF;
 
  # o_OutputDate := o_OutputDate || LPAD(TO_CHAR(v_Day), 2, '0')||'日';
  set v_temp_d = getNumDay(v_Day);
  set o_OutputDate = CONCAT(CONCAT(o_OutputDate,v_temp_m),v_temp_d);
  
	RETURN o_OutputDate;
END