爱吱声

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

作者: heinsect    时间: 2015-2-1 18:10
标题: 程序员的历法
本帖最后由 heinsect 于 2015-2-1 18:13 编辑
3 q2 L) R1 S! L# ^. m. y' f9 l! L: _) \
程序员计算日期是用儒略日的。  G( k  b  g) K6 n9 e
6 a7 o! G: W, e2 w
儒略日中的儒略和儒略历中的儒略的关系嘛,只是因为儒略日的发明人的爷爷葱白凯撒,给儿子用了大帝的名字。刚好儒略历也用了大帝的名字。
/ ^- W' b' Y# a0 x# ?$ L0 ~" }
# n, \' G- E5 r! a& A& Z单用日期的话,儒略日是从某天开始的一个日的整数。两个儒略日的差值就是相差的天数。这样想计算两个日期间的差值,计算星期几就很简单了。至于那个开始日期,儒略日的零点,用起来的时候谁也不关心,我就不讲了。( F' H' {, a$ z) B

! }  y& r& F9 v从格里高利历日期算儒略日(JDN)的公式是这个样子的:# {# o9 i8 c% \/ M$ F- @

5 F0 |" ]6 |# w" J& a5 I/ N9 o先要改一下年月:
, Z; p0 O$ n  L3 r
$ x" O3 v* ], u+ W" m
( _9 U- y: P, z8 n9 {上面这组公式的结果呢,差不多是这个意思:
/ G) j6 H! U; ?/ |( B6 Y- u三月 m = 0, y=y# O# a7 ?8 @5 M8 h, B
.... |9 ?* c* M% ?* o6 w
十二月 m=9, y=y
$ a, ?8 P/ Z- \7 [7 _  U3 u3 F, z一月 m = 10, y=y-1
% R2 J/ I+ O6 l  Q% C5 h3 ?二月 m = 11, y=y-1& u$ S$ J  p* U, n/ g& N

9 @: S0 q  R, A5 m那个4800,是个计算零点,大概在公元前4801年,是和前面所说的那个零点相关的。
1 M2 W+ e; S1 B5 P) b8 x9 N然后计算儒略日的公式长是这个样子的:
8 F4 m8 G# X* k3 N2 a) v9 T! S8 b/ ?6 H
: F! c( g6 }' x' }0 P4 ~) _
7 P, \1 _# r5 l4 E0 |
这个公式中最巧的部分是 floor((153m+2)/5), 做出的效果嘛,看这个表:
" U& z/ B# ]& }8 ^# H; ?9 nMar–Jul:31 30 31 30 31Aug–Dec:31 30 31 30 31Jan–Feb:31 28; H2 t& P3 f6 l4 H9 n2 \
最后面的那个系数,是相对于原点的修正值。原公式算出来的值一般太大,计算中用起来会超过32/64位字长。现在天文计算中一般会选择2000年1月1日为零点,之前有用1900年和1950年的。
: q. e" Y+ }- O5 U. c# X
) V/ ?. Y8 {; M! v从儒略日计算星期几,(JDN+1) mod 7 就好了。
2 P: U- T( M' j9 U+ a4 Z
% }$ }  W1 c, D6 C; j' R2 n& g这个公式是怎么来的呢? 1582年,教皇格里高利十三(XIII)发现,那一年的春分是3月11日,和儒略历里规定的日期3月21日差了十天。原因嘛,就是回归年的长度是365.2422,儒略历用的365.25。格十三用上了全部的指头,哦,应该是找了很多XX家之后,下令当年10月4日的后一天是10月15日,同时规定在原先四年一闰的基础上,100的整数倍年不是闰年,但400的整数倍年又是闰年。新的历法改名为格里高利历。
* g* q1 q% J3 ^/ Q" j2 ]  {! [9 l
! ]8 F) o& b" l+ f8 b8 ^7 X为了计算转换儒略历和格里高利历,一个法国的教会学者Joseph Justus Scaliger给出了这个公式。“儒略日”中的儒略,是他老爸的名字。9 D& u. E; w5 L/ P+ K
/ B! S/ X' {6 y, ^- k5 K( l" n% l0 F
哦,原来的文献中用的是儒略历日期,要算儒略日是这样的:# I3 D& k& }/ T6 i

0 U; q5 w6 J6 q3 q) a
. d4 I  o( R  ?' n
8 ~4 x: C7 Q4 I+ Q5 \& U# P# g1 X从儒略日转格里高利历,也有一组公式,这里有:6 G% M2 Q$ R) q" j: |7 [# ~. I
6 G8 n3 o  c+ n% l+ u( j$ R
其实这些偏差,在儒略历启用之前是有人知道的。但是,始皇三十五年的某一天,一个罗马士兵在西西里岛上,拔出刀来,朝一个老人身上刺下去。这一刺,西方的科技文明停滞了一千多年。[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
- E. U3 h& ]. G2 S( a2 I, @1 T看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...
' F% w" N4 g( x9 a+ F/ B( `  p
试试计算下一千年每年复活节是哪一天,我又得昏过去了。。。哈哈哈。。。
作者: 橡树村    时间: 2015-2-3 01:49
老兵帅客 发表于 2015-2-2 13:098 e" ~( i0 W4 Y3 [! T7 D5 l* c
看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...

8 C& t3 B8 y  v$ @这个在当年可以用的日期函数还很罕见的时候有用。后来系统本身就提供这些计算了,自然没必要记。: D; N3 k6 o: @% r
7 S6 i7 ?) N1 W1 K) m
我最早见到也是学BASIC的时候。
# m$ x% M2 T5 y; Z9 e2 s0 z. a1 |) F  r2 [" u7 s

作者: 橡树村    时间: 2015-2-3 01:53
本帖最后由 橡树村 于 2015-2-3 01:55 编辑
" w. |6 w% I0 _; D$ y! }
hotmen 发表于 2015-2-2 11:27
# Y# D8 N- m* l' ]' R能换算干支就更好了。

, k( r  L. b. v7 r: R& l& m/ Z; G- |4 }% c3 d# U
计算干支里面的日期不难,时辰是从日期推算的,也不难。, e% u6 e5 x# _* ^4 U
月份是按照年来推算的,说起来简单,难点在于一年以及一个月的开始时间的计算。这个很难有通用公式。不过还是比农历要简单,干支记年实际上是阳历,每年开始于立春,然后每间隔一个节气就换一个月,与农历的月份并不相同。这样只要有了节气的准确时间数据库,干支的问题也就解决了。
/ ^0 b; z& M" V. j: K
9 h0 l  c1 g4 `, n) `1 f农历复杂在于,这个历法经常被改动,要准确把历史上的某一天与西历进行换算,需要把曾经使用过的历法都考虑进去,这个麻烦就大了去了。而且每个月的开始取决于月亮的朔望,这就更要把历史上月亮的运行轨道都考虑进去了。- c8 U+ l% ?" T' y/ J3 [
) q/ _, Y* G" a8 U! J2 W7 ]- }* D

作者: 老兵帅客    时间: 2015-2-3 02:13
橡树村 发表于 2015-2-2 12:49( C8 l! S" P# W: |- E3 t
这个在当年可以用的日期函数还很罕见的时候有用。后来系统本身就提供这些计算了,自然没必要记。
4 o% [& \& M1 u9 o" X0 U3 k. ^( S8 \
我最早 ...

9 Z' Y) V8 l' d/ y3 M& X, p问题是DOS下面的BASIC已经提供日期函数了啊,程序员何必再用这个?
作者: 橡树村    时间: 2015-2-3 02:21
老兵帅客 发表于 2015-2-3 02:13* ~1 h# u  A, j6 E% }( Q
问题是DOS下面的BASIC已经提供日期函数了啊,程序员何必再用这个?

( l$ |8 e+ f9 A) G; ~不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程,最多弄个日历啥的。
作者: 老兵帅客    时间: 2015-2-3 02:29
橡树村 发表于 2015-2-2 13:21: e( M8 C8 a1 L5 {. D; w
不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程 ...
' ], R$ G$ k2 n; b5 g  b$ z) m) x
我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找出当前日期,然后自己计算闰年。计算闰年的算法很简单的,远比楼主的简单。
作者: 老兵帅客    时间: 2015-2-3 02:29
橡树村 发表于 2015-2-2 13:217 ], c: H/ K. ~
不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程 ...
* u- W3 d) q5 @/ ?( {2 O
我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找出当前日期,然后自己计算闰年。计算闰年的算法很简单的,远比楼主的简单。
作者: 橡树村    时间: 2015-2-3 02:42
老兵帅客 发表于 2015-2-3 02:29
4 n8 ]( ]4 L5 e  I+ x我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找 ...

: G" f  v9 U: J! M2 I% KTurbo Pascal?- w+ U/ F8 W6 n% r$ R! S( t

1 D  Y3 f/ U3 Y" t% f最早PC机带的BASIC函数很少的,和Pascal比不了。
作者: 老兵帅客    时间: 2015-2-3 02:44
橡树村 发表于 2015-2-2 13:42
3 B: k7 h( w0 G4 }/ h! qTurbo Pascal?
2 d: S& W) a" X6 b% J% d( Y5 [* D; v; g3 T- ?1 U
最早PC机带的BASIC函数很少的,和Pascal比不了。
; L6 O& j. `( k$ O
不,是标准PASCAL,用的是微软的编译器。我上学的时候还没出turbo pascal呢,后来这东西出来了,拿来一试,发现丫不兼容标准PASCAL嘿,于是再也没碰它。
作者: 橡树村    时间: 2015-2-3 02:52
老兵帅客 发表于 2015-2-3 02:447 A: E4 V/ s& s+ u1 g1 y
不,是标准PASCAL,用的是微软的编译器。我上学的时候还没出turbo pascal呢,后来这东西出来了,拿来一试 ...
4 y) ?! ?* y* ~- G3 F
Turbo pascal 是83年的,那时候我还不知道计算机长啥样呢。3 z* f/ R/ }9 Z/ q

) ?. x) t, E1 Y( e我最早是在Comx35机器上接触的BASIC,84年。
作者: 东湖珞珈    时间: 2015-2-3 04:01
橡树村 发表于 2015-2-3 02:52
) D2 y' k: K' A% h1 l- G. u  @Turbo pascal 是83年的,那时候我还不知道计算机长啥样呢。# h* ?3 r$ l& c! Q: z; {

1 r  T- t4 o% p* e7 I我最早是在Comx35机器上接触的BASIC,84年。 ...

- I2 J! m' x% m' P$ K  G8 r村子老资格啊
作者: shijz    时间: 2015-2-3 07:41
原来在「十万个为什么」第一册上看过这个算法。: S( Z3 C7 x/ L
后来看unix上也是以某一点作起点,以该点到现在的总秒数计算当前日期的。
作者: heinsect    时间: 2015-2-3 10:21
孟词宗 发表于 2015-2-1 20:219 ]: i" f  G* U4 X1 d0 `) o
程序员为啥不直接用格历?
9 k: Y' H/ }( T% T. r- w
用儒略日的公式,无论是从格历到儒略日,还是从儒略日到格历,只有整数运算,没有一个跳转。, X  H' P. e- V% E+ P; T* q. c
跳转在计算机程序的低层优化里是个大问题。2 h* M  x1 |" c0 }4 n, A

作者: heinsect    时间: 2015-2-3 10:21
老兵帅客 发表于 2015-2-2 13:09* L4 ~( P  p2 C5 x; U3 h
看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...

! Q& @" m9 D* A3 ?这个方法是天文计算里常用的。不过俺现在的项目中有一个完整的数据库实现,别人做的,中间要计算日期间隔就用了这个方法。系统函数和类库中算星期、日期间隔、日期加偏移应该也是用了这个方法的。
作者: 懒猫猫    时间: 2015-2-3 12:23
抄袭水风语录:7 v1 O) S5 t( q
假装我看懂了,然后评分
作者: 橡树村    时间: 2015-2-3 13:55
heinsect 发表于 2015-2-3 10:214 C2 A( [3 N. Z+ R; V, D9 M. l
这个方法是天文计算里常用的。不过俺现在的项目中有一个完整的数据库实现,别人做的,中间要计算日期间隔 ...
4 K" N' S4 v2 X$ N, M! p
这个计算方法的出现和规范,扩展,一直是天文领域的事情。计算机出现的年头毕竟还是太短。
作者: 橡树村    时间: 2015-2-3 13:56
shijz 发表于 2015-2-3 07:410 w3 A7 ~  q9 l3 d+ p5 _( H
原来在「十万个为什么」第一册上看过这个算法。, }+ z5 n; U+ e' ^* k& h4 f
后来看unix上也是以某一点作起点,以该点到现在的总秒数计 ...
/ B: m; R: u. P: F( v' R) ~
( Z) M! w+ D5 a$ D  ^- I1 @
Unix的起点是1970年一月一日UTC零时,以秒为单位,不计算闰秒,所以计算机行业可以用。天文反而用不了。
作者: 穿着裤衩裸奔    时间: 2015-2-3 14:23
原来这是钓鱼+年龄暴露贴
作者: 喜欢    时间: 2015-2-26 21:50
提问:那个floor的功能是怎么算的?
作者: heinsect    时间: 2015-2-26 21:58
喜欢 发表于 2015-2-26 21:50
" Q% o, ~% ?& a2 z! u, ]提问:那个floor的功能是怎么算的?

, x3 a8 R/ ^2 j6 Z) ?% V! f小于或等于这个实数的整数中最大的那一个
作者: 喜欢    时间: 2015-2-27 00:26
heinsect 发表于 2015-2-26 08:58% h- h( H2 q9 q$ y. L2 d9 L  b' z* b& m
小于或等于这个实数的整数中最大的那一个

# R; E- a: i) \: k我算出来的结果是:( q5 R$ u& g, i* t8 M9 e5 N7 T2 z, E
1        31
- X9 Q/ Q  ^7 X: c- z2        61
: H7 U1 {, F, Z3        92
  Y) p- g  r* P1 F# M7 b$ _4        1222 y' f7 S6 L5 P1 x7 u" g& N; _
5        153
2 n+ c1 ~! G  }2 z, g4 Z2 ^  R+ _6        184
! i3 d% `1 B/ V  m8 [9 R3 ?1 u' X3 m7        2142 L/ F& [2 D& _& {# G
8        245) B  [* q# P  N* E( K6 s
9        2759 N* D, k4 n; ?1 b  w( a4 L8 k
10        306, [  N4 b. `/ F# \; h0 E
11        337/ c: o8 N. M% y/ b
12        367" b: f1 n, G5 X. s" _9 b& z* \! e$ W

作者: 仁    时间: 2015-2-27 02:45
前人都做好的东西了,我们直接用就好了。我用SAS, 把一个日期就上一个相距的天数就得到了那个日期了。两个日期的差就是相间的天数了。不是所有的计算语言都有这个功能吗?




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