kowala's home

kowala's home
這裡是我的學習筆記,陸續增加中。
http://kowala21.blogspot.com

2012-10-12

農曆程式概念探討(6)

kowala's home
http://kowala21.blogspot.com

上一篇,我們討論了如何給定一個日期點而求出對應的陰曆日期,透過物件 Lunar 來做轉換,這篇是準備說明如何使用 Lunar 物件來產生月曆。

首先,我們先準備一個參數物件 calElement 來存放由 Lunar 物件所產生之結果,也可以把它視為格子物件,一個月有31天,一天就用一格代表,接著是做個月曆物件 calendar ,準備好了就可以開始產生月曆了。

產生月曆步驟

先給定 y 年 m 月,取得該月起點,計算該月終點,再使用迴圈逐日產生格子物件陣列 calElement,把 Lunar 物件所產生的陰曆日期填入格子物件 calElement,這樣月曆架構就完成了,最後再把一些修飾資料補齊,來完整它。

格子物件示意圖


程式碼如下:

//月曆物件 y 年  m 月 , m = 0-11

function calendar(y,m) {

   var sDObj, lDObj, lY, lM, lD=1, lL, lX=0, tmp1, tmp2;
   var lDPOS = new Array(3);
   var n = 0;
   var firstLM = 0;

   sDObj = new Date(y,m,1);            //該月起點

   this.length    = solarDays(y,m);    //該月終點
   this.firstWeek = sDObj.getDay();    //起點日星期幾

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

      if(lD>lX) {
         sDObj = new Date(y,m,i+1);    //逐日遞增
         lDObj = new Lunar(sDObj);     //農曆
         lY    = lDObj.year;           //農曆年
         lM    = lDObj.month;          //農曆月
         lD    = lDObj.day;            //農曆日
         lL    = lDObj.isLeap;         //農曆是否閏月
         lX    = lL? leapDays(lY): monthDays(lY,lM); //農曆當月最後一天

         if(n==0) firstLM = lM;
         lDPOS[n++] = i-lD+1;
      }
      this[i] = new calElement(
                y, m+1, i+1, nStr1[(i+this.firstWeek)%7], lY, lM, lD++, lL,
                cyclical(lDObj.yearCyl), //年干支
                cyclical(lDObj.monCyl),  //月干支
                cyclical(lDObj.dayCyl++) //日干支
      );
      ...
   }
   ...
}

上面有用到的函式說明

//該月終點 = y 年 m 月的天數
function solarDays(y,m) {
   if(m==1) return(((y%4 == 0) && (y%100 != 0) || (y%400 == 0))?29:28); //2月
   else     return(solarMonth[m]); //其他月
}

//傳入次序,傳回干支,0=甲子
function cyclical(num) {
   return(Gan[num%10]+Zhi[num%12])
}

至此,月曆大致完成,接著是補足修飾資料,節氣、節日、顯示顏色等。節日又分為三種,陽曆節日,陰曆節日,週序節日等。

修飾資料

節日以字串方式存放於陣列

//國曆節日 *表示放假日
var sFtv = new Array(
"0101*元旦",
"0214 情人節",
"0308 婦女節",
...}

//農曆節日 *表示放假
var lFtv = new Array(
"0100*除夕",
"0101*春節,彌勒佛聖誕",
"0106 定光佛聖誕",
...}

//週序節日
var wFtv = new Array(
"0520 母親節",
"0730 被奴役國家週");

取出方法

原作是採用正則表示式,也有稱呼正規表示式,原文是 Regular Expression、regex 或regexp,縮寫為 RE[1]。

若以取出國曆節日來說,我們可以在條件式看到正則表示式的寫法

 if(sFtv[i].match(/^(\d{2})(\d{2})([\s\*])(.+)$/))

 這代表甚麼意思呢,其實是依規則取出所需的字串

 sFtv[i].match(...)  //指定國曆節日字串,然後比對字串

正則表示式

/^(\d{2})(\d{2})([\s\*])(.+)$/

/  逸脫符號
^ 字串開始
$ 字串結束
\d  數字
{2} 重複2次
[]  範圍運算子
\s 空白
\. 任意字
\* 任意次
+ 代表 1-任意次

正則表示式說明
/^....................$/    代表字串開始與結束

在 /^ $/ 之間有4組(...),代表把字串分為四個,放在記憶體中。
/^ (...)(...)(...)(...) $/

第一、二個是2位數
(\d{2})(\d{2})

第三個是"空白"或是"*"
([\s\*])

第四個是任意次字串
(.+)

來看看國曆節日字串

sFtv[0]="0101*元旦"

經此規則  /^(\d{2})(\d{2})([\s\*])(.+)$/ 過濾後可以得到四個字串,放在記憶體中,
以 RegExp.$1, RegExp.$2, RegExp.$3, RegExp.$4 取出變數

RegExp.$1= "01"
RegExp.$2= "01"
RegExp.$3= "*"
RegExp.$4= "元旦"

這樣我們就可以依條件判定,繼續補足修飾資料,節日完整函式如下:

   //國曆節日
   for(i in sFtv)
      if(sFtv[i].match(/^(\d{2})(\d{2})([\s\*])(.+)$/))
         if(Number(RegExp.$1)==(m+1)) {
            this[Number(RegExp.$2)-1].solarFestival += RegExp.$4 + ' '
            if(RegExp.$3=='*') this[Number(RegExp.$2)-1].color = 'red'
         }

   //週序節日
   for(i in wFtv)
      if(wFtv[i].match(/^(\d{2})(\d)(\d)([\s\*])(.+)$/))
         if(Number(RegExp.$1)==(m+1)) {
            tmp1=Number(RegExp.$2)
            tmp2=Number(RegExp.$3)
            this[((this.firstWeek>tmp2)?7:0) + 7*(tmp1-1) + tmp2 - this.firstWeek].solarFestival += RegExp.$5 + ' '
         }

   //農曆節日
   for(i in lFtv)
      if(lFtv[i].match(/^(\d{2})(.{2})([\s\*])(.+)$/)) {
         tmp1=Number(RegExp.$1)-firstLM
         if(tmp1==-11) tmp1=1
         if(tmp1 >=0 && tmp1<n) {
            tmp2 = lDPOS[tmp1] + Number(RegExp.$2) -1
            if( tmp2 >= 0 && tmp2<this.length) {
               this[tmp2].lunarFestival += RegExp.$4 + ' '
               if(RegExp.$3=='*') this[tmp2].color = 'red'
            }
         }
      }

寫到這裡,本篇已經是尾聲了,雖然本篇討論對象是用 javascript 寫的,但完全可以用 C++ 去寫一遍,並且修改幅度不大,這樣就有了自己的農曆產生器了。

參考原始碼:
http://dolphin.cc.ncu.edu.tw/lunar.html
http://38time.artjoey.com/calendar_big.htm
http://www.time.ac.cn/nongli.htm


參考資料
1. 正規表示式  http://zh.wikipedia.org/wiki/正規表示式


農曆程式概念探討(1)
http://kowala21.blogspot.tw/2012/08/1.html
農曆程式概念探討(2)
http://kowala21.blogspot.tw/2012/08/2.html
農曆程式概念探討(3)
http://kowala21.blogspot.tw/2012/09/3.html
農曆程式概念探討(4)
http://kowala21.blogspot.tw/2012/09/4.html
農曆程式概念探討(5)
http://kowala21.blogspot.tw/2012/09/5.html
農曆程式概念探討(6)