搜尋本站文章

2009-09-12

淺談:消失的日期,以關聯式資料庫的日期資料類型為例;julian Day、Julian Calendar、Gregorian



在我們日常使用的西元曆法中,在電腦的資訊系統使用上,其實有一段消失的日期。
以下為在 Unix、Linux 系統上,使用 cal 指令所呈現的日期資訊:




在上圖中,可以觀察到 1752 年 9 月份內,1752/09/03 ~ 1752/09/13 這段日期,是消失的日期。

但是為何會如此呢?

這可能是因為使用的曆法所造成之問題。

西方古代的曆法非常混亂,直到公元前 46 年,羅馬的凱撒大帝 (Julius Caesar) 根據天文學家 Sosigenes 建議,修訂古羅馬曆改成新的 Julian 曆法。
將一年分為十二個月,規定單數月為 31 日,雙數月為30日,通常二月是 29 日 (平年),每四年設置一閏年,閏年的二月加多一日成為 30 日。

該曆法的結果是,與地球繞日的回歸周期,每 128 年累積的偏差達一日。
公元前 8 年,羅馬議會將八月改成奧古斯都皇帝 (Augustus Caesar) 之名,稱為 August。

同時將八月改為大月而成 31 日,使它和紀念凱撒 (Julius Caesar) 的七月 (July) 日數相同,以顯示他和凱撒的功業同等偉大。


直到 16 世紀,天主教教皇 Gregory 十三世根據天文學家 Aloysius Lilius 及 Christopher Clavius建議,從當時曆法中減去十天,使春分出現在 3 月 21 日,因為他們觀察到春分點發生在 3 月 11 日。

執行以下改革並稱為 Gregorian 曆,其 3 千 3 百多年才會累積一日的偏差。
將 1582 年 10 月 5 日至 1582 年 10 月 14 日這 10 日取消。
每個可被 4 整除的年份是一個閏年。
但 00 結尾的年份一定要被 400 整除,才能算是閏年。否則不是閏年。

因此,1700,1800,1900,2100 和 2200 年都不是閏年。而1600,2000,和 2400年是閏年。


相比而言,舊式的 Julian 曆法裡面只有能被 4 整除的年是閏年。

Gregorian 曆也就是現今世界通用的曆法,羅馬教廷宣佈 1852 年 10 月 4 日後面緊跟著就是15 日。
其他天主教國家也很快跟著這麼做了,例如,義大利、波蘭、葡萄牙和西班牙等。

但新教國家不願意追隨。由其是希臘等東正教國家直到 20 世紀初才修改。英國及其殖民地 (包括現在的美國) 在 1752 年執行。
所以,1752 年 9 月 2 日後面跟著 1752 年 9 月 14 日,我們則在辛亥革命後才修改。

換句話說,西方的國家與國家間曆法,在換與未換成 Gregorian 曆之前,其日期需要加減 10 多天,因為舊的 Julian 曆每 1 28 年即多偏差一日。





為何 SQL Server 的 DateTime 日期類型其起始是西元 1753 年,而非西元 1 年 1 月 1 日呢?

相傳是為了不處理西方 Julian 和 Gregorian 兩種曆法的差異。
而 SQL Server 的前身 Sybase 工程師不想實作各國家間的日期轉換,因此選擇英國改制 Gregorian 曆之後的那一年,也就是 1753 年作為開始。

在 SQL Server 2008 提供了數種資料類型,其支援的日期範圍是:

西元 1 年 1 月 1 日到 9999 年 12 月 31 日。
例如:datetime2、date、timedatetimeoffset等。

也可以支援所謂「消失的日期」,也支援搭配函數做日期時間的運算,例如:
1752/09/03 ~ 1752/09/13。
1582/10/05 ~ 1582/10/14。





以下是 PostgreSQL 8.1.18 Documentation 文件的部份摘錄:

SQL 標準聲稱 "在日期時間字面'datetime literal'的定義上, '日期時間(datetime)值' 中的日期和時間根據羅馬曆法受自然法則的約束 "。
在 1752-09-03 和 1752-09-13 之間的日子, 儘管被羅馬教廷的教令取消了,但為了和 "自然法則"相一致,因而是有效的日期。

Note:
The SQL standard states that "Within the definition of a 'datetime literal', the 'datetime value's are constrained by the natural rules for dates and times according to the Gregorian calendar".
Dates between 1752-09-03 and 1752-09-13, although eliminated in some countries by Papal fiat, conform to "natural rules" and are hence valid dates.




以下是 ANSI SQL 和 ISO 8601 標準 文件的部份摘錄:
date 符合西曆的 ANSI SQL 標準定義:「附註 85 - Datetime 資料類型會允許採用西曆格式的日期以 0001–01–01 CE 到 9999–12–31 CE 的日期範圍儲存」。
預設字串常值格式 (用於下層用戶端) 會符合 SQL 標準格式 (定義為 YYYY-MM-DD)。這個格式與 DATE 的 ISO 8601 定義相同。

date complies with the ANSI SQL standard definition for the Gregorian calendar: "NOTE 85 - Datetime data types will allow dates in the Gregorian format to be stored in the date range 0001–01–01 CE through 9999–12–31 CE."

The default string literal format, which is used for down-level clients, complies with the SQL standard form which is defined as YYYY-MM-DD. This format is the same as the ISO 8601 definition for DATE.





以下是 Oracle® Database Concepts 文件的部份摘錄:
10g Release 1 (10.1)
Part Number B10743-01





Use of Julian Dates
Julian dates allow continuous dating by the number of days from a common reference. (The reference is 01-01-4712 years BCE, so current dates are somewhere in the 2.4 million range.) A Julian date is nominally a noninteger, the fractional part being a portion of a day. Oracle uses a simplified approach that results in integer values. Julian dates can be calculated and interpreted differently. The calculation method used by Oracle results in a seven-digit number (for dates most often used), such as 2449086 for 08-APR-93.

Date Arithmetic
Oracle date arithmetic takes into account the anomalies of the calendars used throughout history. For example, the switch from the Julian to the Gregorian calendar, 15-10-1582, eliminated the previous 10 days (05-10-1582 through 14-10-1582). The year 0 does not exist.

You can enter missing dates into the database, but they are ignored in date arithmetic and treated as the next "real" date. For example, the next day after 04-10-1582 is 15-10-1582, and the day following 05-10-1582 is also 15-10-1582.

由上來看 Oracle,使用的是 Julian Dates。




參考文件:

日期 (Transact-SQL)
http://technet.microsoft.com/zh-tw/library/bb630352.aspx

date (Transact-SQL)
http://msdn.microsoft.com/en-us/library/bb630352.aspx

中國時區
維基百科,自由的百科全書
http://zh.wikipedia.org/wiki/%E4%B8%AD%E5%9C%8B%E6%99%82%E5%8D%80#1949.E5.B9.B4.E5.89.8D.E7.9A.84.E6.99.82.E5.8D.80

中央氣象局全球資訊網:台灣的日光節約時及夏令時
http://www.cwb.gov.tw/V6/astronomy/cdata/summert.htm

夏時制
維基百科,自由的百科全書
http://zh.wikipedia.org/zh-tw/%E6%97%A5%E5%85%89%E7%AF%80%E7%B4%84%E6%99%82%E9%96%93

PostgreSQL 日期單位的歷史
http://twpug.net/docs/postgresql-doc-8.0-zh_TW/datetime-units-history.html

PostgreSQL 8.1.18 Documentation
http://www.postgresql.org/docs/8.1/static/datetime-units-history.html

Red Hat Database: SQL Guide and Reference
Date/Time Types
PostgreSQL supports the full set of SQL date and time types.
http://www.redhat.com/docs/manuals/database/RHDB-7.1.3-Manual/sql/datatype-datetime.html

Gregorian calendar
http://en.wikipedia.org/wiki/Gregorian_calendar

http://en.wikipedia.org/wiki/1582

http://en.wikipedia.org/wiki/1752

天文曆法
http://www.geocities.com/calshing/westerncalendar.htm