爱吱声

标题: 程序员的历法 [打印本页]

作者: heinsect    时间: 2015-2-1 18:10
标题: 程序员的历法
本帖最后由 heinsect 于 2015-2-1 18:13 编辑
4 \1 [# K+ Z; {0 {
" F1 K. O+ }3 T程序员计算日期是用儒略日的。
" B% V: q$ d6 v  y4 }9 e
- B$ p. e, j! B1 c  b) q5 C! J儒略日中的儒略和儒略历中的儒略的关系嘛,只是因为儒略日的发明人的爷爷葱白凯撒,给儿子用了大帝的名字。刚好儒略历也用了大帝的名字。
8 w8 e8 ^* J9 W  x2 x9 N
3 Q' t/ ^) u2 g7 x+ x单用日期的话,儒略日是从某天开始的一个日的整数。两个儒略日的差值就是相差的天数。这样想计算两个日期间的差值,计算星期几就很简单了。至于那个开始日期,儒略日的零点,用起来的时候谁也不关心,我就不讲了。
- N& n( K# E8 C7 }3 O1 X) R/ X& t. Z. R* K! U! @6 S
从格里高利历日期算儒略日(JDN)的公式是这个样子的:$ x* R  L2 w: I  t2 k, [

0 ]& }4 T- y# t先要改一下年月:0 U' Y$ B3 d# @9 Y& S2 R1 o  b& x2 c
# j) O  `+ S" ?) B

+ ?1 r: ?+ k3 s2 c上面这组公式的结果呢,差不多是这个意思:, ~' }, N( I5 H6 W" F5 G
三月 m = 0, y=y
' a% x  z- l$ w. e# l, q/ k...
/ K6 A3 j* l  V0 E; Y; A十二月 m=9, y=y
" ^  m9 p4 Y* q- B# E6 I; ^0 U一月 m = 10, y=y-1
  E7 l$ C1 a8 l+ ?! _( T二月 m = 11, y=y-19 ]8 u) k5 F  t+ G7 D

" E1 Y% A  I1 A6 N- o1 R那个4800,是个计算零点,大概在公元前4801年,是和前面所说的那个零点相关的。4 B5 {- ]4 n0 L' z
然后计算儒略日的公式长是这个样子的:) U( }3 u; n9 Y6 w: V
+ \, n0 p, P' K* i3 ~" \

2 V% l3 F5 Q# d. r, o7 i% f( \" p: [3 Z' B
这个公式中最巧的部分是 floor((153m+2)/5), 做出的效果嘛,看这个表:- V- q+ w& |" p6 e
Mar–Jul:31 30 31 30 31Aug–Dec:31 30 31 30 31Jan–Feb:31 28
1 [8 Q4 J/ Y0 P, m) a最后面的那个系数,是相对于原点的修正值。原公式算出来的值一般太大,计算中用起来会超过32/64位字长。现在天文计算中一般会选择2000年1月1日为零点,之前有用1900年和1950年的。& a) x& {9 y9 t

8 @# `2 j, c$ G3 L4 |+ M从儒略日计算星期几,(JDN+1) mod 7 就好了。2 j+ t1 P9 `, b  V+ W6 J, Q: g( F$ K

4 a" A' J- M/ l5 ?这个公式是怎么来的呢? 1582年,教皇格里高利十三(XIII)发现,那一年的春分是3月11日,和儒略历里规定的日期3月21日差了十天。原因嘛,就是回归年的长度是365.2422,儒略历用的365.25。格十三用上了全部的指头,哦,应该是找了很多XX家之后,下令当年10月4日的后一天是10月15日,同时规定在原先四年一闰的基础上,100的整数倍年不是闰年,但400的整数倍年又是闰年。新的历法改名为格里高利历。9 J" O8 ~$ A7 Y% w
, v. \# h* c& c
为了计算转换儒略历和格里高利历,一个法国的教会学者Joseph Justus Scaliger给出了这个公式。“儒略日”中的儒略,是他老爸的名字。
% @- }( S' X- ]
6 ~/ d/ S  r0 y8 m# _' ~哦,原来的文献中用的是儒略历日期,要算儒略日是这样的:
* v& y, p# c$ V) m( |( \4 R- ^) e/ Q/ r+ s7 h# h

6 U0 j5 m, z/ F, E5 |8 E. T0 c0 Q( r7 ]$ {
从儒略日转格里高利历,也有一组公式,这里有:
( z, r8 X$ M: i: P7 }! f7 y- t) a  @* u4 ]0 Q1 R) D
其实这些偏差,在儒略历启用之前是有人知道的。但是,始皇三十五年的某一天,一个罗马士兵在西西里岛上,拔出刀来,朝一个老人身上刺下去。这一刺,西方的科技文明停滞了一千多年。[groupid=155]软件人家[/groupid]
作者: 穿着裤衩裸奔    时间: 2015-2-1 19:18
不明觉厉,捞分走人
作者: 孟词宗    时间: 2015-2-1 20:21
程序员为啥不直接用格历?
作者: 龙血树    时间: 2015-2-2 01:20
蛮夷的国家成了黑社会渊薮,大科学家的故乡开始赖账
作者: 方恨少    时间: 2015-2-2 03:00
不明觉厉
作者: 东湖珞珈    时间: 2015-2-2 07:17
N多年前学习BASIC语言的时候,就是用这个公式做核心计算,然后再加上几重循环控制的排版,打印一个当年的日历出来。
作者: 水风    时间: 2015-2-2 09:53
假装我看懂了,然后评分
作者: hotmen    时间: 2015-2-2 11:27
能换算干支就更好了。
作者: 老兵帅客    时间: 2015-2-2 13:09
看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多只需要计算某年是否是闰年就足够了。
作者: 夏翁    时间: 2015-2-2 13:52
老兵帅客 发表于 2015-2-2 13:09$ s: ^" Q; Z) N/ G
看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...

# f+ x0 @; |( h* I$ w试试计算下一千年每年复活节是哪一天,我又得昏过去了。。。哈哈哈。。。
作者: 橡树村    时间: 2015-2-3 01:49
老兵帅客 发表于 2015-2-2 13:09
' n! G9 [" _( f: y看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...
3 o9 }4 J2 a9 B3 P6 l+ s3 P
这个在当年可以用的日期函数还很罕见的时候有用。后来系统本身就提供这些计算了,自然没必要记。
6 l2 @6 ^5 d+ s2 D. S. d$ s4 d' C' f- V5 W: K7 p! D, C: }: |, F' x
我最早见到也是学BASIC的时候。. X- r9 b5 r" z: X; [" p/ s. ?
; y+ M! a1 \3 E8 Z. l

作者: 橡树村    时间: 2015-2-3 01:53
本帖最后由 橡树村 于 2015-2-3 01:55 编辑
* N+ |& e$ e. x$ l1 x
hotmen 发表于 2015-2-2 11:27
1 j/ _# B- s4 q能换算干支就更好了。
* p) y! I* v" J+ P

1 s- X3 Y; _7 L! Z: q计算干支里面的日期不难,时辰是从日期推算的,也不难。% D  v% S2 \" U! j4 e
月份是按照年来推算的,说起来简单,难点在于一年以及一个月的开始时间的计算。这个很难有通用公式。不过还是比农历要简单,干支记年实际上是阳历,每年开始于立春,然后每间隔一个节气就换一个月,与农历的月份并不相同。这样只要有了节气的准确时间数据库,干支的问题也就解决了。
" ~9 F% i9 z! I( k/ A+ \) W
" v: A& J8 w0 A, N3 S农历复杂在于,这个历法经常被改动,要准确把历史上的某一天与西历进行换算,需要把曾经使用过的历法都考虑进去,这个麻烦就大了去了。而且每个月的开始取决于月亮的朔望,这就更要把历史上月亮的运行轨道都考虑进去了。: @' P$ l4 D$ \- M2 G
+ K. P# R* w( b% ~% A

作者: 老兵帅客    时间: 2015-2-3 02:13
橡树村 发表于 2015-2-2 12:49
9 f" ~5 Q4 V# ]. F' ^2 t* w这个在当年可以用的日期函数还很罕见的时候有用。后来系统本身就提供这些计算了,自然没必要记。0 I. S" M) U, g6 V) s& y4 u

" o& D: D/ ?" r) s3 M我最早 ...

  k- D4 a/ _" K4 u* m问题是DOS下面的BASIC已经提供日期函数了啊,程序员何必再用这个?
作者: 橡树村    时间: 2015-2-3 02:21
老兵帅客 发表于 2015-2-3 02:137 r% j2 w' y. n7 q6 H
问题是DOS下面的BASIC已经提供日期函数了啊,程序员何必再用这个?
4 \2 S" Q8 y  @& E
不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程,最多弄个日历啥的。
作者: 老兵帅客    时间: 2015-2-3 02:29
橡树村 发表于 2015-2-2 13:21
3 D& F- i6 |2 b不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程 ...
# s" ]4 E3 V* i  H
我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找出当前日期,然后自己计算闰年。计算闰年的算法很简单的,远比楼主的简单。
作者: 老兵帅客    时间: 2015-2-3 02:29
橡树村 发表于 2015-2-2 13:211 `# h( o" w  W3 f7 e
不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程 ...
" @3 f( ^  l0 N/ @6 U% y0 f8 M1 E
我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找出当前日期,然后自己计算闰年。计算闰年的算法很简单的,远比楼主的简单。
作者: 橡树村    时间: 2015-2-3 02:42
老兵帅客 发表于 2015-2-3 02:29
0 _  I+ I3 y- W4 t0 b9 ]我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找 ...
0 z1 }0 }  n# u
Turbo Pascal?$ I) x, s9 Z: c# F0 e/ v

4 S1 I2 i$ k$ O  x- O5 n' D最早PC机带的BASIC函数很少的,和Pascal比不了。
作者: 老兵帅客    时间: 2015-2-3 02:44
橡树村 发表于 2015-2-2 13:42
% E! Z/ H( d* d" gTurbo Pascal?+ P$ Q7 F, e7 G1 m2 K
+ y1 z5 y; p9 S% v, T
最早PC机带的BASIC函数很少的,和Pascal比不了。
2 w( [( [1 w5 g, e& T3 y; y
不,是标准PASCAL,用的是微软的编译器。我上学的时候还没出turbo pascal呢,后来这东西出来了,拿来一试,发现丫不兼容标准PASCAL嘿,于是再也没碰它。
作者: 橡树村    时间: 2015-2-3 02:52
老兵帅客 发表于 2015-2-3 02:44
1 y. O' S/ f6 S不,是标准PASCAL,用的是微软的编译器。我上学的时候还没出turbo pascal呢,后来这东西出来了,拿来一试 ...
5 a" E" q7 K" C8 E# u9 X4 Y
Turbo pascal 是83年的,那时候我还不知道计算机长啥样呢。
1 b2 ?, G( A+ u" m+ h; m4 C% s6 U) g( r( Z' I, X3 _  ]: v  D
我最早是在Comx35机器上接触的BASIC,84年。
作者: 东湖珞珈    时间: 2015-2-3 04:01
橡树村 发表于 2015-2-3 02:52
- L+ G" O! z/ ]4 E: s& dTurbo pascal 是83年的,那时候我还不知道计算机长啥样呢。% f+ q' i& A+ A" i" @$ g

- H# D5 K  T2 l& V我最早是在Comx35机器上接触的BASIC,84年。 ...
4 o& L# N% z! u8 O( k  ^
村子老资格啊
作者: shijz    时间: 2015-2-3 07:41
原来在「十万个为什么」第一册上看过这个算法。! R: Y& q4 J+ b, v" p
后来看unix上也是以某一点作起点,以该点到现在的总秒数计算当前日期的。
作者: heinsect    时间: 2015-2-3 10:21
孟词宗 发表于 2015-2-1 20:21
2 V6 `, ]* |4 M/ U程序员为啥不直接用格历?

: V8 a2 \" \6 J- N; i* {2 ~用儒略日的公式,无论是从格历到儒略日,还是从儒略日到格历,只有整数运算,没有一个跳转。# B% k1 j$ A5 [1 `' Q; N
跳转在计算机程序的低层优化里是个大问题。+ V2 G% a* i  _; I

作者: heinsect    时间: 2015-2-3 10:21
老兵帅客 发表于 2015-2-2 13:09
0 F% c! J- s) R: j2 L2 L看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...

: o( s2 M6 a+ S# H这个方法是天文计算里常用的。不过俺现在的项目中有一个完整的数据库实现,别人做的,中间要计算日期间隔就用了这个方法。系统函数和类库中算星期、日期间隔、日期加偏移应该也是用了这个方法的。
作者: 懒猫猫    时间: 2015-2-3 12:23
抄袭水风语录:
1 O: P. Q9 b% _+ b7 n/ Z; S假装我看懂了,然后评分
作者: 橡树村    时间: 2015-2-3 13:55
heinsect 发表于 2015-2-3 10:21% Q) S# D8 u! n9 ~  B
这个方法是天文计算里常用的。不过俺现在的项目中有一个完整的数据库实现,别人做的,中间要计算日期间隔 ...
4 P7 {: B) ~/ o8 M" q1 v
这个计算方法的出现和规范,扩展,一直是天文领域的事情。计算机出现的年头毕竟还是太短。
作者: 橡树村    时间: 2015-2-3 13:56
shijz 发表于 2015-2-3 07:417 O  X1 W) `" I! v% |
原来在「十万个为什么」第一册上看过这个算法。8 F! ]1 k. Y! l% ]
后来看unix上也是以某一点作起点,以该点到现在的总秒数计 ...
: q8 w, F" F" E  w/ B

, z  i" A7 S* OUnix的起点是1970年一月一日UTC零时,以秒为单位,不计算闰秒,所以计算机行业可以用。天文反而用不了。
作者: 穿着裤衩裸奔    时间: 2015-2-3 14:23
原来这是钓鱼+年龄暴露贴
作者: 喜欢    时间: 2015-2-26 21:50
提问:那个floor的功能是怎么算的?
作者: heinsect    时间: 2015-2-26 21:58
喜欢 发表于 2015-2-26 21:500 e% g8 D$ m2 G7 V
提问:那个floor的功能是怎么算的?

8 L9 C/ e7 {; C$ {; y2 q  e小于或等于这个实数的整数中最大的那一个
作者: 喜欢    时间: 2015-2-27 00:26
heinsect 发表于 2015-2-26 08:581 c1 l$ q' X+ ?* F
小于或等于这个实数的整数中最大的那一个

! r2 C/ I. U3 }# f) `我算出来的结果是:+ A# w; P3 ^8 _7 L" i. B
1        31
' J* Q' s0 O0 z* Z' D( Y2        61
% ?% e- _, \( m( w0 H/ U, ~3        92
; s2 y. b: z/ q, r  `1 u4        1220 ~# L9 Z) p; V) _% M
5        153" X" E3 W% V7 \# V  f/ \
6        184
' x0 E# r9 k7 c' o& a7        2147 L& a: O* H$ m" @6 P
8        245
8 N4 i" ~3 Q+ H4 O/ ?9        275
' \4 \: C9 M5 n6 n0 d8 [10        306. x8 e+ e2 h" [. }% N
11        337
! \  n2 g% ~0 @' Z# k7 m12        367
7 K. g! z# T7 l0 n( g
作者: 仁    时间: 2015-2-27 02:45
前人都做好的东西了,我们直接用就好了。我用SAS, 把一个日期就上一个相距的天数就得到了那个日期了。两个日期的差就是相间的天数了。不是所有的计算语言都有这个功能吗?




欢迎光临 爱吱声 (http://www.aswetalk.net/bbs/) Powered by Discuz! X3.2