蔡勒Zeller公式(計算星期的方法) - jx的日誌 - 網易博客
.NET2009-11-11 06:55:29 閱讀104 評論0 字型大小:大中小
sql語句實現:
SELECT(YEAR(GETDATE())%100+FLOOR( YEAR(GETDATE())%100/4)+FLOOR(20/4)-2*20+FLOOR(13*(MONTH(GETDATE())+1)/5)+DAY(GETDATE())-1)%7
SELECT 定單號, 購物者號,a.定單日期, (YEAR(a.定單日期)%100+FLOOR(YEAR(a.定單日期)%100/4 )+FLOOR(20/4)-2*20+FLOOR(13*(MONTH(a.定單日期)+1)/5)+FLOOR(DAY(a.定單日期))-1 )%7+1 AS 星期 FROM dbo.定單 a
蔡勒Zeller公式
Situation:在寫GoteetBlog的Flash版本時候需要繪製Article Table。
繪製希望能以月曆形式呈現。而月曆的日期對其真是頭疼。
Question:以日期計算星期的方法?我在小學玩國際象棋的時候層級記得老師給我介紹過一個公式呃。
Solution1:1
Weekday=Year+(int)(Year/4)+(int)(Century/4)2
-2*Century+(int)(26*(Month+1)/10)+Day-1;3
Weekday<0?(Weekday+=7):(Weekday%=7);
公式中的符號含義如下:
(Century 是世紀數減一,Year 是年份後兩位,Month是月份,Day是日數。1月和2月要按上一年的13月和 14月來算,這時Century 和Year 均按上一年取值。) 算出來的Weekday除以7,餘數是幾就是星期幾
Example1:以2008年8月1日
Weekday=Year+(int)(Year/4)+(int)(Century/4)
=08+[08/4]+[20/4]-2×20+[26×(8+1)/10]+1-1 =8+2+5-40+[23.4] =-25+23=-2(除以7餘5)
Weekday = Weekday + 7
= -2 + 7
= 5 即2008年8月1日是星期5。
Solution2:1ThisMonth=newDate();2
2ThisMonth.setDate(1);3
3FirstDay=ThisMonth.getDay();4
4//..:o"(.>_<.)o::.計算星期可用蔡勒(Zeller)公式(只適合於1582年10月15日之後的情形): W = Y +[Y/4] + [C/4] - 2C + [13(M+1)/5] + D - 1 公式中的符號含義如下:W:星期;W對7取模得:0-星期日,1-星期一,2-星期二,3-星期三,4-星期四, 5-星期五,6-星期六 C:世紀數減一(年的高兩位數); Y:年(年的低兩位數); M:月(M大於等於3,小於等於14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月來計算,比如2005年1月1日要看作2004年的13月1日來計算); D:日; []代表取整,即只要整數部分。 注意負數不能按習慣的餘數的概念求餘數,只能按數論中的餘數的定義求余。為了方便計算,我們可以給它加上一個7的整數倍,使它變為一個正數。 以2005年2月14日為例:C=20,Y=4,M=14,D=14 W=4+[4/4]+[20/4]-2*20+[26*(14+1)/10]+14-1 =4+1+5-40+39+14-1 =22 (除以7餘1) 所以2005年2月14日是星期一。
蔡勒(Zeller)公式的推導過程
星期制度是一種有古老傳統的制度。據說因為《聖經·創世紀》中規定上帝用了六天時間創世紀,第七天休息,所以人們也就以七天為一個周期來安排自己的工作和生活,而星期日是休息日。從實際的角度來講,以七天為一個周期,長短也比較合適。所以儘管中國的傳統工作周期是十天(比如王勃《滕王閣序》中說的「十旬休暇」,即是指官員的工作每十日為一個周期,第十日休假),但後來也採取了西方的星期制度。 在日常生活中,我們常常遇到要知道某一天是星期幾的問題。有時候,我們還想知道歷史上某一天是星期幾。通常,解決這個方法的有效辦法是看日曆,但是我們總不會隨時隨身帶著日曆,更不可能隨時隨身帶著幾千年的萬年曆。假如是想在計算機編程中計算某一天是星期幾,預先把一本萬年曆存進去就更不現實了。這時候是不是有辦法通過什麼公式,從年月日推出這一天是星期幾呢?答案是肯定的。其實我們也常常在這樣做。我們先舉一個簡單的例子。比如,知道了2004年5月1日是星期六,那麼2004年5月31日「世界無煙日」是星期幾就不難推算出來。我們可以掰著指頭從1日數到31日,同時數星期,最後可以數出5月31日是星期一。其實運用數學計算,可以不用掰指頭。我們知道星期是七天一輪迴的,所以5月1日是星期六,七天之後的5月8日也是星期六。在日期上,8-1=7,正是7的倍數。同樣,5月15日、5月22日和5月29日也是星期六,它們的日期和5月1日的差值分別是14、21和28,也都是7的倍數。那麼5月31日呢?31-1=30,雖然不是7的倍數,但是31除以7,餘數為2,這就是說,5月31日的星期,是在5月1日的星期之後兩天。星期六之後兩天正是星期一。 這個簡單的計算告訴我們計算星期的一個基本思路:首先,先要知道在想算的日子之前的一個確定的日子是星期幾,拿這一天做為推算的標準,也就是相當於一個計算的「原點」。其次,知道想算的日子和這個確定的日子之間相差多少天,用7除這個日期的差值,餘數就表示想算的日子的星期在確定的日子的星期之後多少天。如果餘數是0,就表示這兩天的星期相同。顯然,如果把這個作為「原點」的日子選為星期日,那麼餘數正好就等於星期幾,這樣計算就更方便了。 但是直接計算兩天之間的天數,還是不免繁瑣。比如1982年7月29日和2004年5月1日之間相隔7947天,就不是一下子能算出來的。它包括三段時間:一,1982年7月29日以後這一年的剩餘天數;二,1983-2003這二十一個整年的全部天數;三,從2004年元旦到5月1日經過的天數。第二段比較好算,它等於21*365+5=7670天,之所以要加5,是因為這段時間內有5個閏年。第一段和第三段就比較麻煩了,比如第三段,需要把5月之前的四個月的天數累加起來,再加上日期值,即31+29+31+30+1=122天。同理,第一段需要把7月之後的五個月的天數累加起來,再加上7月剩下的天數,一共是155天。所以總共的相隔天數是122+7670+155=7947天。 仔細想想,如果把「原點」日子的日期選為12月31日,那麼第一段時間也就是一個整年,這樣一來,第一段時間和第二段時間就可以合併計算,整年的總數正好相當於兩個日子的年份差值減一。如果進一步把「原點」日子選為公元前1年12月31日(或者天文學家所使用的公元0年12月31日),這個整年的總數就正好是想算的日子的年份減一。這樣簡化之後,就只須計算兩段時間:一,這麼多整年的總天數;二,想算的日子是這一年的第幾天。巧的是,按照公曆的年月設置,這樣反推回去,公元前1年12月31日正好是星期日,也就是說,這樣算出來的總天數除以7的餘數正好是星期幾。那麼現在的問題就只有一個:這麼多整年裡面有多少閏年。這就需要了解公曆的置閏規則了。 我們知道,公曆的平年是365天,閏年是366天。置閏的方法是能被4整除的年份在2月加一天,但能被100整除的不閏,能被400整除的又閏。因此,像1600、2000、2400年都是閏年,而1700、1800、1900、2100年都是平年。公元前1年,按公曆也是閏年。 因此,對於從公元前1年(或公元0年)12月31日到某一日子的年份Y之間的所有整年中的閏年數,就等於[(Y-1)/4]-[(Y-1)/100]+[(Y-1)/400],[...]表示只取整數部分。第一項表示需要加上被4整除的年份數,第二項表示需要去掉被100整除的年份數,第三項表示需要再加上被400整除的年份數。之所以Y要減一,這樣,我們就得到了第一個計算某一天是星期幾的公式:W=(Y-1)*365+[(Y-1)/4]-[(Y-1)/100]+[(Y-1)/400]+D.(1)其中D是這個日子在這一年中的累積天數。算出來的W就是公元前1年(或公元0年)12月31日到這一天之間的間隔日數。把W用7除,餘數是幾,這一天就是星期幾。比如我們來算2004年5月1日:W=(2004-1)*365+[(2004-1)/4]-[(2004-1)/100]+[(2004-1)/400]+(31+29+31+30+1)=731702,731702/7=104528……6,餘數為六,說明這一天是星期六。這和事實是符合的。 上面的公式(1)雖然很準確,但是計算出來的數字太大了,使用起來很不方便。仔細想想,其實這個間隔天數W的用數僅僅是為了得到它除以7之後的餘數。這啟發我們是不是可以簡化這個W值,只要找一個和它餘數相同的較小的數來代替,用數論上的術語來說,就是找一個和它同餘的較小的正整數,照樣可以計算出準確的星期數。 顯然,W這麼大的原因是因為公式中的第一項(Y-1)*365太大了。其實,(Y-1)*365=(Y-1)*(364+1)=(Y-1)*(7*52+1)=52*(Y-1)*7+(Y-1),這個結果的第一項是一個7的倍數,除以7餘數為0,因此(Y-1)*365除以7的餘數其實就等於Y-1除以7的餘數。這個關係可以表示為:(Y-1)*365≡Y-1(mod7).其中,≡是數論中表示同餘的符號,mod7的意思是指在用7作模數(也就是除數)的情況下≡號兩邊的數是同餘的。因此,完全可以用(Y-1)代替(Y-1)*365,這樣我們就得到了那個著名的、也是最常見到的計算星期幾的公式:W=(Y-1)+[(Y-1)/4]-[(Y-1)/100]+[(Y-1)/400]+D.(2) 這個公式雖然好用多了,但還不是最好用的公式,因為累積天數D的計算也比較麻煩。是不是可以用月份數和日期直接計算呢?答案也是肯定的。我們不妨來觀察一下各個月的日數,列表如下:月 份:1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月--------------------------------------------------------------------------天數:3128(29)31303130313130313031如果把這個天數都減去28(=4*7),不影響W除以7的餘數值。這樣我們就得到另一張表:月 份:1月 2月 3月 4月5月 6月 7月 8月 9月 10月 11月 12月------------------------------------------------------------------------剩餘天數:30(1)3232332323平年累積:33681113161921242629閏年累積:34791214172022252730仔細觀察的話,我們會發現除去1月和2月,3月到7月這五個月的剩餘天數值是3,2,3,2,3;8月到12月這五個月的天數值也是3,2,3,2,3,正好是一個重複。相應的累積天數中,後一月的累積天數和前一月的累積天數之差減去28就是這個重複。正是因為這種規律的存在,平年和閏年的累積天數可以用數學公式很方便地表達:╭d; (當M=1)D={31+d; (當M=2) (3)╰[13*(M+1)/5]-7+(M-1)*28+d+i. (當M≥3)其中[...]仍表示只取整數部分;M和d分別是想算的日子的月份和日數;平年i=0,閏年i=1。對於M≥3的表達式需要說明一下:[13*(M+1)/5]-7算出來的就是上面第二個表中的平年累積值,再加上(M-1)*28就是想算的日子的月份之前的所有月份的總天數。這是一個很巧妙的辦法,利用取整運算來實現3,2,3,2,3的循環。比如,對2004年5月1日,有:D=[13*(5+1)/5]-7+(5-1)*28+1+1=122,這正是5月1日在2004年的累積天數。 假如,我們再變通一下,把1月和2月當成是上一年的「13月」和「14月」,不僅仍然符合這個公式,而且因為這樣一來,閏日成了上一「年」(一共有14個月)的最後一天,成了d的一部分,於是平閏年的影響也去掉了,公式就簡化成:D=[13*(M+1)/5]-7+(M-1)*28+d.(3≤M≤14)(4)上面計算星期幾的公式,也就可以進一步簡化成:W=(Y-1)+[(Y-1)/4]-[(Y-1)/100]+[(Y-1)/400]+[13*(M+1)/5]-7+(M-1)*28+d.因為其中的-7和(M-1)*28兩項都可以被7整除,所以去掉這兩項,W除以7的餘數不變,公式變成:W=(Y-1)+[(Y-1)/4]-[(Y-1)/100]+[(Y-1)/400]+[13*(M+1)/5]+d.(5)當然,要注意1月和2月已經被當成了上一年的13月和14月,因此在計算1月和2月的日子的星期時,除了M要按13或14算,年份Y也要減一。比如,2004年1月1日是星期四,用這個公式來算,有:W=(2003-1)+[(2003-1)/4]-[(2003-1)/100]+[(2003-1)/400]+[13*(13+1)/5]+1=2002+500-20+5+36+1=2524;2524/7=360……4.這和實際是一致的。 公式(5)已經是從年、月、日來算星期幾的公式了,但它還不是最簡練的,對於年份的處理還有改進的方法。我們先來用這個公式算出每個世紀第一年3月1日的星期,列表如下:年份:1(401,801,…,2001)101(501,901,…,2101)--------------------------------------------------------------------星期: 42====================================================================年份:201(601,1001,…,2201)301(701,1101,…,2301)--------------------------------------------------------------------星期:05可以看出,每隔四個世紀,這個星期就重複一次。假如我們把301(701,1101,…,2301)年3月1日的星期數看成是-2(按數論中對餘數的定義,-2和5除以7的餘數相同,所以可以做這樣的變換),那麼這個重複序列正好就是一個4,2,0,-2的等差數列。據此,我們可以得到下面的計算每個世紀第一年3月1日的星期的公式:W=(4-Cmod4)*2-4.(6)式中,C是該世紀的世紀數減一,mod表示取模運算,即求餘數。比如,對於2001年3月1日,C=20,則:W=(4-20mod4)*2-4=8-4=4.把公式(6)代入公式(5),經過變換,可得:(Y-1)+[(Y-1)/4]-[(Y-1)/100]+[(Y-1)/400]≡(4-Cmod4)*2-1(mod7).(7)因此,公式(5)中的(Y-1)+[(Y-1)/4]-[(Y-1)/100]+[(Y-1)/400]這四項,在計算每個世紀第一年的日期的星期時,可以用(4-Cmod4)*2-1來代替。這個公式寫出來就是:W=(4-Cmod4)*2-1+[13*(M+1)/5]+d.(8)有了計算每個世紀第一年的日期星期的公式,計算這個世紀其他各年的日期星期的公式就很容易得到了。因為在一個世紀里,末尾為00的年份是最後一年,因此就用不著再考慮「一百年不閏,四百年又閏」的規則,只須考慮「四年一閏」的規則。仿照由公式(1)簡化為公式(2)的方法,我們很容易就可以從式(8)得到一個比公式(5)更簡單的計算任意一天是星期幾的公式:W=(4-Cmod4)*2-1+(y-1)+[y/4]+[13*(M+1)/5]+d.(9)式中,y是年份的後兩位數字。如果再考慮到取模運算不是四則運算,我們還可以把(4-Cmod4)*2進一步改寫成只含四則運算的表達式。因為世紀數減一C除以4的商數q和餘數r之間有如下關係:4q+r=C,其中r即是Cmod4,因此,有:r=C-4q=C-4*[C/4].(10)則(4-Cmod4)*2=(4-C+4*[C/4])*2=8-2C+8*[C/4]≡[C/4]-2C+1(mod7).(11)把式(11)代入(9),得到:W=[C/4]-2C+y+[y/4]+[13*(M+1)/5]+d-1.(12)這個公式由世紀數減一、年份末兩位、月份和日數即可算出W,再除以7,得到的餘數是幾就表示這一天是星期幾,唯一需要變通的是要把1月和2月當成上一年的13月和14月,C和y都按上一年的年份取值。因此,人們普遍認為這是計算任意一天是星期幾的最好的公式。這個公式最早是由德國數學家克里斯蒂安·蔡勒(ChristianZeller,1822-1899)在1886年推導出的,因此通稱為蔡勒公式(Zeller』sFormula)。為方便口算,式中的[13*(M+1)/5]也往往寫成[26*(M+1)/10]。 現在仍然讓我們來算2004年5月1日的星期,顯然C=20,y=4,M=5,d=1,代入蔡勒公式,有:W=[20/4]-40+4+1+[13*(5+1)/5]+1-1=-15.注意負數不能按習慣的餘數的概念求餘數,只能按數論中的餘數的定義求余。為了方便計算,我們可以給它加上一個7的整數倍,使它變為一個正數,比如加上70,得到55。再除以7,餘6,說明這一天是星期六。這和實際是一致的,也和公式(2)計算所得的結果一致。最後需要說明的是,上面的公式都是基於公曆(格里高利曆)的置閏規則來考慮的。對於儒略曆,蔡勒也推出了相應的公式是:W=5-C+y+[y/4]+[13*(M+1)/5]+d-1.(13)
推薦閱讀:
※【Miuo】配色公式:紅藍白,穿出質感和經典
※你會寫 多表求和 公式嗎?
※干支轉換EXCEL公式
※蔡勒公式
※使用excel 數組公式的注意事項