前推格里历

前推格里历（Proleptic Gregorian Calendar），亦称前公历、 Proceptic 西历、逆推格里历，是将现行的格里历（公历）的置闰规则向前推算，应用于其正式颁布年份1582年之前的日期的历法表达。这样做是为了方便历史年代的连续计算和表达，特别是在历史学、天文学和计算领域。

根据国际标准化组织 ISO 8601:2004 标准（第3.2.1节），在信息交换的合作伙伴同意的情况下，允许使用前推格里历来表示1582年之前的日期。

## 历史背景

格里历由教宗额我略十三世在1582年颁布，用以取代儒略历。儒略历每4年一闰，平均年长为365.25日，这比实际的回归年（约365.2422日）长了约11分14秒。这个微小的差异导致自公元325年第一次尼西亚公会议以来，春分日逐渐向更早的日期漂移。格里历的改革调整了置闰规则（每400年少3个闰日），并一次性删除了1582年10月中的10天（10月4日之后直接是10月15日），使春分日恢复到3月21日左右。

前推格里历的概念，便是将格里历的这些置闰规则应用到1582年之前，犹像格里历在那时就已经存在并被使用。这为研究和记录历史事件提供了一个统一的年代标尺。

## 应用场景

### 历史学

历史学家在处理1582年之前的日期时，有时会使用前推格里历来进行年代的标准化转换，尤其是在需要比较不同地区、不同历法记录的事件时。然而，最佳实践是首先记录历史文献中使用的原始日期（注明所用历法，如儒略历“旧式”日期），然后再提供其对应的前推格里历日期。

例如，美国首任总统乔治·华盛顿的生日，根据当时大英帝国（包括其美洲殖民地）使用的儒略历以及以3月25日为新年起始日的纪年方式，记录为1731年2月11日（旧式，O.S.）。大英帝国于1752年改用格里历，并将新年起始日改为1月1日。若将华盛顿的生日按前推格里历规则转换，并考虑到新年起始日的调整，则为1732年2月22日。明确注明“旧式”与“新式”（或前推格里历）对于避免混淆至关重要。

另一个例子是日本的神武天皇即位日。根据日本古籍《日本书纪》中的干支记载“辛酉年春正月庚辰朔”，明治时代的天文学家和历算家将其比定为前推格里历的公元前660年2月11日，并定为纪元节（后来的建国纪念之日）。此年代和日期的史实性在学术界有广泛讨论，但仍可作为前推格里历应用的一个实例。

### 计算机与软件

ISO 8601标准是日期和时间表示的国际标准，它在特定条件下允许使用前推格里历。许多编程语言和数据库系统为了简化日期时间的处理，内部可能采用前推格里历来表示历史日期。例如：

- PostgreSQL 数据库在处理日期时，对1582年之前的日期采用前推格里历。
- MySQL 数据库同样支持历史日期，其日历系统从公元1年开始就遵循格里历规则。
- SQLite 的日期和时间函数也能够处理1582年之前的日期，其行为兼容前推格里历。
- Python 的 `datetime` 模块，其标准日历算法也是基于前推格里历，可以表示从公元1年开始的日期。
- 微软公司的编程基础库ATL的类 `COleDateTime` 支持从公元100年1月1日开始的日期，其行为模式也符合前推格里历。

在这些系统中，1582年10月没有日期间断。

## 计算方法与规则

前推格里历的计算规则与标准格里历完全相同：

- 年份能被4整除但不能被100整除的是闰年（如2024年）。

- 年份能被400整除的是闰年（如2000年）。

- 其他年份为平年。

将此规则向前无限追溯即可得到任何年份的日数和日期。

### 公元元年及公元前年份

在历史纪年中，通常没有“公元0年”。公元1年之前是公元前1年。

然而，在天文学年份编号和ISO 8601标准中，为了方便计算，引入了公元0年，它对应于历史上的公元前1年。更早的年份则用负数表示（例如，公元前2年对应天文学年份-1年）。

在这种编号系统中：

- 天文学的 0 年 (对应历史上的公元前1年) 是闰年 (因为它相当于可以被4整除的年份，且适用格里历规则)。
- 历史上的公元前1年 (天文学0年)、公元前5年 (天文学-4年)、公元前9年 (天文学-8年) 等，如果按照格里历规则判断，都是闰年。

例如，要判断历史上的公元前 $y$ 年是否为前推格里历闰年：

- 若使用天文学年份编号，则年份为 $-(y-1)$。若 $-(y-1)$ 符合格里历闰年规则，则为闰年。

- 若直接使用公元前 $y$ 年，可以判断 $y$ 是否符合“能被4整除但非整百年份，或能被400整除的整百年份”这一规律的反推（需注意BC年号越大，年份越早）。更简便的是将其转换为对应的天文学年份判断。

### 儒略日

在实际计算中，尤其是需要跨越很长时间范围或在不同历法间转换时，天文学家和程序员常使用儒略日（Julian Day Number, JDN）作为中间参照。JDN是一个自前推儒略历公元前4713年1月1日世界时12:00起算的连续日数。任何日期都可以转换为JDN，反之亦然。进行这种转换时，必须明确指定所用的历法是儒略历还是（前推）格里历。

例如，可以通过Fliegel与 Van Flandern 在1968年提出的算法将格里历日期（包括前推格里历日期）转换为JDN。(请注意：某些从JDN转换回非常早期的前推格里历日期的简化算法可能存在问题，特别是在公元400年之前，因此需要使用经过验证的完整算法。)

另一个概念是Rata Die系统，它将公元1年1月1日（前推格里历）定义为第1天，并以此计算连续日数。一些编程系统使用此方法。

## 与儒略历的差异

由于儒略历和格里历的置闰规则不同（儒略历每4年一闰，格里历每400年少3闰），随着时间向前推移，两者累积的日数差异会逐渐变化。

- 在公元4年至公元100年2月28日（儒略历）期间，前推格里历比儒略历 慢2天。
- 从公元100年3月1日（儒略历）至200年2月28日（儒略历），前推格里历比儒略历 慢1天。
- 从公元200年3月1日（儒略历）至300年2月28日（儒略历），两者日期 相同。
- 此后，每经过一个儒略历的世纪年但非格里历闰年的世纪年（如500年、600年、700年、900年、1000年、1100年、1300年、1400年、1500年），前推格里历会比儒略历快一天。
- 到1582年10月格里历改革前夕，格里历日期已比儒略历日期快10天。

下表显示了在首次正式引入公历之前，儒略历和前推公历日期的区别：

儒略历范围｜公历范围｜公历领先日数

4年3月3日（预期儒略历的开始） 100年3月1日｜4年3月1日 100年2月28日｜-2日

100年3月2日 200年2月29日｜100年3月1日 200年2月28日｜-1日

200年3月1日 300年2月28日｜200年3月1日 300年2月28日｜0日

300年2月29日 500年2月27日｜300年3月1日 500年2月28日｜1日

500年2月28日 600年2月26日｜500年3月1日 600年2月28日｜2日

600年2月27日 700年2月25日｜600年3月1日 700年2月28日｜3日

700年2月26日 900年2月24日｜700年3月1日 900年2月28日｜4日

900年2月25日 1000年2月23日｜900年3月1日 1000年2月28日｜5日

1000年2月24日 1100年2月22日｜1000年3月1日 1100年2月28日｜6日

1100年2月23日 1300年2月21日｜1100年3月1日 1300年2月28日｜7日

1300年2月22日 1400年2月20日｜1300年3月1日 1400年2月28日｜8日

1400年2月21日 1500年2月19日｜1400年3月1日 1500年2月28日｜9日

1500年2月20日 1582年10月4日｜1500年3月1日 1582年10月14日｜10日

注意：上表假设儒略闰日为2月29日。然而，历史上儒略历的闰日（拉丁语：ante diem bis sextum Kalendas Martias）是通过重复2月24日来实现的（称为“双岗日”，bissextile day）。这意味着在儒略历的闰年中，严格来说有两个2月24日，这会影响2月24日至2月28日之间的日期计数。此外，儒略历在公元前45年至公元4年之间的置闰并非规律的每四年一闰，存在“闰年错误”时期，直到公元4年（或有说公元8年）之后才稳定下来。

## 使用中的混淆点与注意事项

- 旧式与新式日期 (Old Style and New Style dates)：在格里历被各国逐渐采用的过渡时期（1582年至20世纪初，如俄国到1918年，土耳其到1927年），同一历史事件可能因地区不同而有不同的日期记录。一些国家在改历的同时，还将新年从3月25日（或其他日期）移至1月1日。这会导致年份和日期都发生变化，如前述乔治·华盛顿的生日例子，其儒略历日期1731年2月11日不仅月份和日数不同于格里历的2月22日，年份也因旧式以3月25日为新年起点而记为1731年，但在以1月1日为新年的格里历中则属于1732年。
- 公元0年的问题：如前所述，历史纪年无公元0年，而天文学和ISO 8601有。使用时需明确是哪种体系。
- 史料的原始日期：研究历史时，应首先采用史料中记载的原始日期和历法，然后根据需要换算为前推格里历日期，并加以注明。
- 软件实现的差异：虽然许多系统声称支持前推格里历，但其对非常早期日期（如公元前）的处理细节，特别是闰年规则和公元0年的处理，可能存在细微差别，需查阅具体文档。一些简单的日期计算代码可能未完全实现格里历的所有复杂规则（如整百年份的判断），导致错误。