計算農曆
只要知道太陽與月亮,在地心黃道座標系統上,隨時間改變的位置,我們就可以據以訂出農曆。
下面我們將用R平台上套件astrolibR來算出農曆。
農曆是甚麼?
我國的農曆基本上是陰陽合曆。陰曆的月以月亮為基礎,新月的時候就是初一。太陽與月亮落在同一黃經的那一天就是新月日,那天就是陰曆初一。一個月有時29天,有時30天,完全決定於兩次新月日之間相差29天還是30天。
農曆的年以太陽為基礎,地球繞太陽一圈需時365.2422天。用地心赤道坐標系統來講,太陽在春分與秋分兩次落在赤道平面上。一次在3月份另一次在9月份,3月份的那一刻叫春分,9月份的那一刻叫秋分。太陽在黃道面上,一年繞行地球一圈,以春分時太陽的位置為基準,每繞行15度得一節氣,分別是春分,清明,穀雨,立夏,小滿,芒種,夏至,小暑,大暑,立秋,處暑,白露,秋分,寒露,霜降,立冬,小雪,大雪,冬至,小寒,大寒,立春,雨水,驚蟄。太陽位於黃經0度時是春分,15度時是清明,餘此類推。位於黃經30度整數倍的節氣叫做中氣,換句話說,春分,穀雨,小滿,夏至,大暑,處暑,秋分,霜降,小雪,冬至,大寒,雨水等為中氣。節氣與太陽的位置關係密切,太陽是地球能量的主要來源,所謂春耕、夏耘、秋收、冬藏道盡了農作與太陽之間的關係。先人的對於農時的經驗,大致用節氣來表示。
節氣
中央氣象局的 天文常識系列 提到節氣
節氣的定法有兩種,古時候使用的稱為平氣法,就是把冬至到下一個冬至平分為24等分,每一節氣都是15天多。從清初時憲曆(西元1645年)起,節氣的推算改為定氣法,自春分點開始,太陽在黃道上每視行15度定一個節氣,節氣間隔的時間並不等長。
用astrolibR套件的sumpos,我們可以得到太陽隨時間改變的位置。其中的成分$longmed就是黃經,看哪一天太陽的位置通過黃經0度,那一天就是春分;看哪一天太陽的位置通過黃經15度,那一天就是清明,餘此類推。
在附錄中,我們自製了函數jieqi(year),用jieqi(2016)就可算出西元2016年的節氣。
陰曆初一
用astrolibR套件的moonpos,我們可以得到月亮隨時間改變的位置。計算什麼時刻,太陽與月亮的黃經相同,那刻所在的日子就是陰曆初一。
在附錄中,我們自製了函數lunardayone(year),用lunardayone(2016)就可算出西元2016年的那些天是陰曆初一。
陰曆的月份
平均起來一個陰曆月約有29.5天,而一年有365.2422天,12個陰曆月比一年短少約11天。為了維持一年有12個月份,我國用閏月的方式來解決。大致上,每隔約三年,設置一個月為閏月,其月份與前一個月同。假如前一個月是8月而這個月是閏月,這個月叫潤8月,而下個月叫9月。
哪一個月的初一是春節?哪一個月要閏月?
中央研究院劉智漢在 這裡提到歲首的問題:
自漢武帝太初元年(西元前104年)改曆以來,至今大都採用夏代的建寅制,即自冬至所在 月份起算的第三個月為正月因此,冬至所在的陰曆月為11月。
Aslaksen的 文章列出了安置月份與置潤的原則。條列如下:
- 計算時間時以東經120度為基準
- 每天的起算時間為午夜零時
- 新月那天為陰曆初一
- 一歲的開始為冬至的那一刻所在的日子,以下一個冬至日的前一天為最終日。用歲來稱呼,與大年初一開始的年有所區別。冬至日的陰曆月訂為11月
- 若一歲裡包含了完整的12個陰曆月,這一歲叫潤歲。在潤歲裡,第一個沒有中氣的月份必須閏月
根據這些原則,在附錄中自製了函數lunarCalendar(year)。用lunarCalendar(2017),我們算出2017年陰陽曆的對應,得到2017年潤6月,與中央氣象局的 資料 相符合。
附錄
library(astrolibR)
# 清明時太陽位於黃經15度,穀雨30度,...
jieqinames<-c("清明","穀雨","立夏","小滿","芒種","夏至","小暑","大暑","立秋"
,"處暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至","小寒","大寒","立春","雨水","驚蟄","春分")
# d1,d2 angle with unit degree.
diff<-function(d1,d2,mod){
dif<- ((d1-d2) %% mod)
if (dif>mod/2)
dif<-dif-mod
dif
}
# 節氣足碼,清明1,穀雨2,...,不是節氣0
jieqiindex<-function(year,month,day){
jd<-jdcnv(year,month,day,0)-1/3
sun1<-sunpos(jd)$longmed%%15
sun2<-sunpos(jd+1)$longmed%%15
if (diff(sun1,0,15)<=0 && diff(sun2,0,15)>0)
floor(sunpos(jd)$longmed%%360/15)+1
else
0
}
# 是否陰曆初一
isLunarOne<-function(year,month,day){
jd<-jdcnv(year,month,day,0)-1/3
sun1<-sunpos(jd)$longmed%%360
sun2<-sunpos(jd+1)$longmed%%360
mon1<-moonpos(jd)$geolong%%360
mon2<-moonpos(jd+1)$geolong%%360
if (diff(sun1,mon1,360)>=0 && diff(sun2,mon2,360)<0)
TRUE
else
FALSE
}
# 算出節氣的日子
jieqi<-function(year){
leapyear=FALSE
if (year %% 4 ==0) leapyear=TRUE
if (year %% 100==0) leapyear=FALSE
if (year %% 400==0) leapyear=TRUE
days=c(31,28,31,30,31,30,31,31,30,31,30,31)
if (leapyear) days[2]=29
res<-list()
for (month in seq(1,12)){
for (day in 1:days[month]){
index<-jieqiindex(year,month,day)
if (index>0){
res<-c(res,list(c(year,month,day,jdcnv(year,month,day,12),index)))
}
}
}
res
}
lunardayone<-function(year){
leapyear=FALSE
if (year %% 4 ==0) leapyear=TRUE
if (year %% 100==0) leapyear=FALSE
if (year %% 400==0) leapyear=TRUE
days=c(31,28,31,30,31,30,31,31,30,31,30,31)
if (leapyear) days[2]=29
res<-list()
for (month in seq(1,12)){
for (day in 1:days[month]){
if (isLunarOne(year,month,day)){
res<-c(res,list(c(year,month,day,jdcnv(year,month,day,12))))
}
}
}
res
}
# 一歲裡陰的陰曆初一清單,假如一歲裡包含完整的12個陰曆月,則長度為13
suiones<-function(lunarones,sols1,sols2){
res<-list()
for (ele in lunarones){
if (ele[4]>=sols1[4] && ele[4]<=sols2[4]){ # should be <=
res<-c(res,list(ele))
}
}
res
}
# 連續兩個陰曆初一間是否缺少中氣,及m1的月份是否缺少中氣
lackzhongqi<-function(m1,m2,jieqis){
res<-TRUE
for (i in seq_along(jieqis)){
if (i%%2==0 && jieqis[[i]][4]>=m1[4] &&jieqis[[i]][4]<m2[4]){
res<-FALSE
break
}
}
res
}
# 排定陰曆月份,包含閏月的設定
assignmonth<-function(sui,jieqis){
leapsui<-FALSE
if (length(sui)>=13){
leapsui<-TRUE
}
completed<-FALSE
suiclone<-sui
Mn<-11
for (i in seq_along(suiclone)){
if (leapsui && !completed && lackzhongqi(suiclone[[i]],suiclone[[i+1]],jieqis)){
suiclone[[i]]<-c(suiclone[[i]],Mn+0.5) # 閏月
completed<-TRUE
}
else {
Mn<-(Mn+1)
if (Mn>12) Mn<-Mn%%12
suiclone[[i]]<-c(suiclone[[i]],Mn) # 平月/普通月
}
}
suiclone
}
lunarCalendar<-function(year){
jieqis<-c(jieqi(year-1),jieqi(year),jieqi(year+1))
lunarones<-c(lunardayone(year-1),lunardayone(year),lunardayone(year+1))
wintersolstices<-list()
for (ele in jieqis){
#if (isSolstice(ele[1],ele[2],ele[3]))
if (ele[5]==18)
wintersolstices<-c(wintersolstices,list(ele))
}
firstsui<-suiones(lunarones,wintersolstices[[1]],wintersolstices[[2]])
secondsui<-suiones(lunarones,wintersolstices[[2]],wintersolstices[[3]])
firstsui<-assignmonth(firstsui,jieqis)
secondsui<-assignmonth(secondsui,jieqis)
res<-c()
for (ele in c(firstsui,secondsui)){
if (ele[1]==year){
st=""
if (ele[5]%%1>0) st<-"潤" # ele[5]有小數部分代表閏月
st<-paste0(st,floor(ele[5]),"月")
res<-c(res,paste(ele[1],ele[2],ele[3],st))
}
}
res
}
# test jieqi
res<-jieqi(2017)
for (ele in res){
print(paste(ele[1],ele[2],ele[3],jieqinames[ele[5]]))
}
# test lunardayone
lunardayone(2016)
# test lunarCalendar
lunarCalendar(2016)
lunarCalendar(2017)
lunarCalendar(2033)
沒有留言:
張貼留言