月相与经纬度——从"月有阴晴圆缺"到球面天文学,中秋到底该哪天圆?
风险科技 2026年9月21日 · 预计阅读 14 分钟
一、文化背景:月亮如何塑造了时间
从朔望到农历
月相变化是人类最早观测到的天文周期之一。朔望月(Synodic Month)——从一个新月到下一个新月——平均长度为29.53天。
中国农历正是基于朔望月的:每月初一为朔(新月),十五为望(满月)。所以农历月份的长度可以是29天或30天,全年354或355天,比公历短约11天——这就是农历春节每年都在公历不同日期的原因。
月亮的文化形象
- 中国:"明月几时有"——苏轼的中秋词,满月象征团圆
- 古希腊:塞勒涅(Selene)女神驾月车巡游夜空
- 伊斯兰:新月标志斋月和开斋节的开始
- 现代:"蓝月"("一个月里的第二次满月",或称"月蓝";另有"季节蓝月"=一个季度里第四次满月)、"超级月亮"(满月恰逢近地点)
二、月相的几何本质
核心原理
月相的本质是太阳-地球-月球三者之间的相对位置。

上图从北极上空俯瞰地月系统。太阳在右侧(图中水平箭头表示近似平行的太阳光)。
内圈(虚线圆)是月球绕地球的轨道,画的是月球在空间中的实际位置与光照:因为太阳很远,阳光近似平行,所以月球被照亮的半球始终朝向右侧(太阳方向)——不管它转到轨道的哪一处,亮面永远朝右。
外圈是北半球中纬度地面上看到的同一时刻的月相形状。它和内圈一一对应(虚线相连),但形状不同:外圈不是"亮面恒朝右",而是上弦各相亮面朝右(像字母 D),下弦各相亮面朝左(像字母 C)——因为观者在地面,看到的朝向由月亮与太阳在天空中的相对方位决定。
图中位置角 θ 从 0° 起逆时针增加,对应右下侧的"新月"(θ=0°,月球在地球与太阳之间,整轮不可见)→ 上弦月(θ=90°)→ 满月(θ=180°,地球在中间)→ 下弦月(θ=270°)→ 回到新月。照度
。 两点容易看错的地方:① 内圈与太阳同侧的那个位置(θ=0°)是新月而不是满月——月球背对地球的那一半被照亮了;② 外圈的"亮面朝向"每过半个月就要左右翻转一次(D 形 ↔ C 形),这正是南半球看月亮时形状相反的原因(见第五节)。
月相角
定义月相角
:日食(新月) :上弦月 :月食(满月) :下弦月
月相角随时间均匀变化:
其中
注意:按这个线性公式,7.4 天算出的不是整 90°,而是 90.2°——因为"恰好一半"的时刻并非朔望月的整数分之一。表中的数值是代码实跑结果,未做圆整。
python
import math
def moon_phase_angle(days_since_new):
"""计算月相角(度)"""
return (360 / 29.53 * days_since_new) % 360
def illumination_fraction(phase_angle):
"""计算被照亮比例(照度)"""
return (1 - math.cos(math.radians(phase_angle))) / 2
# 不同月相
print("月龄(天) | 月相角 | 照明比例 | 月相名称")
for days in [0, 3.7, 7.4, 11, 14.8, 18.5, 22.2, 26]:
angle = moon_phase_angle(days)
illum = illumination_fraction(angle)
# 判断月相
if abs(angle) < 10 or abs(angle - 360) < 10:
name = "🌑 新月"
elif abs(angle - 90) < 10:
name = "🌓 上弦月"
elif abs(angle - 180) < 10:
name = "🌕 满月"
elif abs(angle - 270) < 10:
name = "🌗 下弦月"
elif angle < 90:
name = "🌒 蛾眉月"
elif angle < 180:
name = "🌔 盈凸月"
elif angle < 270:
name = "🌖 亏凸月"
else:
name = "🌘 残月"
print(f" {days:5.1f} | {angle:6.1f}° | {illum:6.1%} | {name}")输出:
月龄(天) | 月相角 | 照明比例 | 月相名称
0.0 | 0.0° | 0.0% | 🌑 新月
3.7 | 45.1° | 14.7% | 🌒 蛾眉月
7.4 | 90.2° | 50.2% | 🌓 上弦月
11.0 | 134.1° | 84.8% | 🌔 盈凸月
14.8 | 180.4° | 100.0% | 🌕 满月
18.5 | 225.5° | 85.0% | 🌖 亏凸月
22.2 | 270.6° | 49.4% | 🌗 下弦月
26.0 | 317.0° | 13.5% | 🌘 残月三、月出月没时间:月相决定可见性
月相决定了月亮什么时间出现在天空中。这是一条实用的规律:
| 月相 | 月出时间 | 上中天时间 | 月没时间 | 最佳观测时段 |
|---|---|---|---|---|
| 🌑 新月 | 日出 | 正午 | 日落 | 不可见(与太阳同升落) |
| 🌒 蛾眉月 | 上午 | 下午 | 晚上 | 日落后西方天空 |
| 🌓 上弦月 | 正午 | 日落 | 午夜 | 下午到前半夜 |
| 🌔 盈凸月 | 下午 | 晚上 | 凌晨 | 前半夜 |
| 🌕 满月 | 日落 | 午夜 | 日出 | 整夜可见 |
| 🌖 亏凸月 | 晚上 | 凌晨 | 上午 | 后半夜 |
| 🌗 下弦月 | 午夜 | 日出 | 正午 | 后半夜到清晨 |
| 🌘 残月 | 凌晨 | 上午 | 下午 | 日出前东方天空 |
核心规律:月亮的可见时间比太阳大约晚
python
def moon_phase_angle(days_since_new):
"""月相角(度)—— 见第二节定义"""
return (360 / 29.53 * days_since_new) % 360
def moon_rise_set(days_since_new):
"""估算月出月没时间"""
angle = moon_phase_angle(days_since_new)
# 月出比日出晚的时间(小时)
# 新月与太阳同升落,每天推迟约50分钟
delay_hours = angle / 360 * 24 # 满月时推迟12小时
# 月出 ≈ 日出 + delay
sunrise = 6 # 简化:日出约6点
sunset = 18 # 日落约18点
moonrise = (sunrise + delay_hours) % 24
moonset = (sunset + delay_hours) % 24
return moonrise, moonset
# 不同月相的月出月没
print("月相 | 月出 | 上中天 | 月没 | 可见时段")
for days in [0, 4, 7.4, 11, 14.8, 18.5, 22.2, 26]:
rise, set = moon_rise_set(days)
transit = (rise + 6) % 24 # 近似:月出后约6小时上中天
names = {0: "新月", 4: "蛾眉月", 7.4: "上弦月", 11: "盈凸月",
14.8: "满月", 18.5: "亏凸月", 22.2: "下弦月", 26: "残月"}
print(f" {names[days]:4s} | {rise:5.1f}时 | {transit:5.1f}时 | {set:5.1f}时 | ", end="")
if days < 2:
print("白天不可见")
elif days < 7:
print(f"日落后至 {set:.0f} 时")
elif days < 13:
print(f"午后至 {set:.0f} 时")
elif days < 17:
print("整夜可见")
elif days < 21:
print("半夜至上午")
else:
print(f"日出前至 {rise:.0f} 时可见")输出:
月相 | 月出 | 上中天 | 月没 | 可见时段
新月 | 6.0时 | 12.0时 | 18.0时 | 白天不可见
蛾眉月 | 9.3时 | 15.3时 | 21.3时 | 日落后至 21 时
上弦月 | 12.0时 | 18.0时 | 0.0时 | 午后至 0 时
盈凸月 | 14.9时 | 20.9时 | 2.9时 | 午后至 3 时
满月 | 18.0时 | 0.0时 | 6.0时 | 整夜可见
亏凸月 | 21.0时 | 3.0时 | 9.0时 | 半夜至上午
下弦月 | 0.0时 | 6.0时 | 12.0时 | 日出前至 0 时可见
残月 | 3.1时 | 9.1时 | 15.1时 | 日出前至 3 时可见四、月球赤纬与观测纬度
和太阳一样,月球也有赤纬——月球在天空中的高度不仅取决于时间,还取决于观测地的纬度。
月球赤纬的变化
月球赤纬的变化比太阳更复杂,因为月球轨道面与黄道面有约
月球的赤纬在约
月亮高度角
与太阳高度角公式完全相同,只是代入月球赤纬
其中
不同纬度看月亮
一个有趣的现象是月亮的"朝向"随纬度变化:
- 北半球中纬度(如北纬 40°):上弦月傍晚,亮边朝下偏右,看起来像字母"D"
- 南半球中纬度(如南纬 40°):同样时刻,亮边朝下偏左,看起来像字母"C"
- 近赤道:亮边几乎正朝下(位置角接近 0°),月亮像一艘平躺的小船
这是因为月球被照亮的方向始终朝向太阳,而太阳在天空中的位置随纬度不同——所以"亮边朝哪"不能用"北半球一律 D、南半球一律 C"来记。
上面的代码算了亮边位置角(0°=朝上、90°=朝右、180°=朝下、270°=朝左):北纬 40° 得 49.9°(右下方,D 形),南纬 40° 得 310.1°(左下方,C 形),而中间纬度落在"上半亮"到"下半亮"的过渡区。赤道附近接近"下半亮"的极端,看起来就是横躺着的小船。
python
def moon_elevation(lat, moon_dec, hour_angle=0):
"""计算月亮高度角(度)"""
lat_r = math.radians(lat)
dec_r = math.radians(moon_dec)
ha_r = math.radians(hour_angle)
sin_h = (math.sin(lat_r) * math.sin(dec_r) +
math.cos(lat_r) * math.cos(dec_r) * math.cos(ha_r))
sin_h = max(-1, min(1, sin_h))
return math.degrees(math.asin(sin_h))
print("满月夜半(0时),不同纬度月亮的最高高度:")
print("城市 | 纬度 | 月亮最高高度 | 印象")
for city, lat in [("漠河", 53.5), ("北京", 39.9), ("上海", 31.2),
("广州", 23.1), ("赤道", 0), ("悉尼", -33.9)]:
# 假设满月时月球赤纬约23.5°(夏至附近)
for moon_dec in [23.5, 0, -23.5]:
elev = moon_elevation(lat, moon_dec, 0)
if abs(elev - 90) < 30: # 找最高的
pass
# 满月时月球在太阳对面,赤纬约-23.5°(冬季)或+23.5°(夏季)
dec_winter = -23.5 # 冬至附近满月
dec_summer = 23.5 # 夏至附近满月
elev_w = moon_elevation(lat, dec_winter, 0)
elev_s = moon_elevation(lat, dec_summer, 0)
print(f" {city} | {lat:>5.1f}° | 冬{elev_w:5.1f}°/夏{elev_s:5.1f}° | ", end="")
if abs(lat) < 10:
print("月亮接近天顶")
elif lat > 30:
print("冬季满月高,夏季满月低")
else:
print("适中")输出:
满月夜半(0时),不同纬度月亮的最高高度:
城市 | 纬度 | 月亮最高高度 | 印象
漠河 | 53.5° | 冬 13.0°/夏 60.0° | 冬季满月高,夏季满月低
北京 | 39.9° | 冬 26.6°/夏 73.6° | 冬季满月高,夏季满月低
上海 | 31.2° | 冬 35.3°/夏 82.3° | 冬季满月高,夏季满月低
广州 | 23.1° | 冬 43.4°/夏 89.6° | 适中
赤道 | 0.0° | 冬 66.5°/夏 66.5° | 月亮接近天顶
悉尼 | -33.9° | 冬 79.6°/夏 32.6° | 适中注意一个有趣的对称性:北半球冬季满月高悬南方天空(因为太阳在南方,月球在对面),夏季满月低垂;南半球则完全相反。
五、月相观测的经纬度效应
月亮的"笑脸"朝向
这是最有趣的视觉现象。月亮的明暗界线(terminator)的倾斜角度取决于观测地的经纬度和时间。
在北半球中纬度,当我们月出后不久看蛾眉月,月亮的凸面朝右下方;而在南半球,同样的月相,凸面朝左上方。
数学解释
月亮的明亮部分朝向太阳。在地平坐标系中,太阳与月亮的方位角差决定了月亮的"朝向"。这个角度由球面三角公式决定:
python
def altaz(lat, dec, ha):
"""地平坐标:高度角 h 与方位角 A(A 从北起算,向东为正)"""
la, de, H = math.radians(lat), math.radians(dec), math.radians(ha)
sinh = math.sin(la) * math.sin(de) + math.cos(la) * math.cos(de) * math.cos(H)
sinh = max(-1, min(1, sinh))
h = math.degrees(math.asin(sinh))
# 方位角(用 atan2 保证四象限正确,避免 acos 的"东西不分")
y = -math.cos(de) * math.sin(H)
x = math.sin(de) * math.cos(la) - math.cos(de) * math.sin(la) * math.cos(H)
A = math.degrees(math.atan2(y, x)) % 360
return h, A
def moon_bright_side_angle(lat, moon_dec, moon_ha, sun_dec, sun_ha):
"""返回【月面亮边的位置角】(度):0°=亮边在正上方,90°=在右,180°=在下
原理:月亮亮的一侧永远指向太阳。在天球上,从月亮中心指向太阳的
方向角就是亮边的朝向。用两者的地平坐标(高度/方位)算这个夹角。
"""
hm, Am = altaz(lat, moon_dec, moon_ha)
hs, As = altaz(lat, sun_dec, sun_ha)
# 两者在地平坐标下的角距(球面余弦)
dA = math.radians(As - Am)
cos_sep = (math.sin(math.radians(hm)) * math.sin(math.radians(hs)) +
math.cos(math.radians(hm)) * math.cos(math.radians(hs)) * math.cos(dA))
sep = math.degrees(math.acos(max(-1, min(1, cos_sep))))
# 位置角:以"上"为 0°,顺时针为正(天球上从月亮看太阳的方位)
y = math.sin(dA) * math.cos(math.radians(hs))
x = math.cos(math.radians(hm)) * math.sin(math.radians(hs)) - \
math.sin(math.radians(hm)) * math.cos(math.radians(hs)) * math.cos(dA)
pa = math.degrees(math.atan2(y, x)) % 360
return hm, Am, sep, pa
print("上弦月(傍晚,太阳在地平线下约 10°)的亮边朝向:")
print("纬度 | 月亮高度 | 亮边位置角 | 视觉朝向")
for lat in [40, 20, 0, -20, -40]:
# 上弦月:月亮时角约 -45°(偏西),太阳已落到地平线下(时角约 +15°)
hm, Am, sep, pa = moon_bright_side_angle(lat, 0, -45, 0, 15)
# pa: 0°=上, 90°=右, 180°=下, 270°=左
if 45 <= pa < 135:
orient = "右半边亮(字母 D 形)"
elif 135 <= pa < 225:
orient = "下半亮"
elif 225 <= pa < 315:
orient = "左半边亮(字母 C 形)"
else:
orient = "上半亮"
print(f" {lat:+4d}° | {hm:5.1f}° | {pa:6.1f}° | {orient}")输出:
上弦月(傍晚,太阳在地平线下约 10°)的亮边朝向:
纬度 | 月亮高度 | 亮边位置角 | 视觉朝向
+40° | 32.8° | 49.9° | 右半边亮(字母 D 形)
+20° | 41.6° | 27.2° | 上半亮
+0° | 45.0° | 0.0° | 上半亮
-20° | 41.6° | 332.8° | 上半亮
-40° | 32.8° | 310.1° | 左半边亮(字母 C 形)(一个常见的误会) 澳大利亚国旗上的新月"躺着",常被说成"因为南半球看月亮是躺着的"。这个推断不成立:国旗上的弯月是伊斯兰教新月的通用符号(该符号本身源自古代近东的月神崇拜),澳大利亚用它表达历史与地理关联,与本地观测几何无关。南半球看月亮确实与北半球上下相反,但那是观测者朝向造成的视觉效果,跟两面国旗上怎么画月亮是两回事。
六、月球轨道与近地点、远地点
月球轨道不是正圆,因此地月距离在变化:
- 近地点(Perigee):约363,300 km
- 远地点(Apogee):约405,500 km
这个距离变化约11%,导致满月的视直径变化:
其中
python
def moon_angular_diameter(distance_km):
"""计算月球视直径(角分)"""
R_moon = 1737 # km
theta_rad = 2 * math.atan(R_moon / distance_km)
return math.degrees(theta_rad) * 60 # 转换为角分
# 各距离下的视直径
distances = [
("近地点", 363300),
("平均距离", 384400),
("远地点", 405500),
]
print("位置 | 地月距离 | 视直径 | 与平均比较")
for name, d in distances:
ang = moon_angular_diameter(d)
avg_ang = moon_angular_diameter(384400)
pct = (ang / avg_ang - 1) * 100
print(f" {name} | {d:>6d} km | {ang:.2f}' | {pct:+5.1f}%")输出:
位置 | 地月距离 | 视直径 | 与平均比较
近地点 | 363300 km | 32.87' | +5.8%
平均距离 | 384400 km | 31.07' | +0.0%
远地点 | 405500 km | 29.45' | -5.2%"超级月亮"(Supermoon)指满月恰逢近地点附近。它不是天文学术语,是占星师 Richard Nolle 在 1979 年提出的通俗说法,通常定义为"满月时地月距离小于约 360,000 km"——门槛是人为设定的。相对平均满月,视直径大约 +5.8%,面积大约 +12.0%。
这个幅度比常见说法小:网上流传的"大 14%、亮 30%"是把近地点最小值到远地点最大值的全程差(约 14%)当成了"与平均之比",又叠加了亮度感知的放大。以平均为基准,真实增幅是 5.8%——肉眼能分辨,但远不到"震撼"的程度。
七、月相与潮汐
潮汐是月相最直接的地球物理效应。
潮汐原理
月球引力在地球表面产生两个潮汐鼓包(一个朝向月球,一个背向月球)。地球自转使每个地点每天经历两次高潮和两次低潮(间隔约12小时25分钟)。
大潮与小潮
- 朔/望(新月/满月):太阳与月球引力方向一致 → 大潮(spring tide),潮差最大
- 上/下弦月:太阳与月球引力方向垂直 → 小潮(neap tide),潮差最小
python
def moon_phase_angle(days_since_new):
"""月相角(度)—— 见第二节定义"""
return (360 / 29.53 * days_since_new) % 360
def tide_range(moon_age_days):
"""基于月相估算潮差比(平均潮差的倍数)
► 2026-09 勘误:原实现写 `sun_angle = (angle + 180) % 360`,把相位反了,
结果算成"新月最小、弦月最大",与事实完全相反。
正确做法:太阳与月球的夹角(elongation)就等于月相角 α 本身 ——
α=0(新月,日地月一线)时日月引力同向,潮差最大;
α=180(满月)时也接近一线,同样最大;α=90/270(弦月)时垂直,最小。
所以被引用的量是 cos(α),不是 cos(α+180)。
"""
angle = moon_phase_angle(moon_age_days)
# 太阳潮汐贡献 ≈ 月球潮汐的 0.46 倍
# 合成潮差 = 1 + 0.46*cos(α) ← 相位用 α 本身
tide_ratio = 1 + 0.46 * abs(math.cos(math.radians(angle)))
return tide_ratio
print("月相 | 潮差比 | 潮汐类型")
for days, name in [(0, "新月"), (5, "蛾眉月"), (7.4, "上弦月"),
(11, "盈凸月"), (14.8, "满月"), (22.2, "下弦月")]:
ratio = tide_range(days)
ttype = "🌊 大潮" if ratio > 1.3 else "🌊 小潮" if ratio < 0.8 else "普通"
print(f" {name} | {ratio:.2f} | {ttype}")输出:
月相 | 潮差比 | 潮汐类型
新月 | 1.46 | 🌊 大潮
蛾眉月 | 1.22 | 普通
上弦月 | 1.00 | 普通
盈凸月 | 1.32 | 🌊 大潮
满月 | 1.46 | 🌊 大潮
下弦月 | 1.01 | 普通这正是沿海渔民"初一十五涨大潮"、"初八二十三到处是泥滩"的经验总结——前者是大潮,后者是小潮。
八、月球轨道与交点
月球的轨道面与黄道面有约
注意别把两个周期混为一谈:
| 周期 | 长度 | 含义 |
|---|---|---|
| 交点退行周期 | 18.6 年 | 交点绕黄道退行一周 |
| 沙罗周期 | 18 年 11 天 8 小时 | 日食/月食重复出现的周期 |
沙罗周期之所以有零头(差约 1/3 天),是因为它要同时接近朔望月、交点月的公倍数——它并不等于 18.6 年,两者数值接近纯属巧合。沙罗周期能让同一类食隔 18 年重现,但每次地球自转使可见地带西移约 120°。
日食与月食的条件
- 日食:朔(新月)+ 月球在黄白交点附近(月球穿过太阳与地球的连线)
- 月食:望(满月)+ 月球在黄白交点附近(地球穿过太阳与月球的连线)
python
def eclipse_season(day_of_year, node_position):
"""判断是否在食季"""
# 食季:交点前后约18天
distance_to_node = min(
abs(day_of_year - node_position),
365 - abs(day_of_year - node_position)
)
return distance_to_node < 18
# 近似:2026年升交点在春分附近
ascending_node = 80 # 约春分日
print("2026年食季判断:")
for doy, name in [(80, "春分"), (172, "夏至"), (266, "秋分"), (355, "冬至")]:
is_season = eclipse_season(doy, ascending_node)
print(f" {name}(第{doy}天): {'✅ 食季' if is_season else '普通'}")输出:
2026年食季判断:
春分(第80天): ✅ 食季
夏至(第172天): 普通
秋分(第266天): 普通
冬至(第355天): 普通注意这个例子的局限:交点位置用的是"约春分日"的粗略估值,且只在四个节气上做了判断,所以只有春分落在食季内。真实的食季判断需要当时的交点黄经,不能用一个固定常数代替——这里演示的是判据形式,不是可用的天文表。
九、Python:任意日期的月相计算
综合以上知识,写一个函数计算任意日期的月相近似值。
python
import math
from datetime import date, datetime
def moon_phase_approx(dt):
"""计算给定日期的月相近似值(基于2000年1月6日新月)"""
# 已知参考新月:2000年1月6日 18:14 UTC
ref_new_moon = date(2000, 1, 6)
# 计算天数差
delta = dt - ref_new_moon
days = delta.days
# 朔望月周期
synodic_month = 29.5305877
# 月龄
moon_age = days % synodic_month
phase_angle = (moon_age / synodic_month) * 360
# 照明比例
illum = (1 - math.cos(math.radians(phase_angle))) / 2
# 月相判断
if phase_angle < 22.5 or phase_angle >= 337.5:
phase = "🌑 新月"
elif phase_angle < 67.5:
phase = "🌒 蛾眉月"
elif phase_angle < 112.5:
phase = "🌓 上弦月"
elif phase_angle < 157.5:
phase = "🌔 盈凸月"
elif phase_angle < 202.5:
phase = "🌕 满月"
elif phase_angle < 247.5:
phase = "🌖 亏凸月"
elif phase_angle < 292.5:
phase = "🌗 下弦月"
else:
phase = "🌘 残月"
return moon_age, phase_angle, illum, phase
# 测试几个日期
test_dates = [
date(2026, 1, 1), # 元旦
date(2026, 2, 17), # 春节(农历正月初一)
date(2026, 6, 4), # 写作日期
date(2026, 7, 29), # 七夕(农历七月初七)
date(2026, 9, 25), # 中秋(农历八月十五)
date(2026, 10, 6), # 国庆后
]
print("日期 | 月龄 | 月相角 | 照明 | 月相")
for dt in test_dates:
age, angle, illum, phase = moon_phase_approx(dt)
print(f" {dt} | {age:5.2f}天 | {angle:6.1f}° | {illum:5.1%} | {phase}")输出:
日期 | 月龄 | 月相角 | 照明 | 月相
2026-01-01 | 12.68天 | 154.6° | 95.2% | 🌔 盈凸月
2026-02-17 | 0.62天 | 7.6° | 0.4% | 🌑 新月
2026-06-04 | 19.03天 | 232.0° | 80.8% | 🌖 亏凸月
2026-07-29 | 14.97天 | 182.5° | 100.0% | 🌕 满月
2026-09-25 | 13.91天 | 169.5° | 99.2% | 🌕 满月
2026-10-06 | 24.91天 | 303.6° | 22.3% | 🌘 残月春节是新月(农历初一),中秋接近满月——历法对上了。但要注意:中秋当天并不是"最圆"的时刻。这里算的是近似月龄,真实满月时刻由天文算法给出(见第十二节:2026 年中秋的满月落在两天后)。
另外,这个近似公式用固定的 29.5305877 天做拟合,误差可达 ±1 天——它足以判断"大概哪种月相",不足以用来定交节或定朔望。
十、月球的长期演化:潮汐锁定与远离
潮汐锁定
月球已经被地球潮汐锁定(Tidal Locking)——它的自转周期等于公转周期(约27.3天)。这就是为什么我们永远看到月球的同一面("正面"),看不见背面("暗面"——其实是"远面")。
月球在远离
地球的潮汐力正在将月球推离地球:每年约3.8厘米。这个现象通过阿波罗登月计划放置的激光反射镜精确测量得到。
这里有个比"10 亿年后"更值得说的事实:月球和太阳看起来一样大,这个"巧合"其实并不精确。
- 太阳视直径:31.46′(地球在远日点)~ 32.53′(近日点),平均约 31.99′
- 月球视直径:29.37′(远地点)~ 33.51′(近地点),平均约 31.08′
也就是说,按平均距离,月球(31.08′)比太阳(31.99′)小约 0.9 角分。日全食之所以还能发生,靠的是月球恰好跑到近地点附近(那时 33.51′ > 31.99′,超出约 1.5′)。换个位置,同样的日月食几何就只能给出日环食——天上镶一圈金边。
所以"日全食将在 10 亿年后消失"这句话,方向对但机理错:严格来说,月球视直径超过太阳这件事,现在就已经只在近地点附近成立了。随着月球每年远离 3.8 cm,这个"能全食"的窗口会越来越窄,而不是"忽然在某个时刻消失"。
到 10 亿年时地月距离增加约 38,000 km,月球平均视直径降到 28.28′——比现在小了近 3 角分,日全食基本退出舞台。
python
def moon_angular_diameter(distance_km):
"""月球视直径(角分)—— 见第六节定义"""
return math.degrees(2 * math.atan(1737.4 / distance_km)) * 60
def moon_distance_evolution(year):
"""计算给定年份的地月平均距离"""
d0 = 384400 # 当前平均距离(km)
rate = 0.000038 # 每年增加(km) = 3.8 cm/年
d = d0 + rate * (year - 2026)
return d
# 太阳视直径随日地距离变化:约 31.46′(远日) ~ 32.53′(近日)
# 判定日全食:月球视直径 >= 太阳视直径。取太阳平均 31.99′ 作参考线。
SUN_AVG = 31.99
print("年份 | 地月距离(km) | 月视直径 | 与太阳平均比 | 相对今天的日食形态")
for year in [2026, 500_000_000, 1_000_000_000, 2_000_000_000]:
d = moon_distance_evolution(year)
ang = moon_angular_diameter(d)
ratio = ang / SUN_AVG
tag = ("全食需月球在近地点附近" if ratio < 1 else "平均距离下即可全食")
print(f" {year:>10d} | {d:>10.0f} km | {ang:5.2f}' | {ratio:9.4f} | {tag}")输出:
年份 | 地月距离(km) | 月视直径 | 与太阳平均比 | 相对今天的日食形态
2026 | 384400 km | 31.08' | 0.9714 | 全食需月球在近地点附近
500000000 | 403400 km | 29.61' | 0.9257 | 全食需月球在近地点附近
1000000000 | 422400 km | 28.28' | 0.8840 | 全食需月球在近地点附近
2000000000 | 460400 km | 25.95' | 0.8111 | 全食需月球在近地点附近实际上月球的远离速度在亿年尺度上会变化,但趋势是确定的:未来的日全食将越来越少,最终消失。
十一、中秋:为什么是八月十五,又为什么常常"十六圆"
中秋的公历日期,摆动幅度有多大
农历八月十五固定在公历的哪个位置?答案是:不固定,而且摆幅不小。
用农历历法数据把 2000—2050 年(51 年)的中秋日期全部算出来:
python
from lunardate import LunarDate
zq = []
for y in range(2000, 2051):
d = LunarDate(y, 8, 15).to_solar_date()
zq.append((y, d))
sept = [d for _, d in zq if d.month == 9]
octo = [d for _, d in zq if d.month == 10]
print(f"样本 {len(zq)} 年")
print(f"落在公历 9 月 : {len(sept)} 次 范围 {min(sept)} ~ {max(sept)}")
print(f"落在公历 10 月: {len(octo)} 次 范围 {min(octo)} ~ {max(octo)}")
print(f"全部 {len(zq)} 年的最早/最晚: {min(d for _, d in zq)} / {max(d for _, d in zq)}")输出:
样本 51 年
落在公历 9 月 : 39 次 范围 2000-09-12 ~ 2050-09-30
落在公历 10 月: 12 次 范围 2001-10-01 ~ 2047-10-04
全部 51 年的最早/最晚: 2000-09-12 / 2050-09-30也就是说,51 年里中秋最早出现在 9 月 12 日(2000 年),最晚拖到 10 月 4 日(2047 年)——同一年内可能的落点跨度是 22 天(9 月 12 日到 10 月 4 日)。约四分之一的中秋(12/51)落在 10 月。
这个摆动的来源是农历与阳历的错位:
- 农历平年 12 个朔望月 ≈ 354.37 天,比回归年短约 10.9 天
- 所以每过一个平年,中秋就比前一年早约 11 天
- 累积到一定程度,加一个闰月(13 个月 ≈ 383.9 天),一下子又推后约 19 天
- 一进一退之间,中秋就在一个多月的区间里来回摆
这也解释了为什么中秋总在秋分前后:农历八月大致对应公历 9—10 月,而秋分固定在 9 月 22—23 日。中秋被"锚"在秋分附近,但一年一年地在其左右漂移。
「十五的月亮十六圆」到底占多少
这句话流传很广,那它到底对不对?要看天文满月的时刻落在农历初几。
用 Meeus 月相算法(精度约几分钟)算 2000—2050 年中秋对应的满月时刻,按北京时间归入农历日:
python
import math
from datetime import datetime, timedelta, timezone
from lunardate import LunarDate
# 满月时刻(Meeus《Astronomical Algorithms》第49章,取主修正项)
def full_moon_jde(k):
T = (k - 0.5) / 1236.85
JDE = (2451550.09766 + 29.530588861 * (k - 0.5)
+ 0.00015437 * T**2 - 0.00000015 * T**3)
E = 1 - 0.002516 * T - 0.0000074 * T * T
M = math.radians((2.5534 + 29.10535670 * (k - 0.5)) % 360)
Mp = math.radians((201.5643 + 385.81693528 * (k - 0.5)
+ 0.0107582 * T**2) % 360)
F = math.radians((160.7108 + 390.67050284 * (k - 0.5)
- 0.0016118 * T**2) % 360)
O = math.radians((124.7746 - 1.56375588 * (k - 0.5)) % 360)
return JDE + (-0.40614 * math.sin(Mp) + 0.17302 * E * math.sin(M)
+ 0.01614 * math.sin(2*Mp) + 0.01043 * math.sin(2*F)
+ 0.00734 * E * math.sin(Mp - M)
- 0.00515 * E * math.sin(Mp + M)
+ 0.00209 * E*E * math.sin(2*M)
- 0.00111 * math.sin(Mp - 2*F)
- 0.00057 * math.sin(Mp + 2*F)
+ 0.00056 * E * math.sin(2*Mp + M)
- 0.00042 * math.sin(3*Mp)
+ 0.00042 * E * math.sin(M + 2*F)
+ 0.00038 * E * math.sin(M - 2*F)
- 0.00024 * E * math.sin(2*Mp - M)
- 0.00017 * math.sin(O))
def jde_to_bj(jde):
"""JDE → 北京时间(含 ΔT 近似)"""
T = (jde - 2451545.0) / 36525
dt_s = 62.92 + 0.32217*T + 0.005589*T*T
days = (jde - dt_s/86400) - 2451545.0
return (datetime(2000,1,1,12,tzinfo=timezone.utc)
+ timedelta(days=days) + timedelta(hours=8))
dist = {}
for y in range(2000, 2051):
gd = LunarDate(y, 8, 15).to_solar_date()
k0 = math.floor((y - 2000) * 12.3685)
fm = min((jde_to_bj(full_moon_jde(k)) for k in range(k0-1, k0+15)),
key=lambda t: abs((t.date() - gd).days))
day = 15 + (fm.date() - gd).days
dist[f"八月{day:02d}"] = dist.get(f"八月{day:02d}", 0) + 1
tot = sum(dist.values())
for k in sorted(dist):
print(f"{k}: {dist[k]:2d} 次 ({dist[k]/tot*100:5.1f}%)")输出:
八月15: 20 次 ( 39.2%)
八月16: 24 次 ( 47.1%)
八月17: 7 次 ( 13.7%)结论:这句话是对的,但只说出了不到一半的情况。
- 「八月十六圆」占 47.1%——是最大的一类,所以俗话有理
- 「八月十五圆」占 39.2%——正日子真圆的也不少
- 「八月十七圆」占 13.7%——每七年左右就有一次
2026 年就是"十七圆"那一类:中秋在 9 月 25 日(周五),而满月时刻是北京时间 9 月 27 日 00:48(UTC 9 月 26 日 16:49)。中秋当晚其实已经很圆了(照明约 99.8%),但天文意义上的"望"要等到两天后。
为什么会这样?因为朔望月的长度不是整数天(29.53 天),而农历的月份天数只能是 29 或 30 天。"初一"锚定的是朔(新月)时刻所在的日期,不是时刻本身——朔可能发生在初一凌晨,也可能在初一深夜。这个零点几天的差值,累积到十五就决定了满月是落在十五、十六还是十七。
一句话:农历把"朔"对齐到了日期,但满月自己不管这些——它按自己的节奏走,落在哪天是算出来的,不是定出来的。
2026 年中秋的月亮,能看什么
拿上面同一套算法看 2026 年:
| 项目 | 数值 |
|---|---|
| 中秋日期 | 2026-09-25(周五,农历八月十五) |
| 满月时刻 | 北京时间 09-27 00:48 |
| 上次朔(新月) | 北京时间 09-11 11:27 |
| 月龄(中秋当晚 21:00) | 约 14.4 天 |
| 中秋当晚照明比例 | 约 99.8%(肉眼已是满月) |
顺带一个与第七节呼应的现象:2026 年 9 月的满月是收获月(Harvest Moon)——它是离秋分最近的那次满月。收获月的特点是连续几晚月出时间只推迟 20—30 分钟(平时约 50 分钟),因为月球轨道与地平线的夹角在这段时间特别小。古人靠它延长秋收的夜间作业时间,这大概是"天文现象直接变成生产力"的最早案例之一。
十二、数学文化:月球运动的数学描述史
艾萨克·牛顿(Isaac Newton, 1643-1727)
英国物理学家。1687 年《自然哲学的数学原理》中,他首次用万有引力解释了月球的运动——在此之前,开普勒定律只精确描述行星绕日,月球绕地的复杂轨迹(受太阳摄动)一直无法用统一规律说明。牛顿的贡献不是"解释月相"(月相的几何成因在古希腊就已大致清楚,是太阳光照角度问题),而是把月球的位置计算变成了力学可预测的问题;他还在同书中给出了潮汐由日月引力引起的定量解释。
皮埃尔-西蒙·拉普拉斯(Pierre-Simon Laplace, 1749-1827)
法国数学家和天文学家。1799—1825 年出版五卷《天体力学》(Traité de mécanique céleste),把太阳、地球、月球三体相互作用的引力展开为无穷级数,给出了可长期使用的高精度月球位置理论——月球的复杂运动第一次被写成一套能算到小数点后多位的公式。
关于天平动(Libration,月球因轨道的偏心与倾角,使地球上能看到约 59% 月面而非恰好一半):发现者是伽利略(他的月球素描已记录该现象),拉普拉斯做的是给出其成因的严格力学解释与定量计算。把两者区分开更准确。
关键要点
- 月相的本质是月球绕地球公转造成的日-地-月几何关系变化,照明比例
- 朔望月周期29.53天,决定了月出月没时间每天推迟约50分钟
- 月出月没受月相角
控制:新月与太阳同升落,满月与太阳对升对落 - 月球赤纬在
间变化,月亮的观测高度角由球面公式 决定 - 不同纬度的月亮朝向不同:亮边永远指向太阳;北半球中纬度上弦呈 D 形、南半球中纬度呈 C 形、近赤道则接近横躺的小船(朝向随纬度连续过渡,不是南北二分)
- 潮汐的强弱由月相决定:大潮(新月/满月)潮差最大,小潮(弦月)潮差最小
- 日食月食的条件是新月/满月 + 月球在黄白交点附近——食季每半年一次
- 月球正在远离地球(每年约 3.8 cm)。注意:月球平均视直径(31.08′)本来就略小于太阳(31.99′),日全食只在月球靠近近地点时才发生;随着月球远去,这种机会会越来越少
[1] Duffett-Smith, P., & Zwart, J. (2017). Practical Astronomy with your Calculator or Spreadsheet. 4th Ed. Cambridge University Press
[2] Meeus, J. (1998). Astronomical Algorithms. 2nd Ed. Willmann-Bell
[4] 陈遵妫 (1984). 《中国天文学史》. 上海人民出版社
[5] 3Blue1Brown (2021). The Geometry of Moon Phases. YouTube. 用三维动画直观展示月相成因。
数据说明:月相角、照明比例、视直径、潮汐比等由文中代码在本机实跑得出,输出为运行原样;2026 年中秋(9 月 25 日,农历八月十五)与满月时刻(北京时间 9 月 27 日 00:48)经权威天文源交叉核对;中秋公历日期分布(2000–2050)与"十五/十六圆"比例由 Meeus 月相算法批量计算,代码与结果留档于
data/moon_facts.json。古籍引文据通行本。图片说明:月相几何图由文中几何计算绘制(亮面朝向按"始终朝向太阳"求解,并附照度自校验),非示意图。
本文由 AI 辅助创作,天文计算、代码验证与数据核对由作者完成。