利用python的pyephem模塊(需先安裝)可以很容易獲得合朔及冬至時間。但需注意,該模塊在曆元2000.0年±3000年的平均誤差在10分鐘以內,但此外的誤差是難以接受的。
關於朔旦冬至的判斷:冬至月判斷可能存在的幾個誤差。
- 現代測算誤差:現代的天文計算,是近代天文學發展起來以後利用精密儀器測量天體的實際運行狀況,由此構建的理論模型。但天體的運動狀況非常複雜,當今的測量結果能難適用於久遠前或久遠後的宇宙,距離時間越遠,誤差越大。對於先秦時期的歷表,應注意定朔或冬至時間在0-1時或23-24時段,可能幾分鐘的誤差在進位時表現爲1天。
- 古代測算誤差:戰國前後是以冬至所在的月份作爲正月,但月朔是無法觀測的,推朔會有誤差,而冬至的測量也存在誤差。以《左傳》兩次“日南至”的記載爲例,皆與今測算差2日。
歷表的排布:每個冬至合朔點有3項數據(干支、日期、時間),其中時間部分主要用於參考可能的誤差問題,分三行排列。每年從冬至月起排,如果到下一個冬至月出現了13次合朔,該年則排出13個月份,但並不表示該年存在閏月,月名僅供參考。古代的置閏會因爲不同的歷元和置閏方法有變化。如果連續兩個月朔日干支的天干部分相同,說明前一個月爲大月,否則爲小月。如果連續三個月朔日干支的天干部分相同,說明前兩個月爲連大月。古歷的朔策較今值爲大,大小月的情況也與實歷不符。
wangnian部分的代碼參見:https://blog.csdn.net/weixin_42763614/article/details/82955539
import ephem
from wangnian import *
nlrq = ["初一","初二","初三","初四","初五","初六","初七","初八","初九","初十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十","二一","二二","二三","二四","二五","二六","二七","二八","二九","三十"]
def jd2ce(JD):
JD = JD + 0.5 # 以-4812年1月1日0時爲曆元
Z = int(JD)
F = JD - Z
if Z < 2299161: # 儒略曆
A = Z
else: # 格里曆
a = int((Z - 1867216.25) / 36524.25)
A = Z + 1 + a - int(a / 4)
B = A + 1524
C = int((B - 122.1) / 365.25)
D = int(365.25 * C)
E = int((B - D) / 30.6001)
day = B - D - int(30.6001 * E) + F
if E < 14:
month = E - 1
elif E < 16:
month = E - 13
if month > 2:
year = C - 4716
elif month in [1, 2]:
year = C - 4715
d = day - int(day) #取出一日的小數部分,轉換爲時分秒
h = int(d * 24)
m = int((d * 24 - h) * 60)
s = ((d *24 - h) * 60 - int((d *24 - h) * 60)) * 60
if year <= 0: # 將天文年表示爲公曆年
year -= 1
return year, month, int(day),h,m,round(s)
def ut8(t): # 由UT0h轉爲UT8h
d = ephem.Date(t)
date = d.tuple() # 獲得年月日時分秒
jd0 = ephem.julian_date(t)
jd = jd0 + 8/24
ce = jd2ce(jd)
return ce
def jd2gz(t):
JD = ephem.julian_date(t)
tiangan = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"]
dizhi = ["子", "醜", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"]
gz = [''] * 60 # 六十甲子表
for i in range(60): gz[i] = tiangan[i % 10] + dizhi[i % 12]
return gz[int(JD+0.5+8/24+49)%60]
def time_format(t,type):
if type == 0: # 日期
return "BC{}-{:02d}-{:02d}".format(-ut8(t)[0],ut8(t)[1],ut8(t)[2])
elif type == 1: # 時間
return "{:02d}:{:02d}:{:02d}".format(ut8(t)[3], ut8(t)[4], ut8(t)[5])
header = ["參考年","王公紀年","冬至",'冬至日期',"冬至月朔","二月(醜)","三月(寅)","四月","五月","六月","七月","八月","九月","十月","十一月","十二月","十三月"]
dzhsb = open("冬至合朔表.xls",'w')
for i in range(len(header)):
dzhsb.write(header[i]+'\t')
for i in range(620): # BC722-BC104
year = -723 + i
time = str(year)+'/12'
dz = ephem.next_solstice(time) # 冬至時間
dz_jd = ephem.julian_date(dz) # 冬至儒略日
dzs0 = jd2ce(dz_jd - 29) # 冬至前一月
dzs = ephem.next_new_moon(dzs0) # 預判的冬至朔
dzs_jd = ephem.julian_date(dzs) # 在UT8h下比較冬至朔是否爲實際的冬至所在月朔日
if int(dz_jd+0.5+8/24) < int(dzs_jd+0.5+8/24):
dzs = ephem.next_new_moon(jd2ce(dz_jd - 30))
dzs_jd = ephem.julian_date(dzs)
# 寫入上一年曆表
if i != 0:
if str(heshuo) == str(dzs):
hsb.pop() # 上年最後一月朔爲是年冬至朔,則刪除
dzhsb.write('\n')
for m in range(3):
dzhsb.write(hsb[0][0]+'\t') # 參考年
dzhsb.write(hsb[0][1]+'\t') # 王公紀年年
for n in range(len(hsb)): # 月表
if n == 0 : continue
if n == 2 : # 在冬至後插入冬至的日期
if m == 1: dzhsb.write(dzrq)
dzhsb.write('\t')
dzhsb.write(hsb[n][m+0]+'\t')
dzhsb.write('\n')
hsb.clear()
# 生成歷表
hsb = [[str(year + 1), wgjn[841 + year + 1]]]
hsb.extend([[jd2gz(dz),time_format(dz,0),time_format(dz,1)]]) # 干支,年月日,時分秒
hsb.extend([[jd2gz(dzs),time_format(dzs,0),time_format(dzs,1)]])
dzrq = nlrq[int(dz_jd+0.5+8/24)-int(dzs_jd+0.5+8/24)]
for j in range(12): # 冬至以後的每月合朔表
hs = jd2ce(dzs_jd + 29*(j+1)) # 估測的合朔日期
heshuo = ephem.next_new_moon(hs) # 實際合朔日期
hsb.extend([[jd2gz(heshuo),time_format(heshuo,0),time_format(heshuo,1)]])