爱吱声

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

作者: heinsect    时间: 2015-2-1 18:10
标题: 程序员的历法
本帖最后由 heinsect 于 2015-2-1 18:13 编辑
6 f. {; ?% }; {2 ^/ t. ]. e9 t* |" b% w( k1 k* f" f" P+ C
程序员计算日期是用儒略日的。1 l( L) F' Q' L

$ f/ _# d9 r0 A  M儒略日中的儒略和儒略历中的儒略的关系嘛,只是因为儒略日的发明人的爷爷葱白凯撒,给儿子用了大帝的名字。刚好儒略历也用了大帝的名字。1 n( @; v* [4 ]) g) E) `
9 B; r' G, i) ?  c
单用日期的话,儒略日是从某天开始的一个日的整数。两个儒略日的差值就是相差的天数。这样想计算两个日期间的差值,计算星期几就很简单了。至于那个开始日期,儒略日的零点,用起来的时候谁也不关心,我就不讲了。
7 w$ m/ {' M. H7 L8 P* {$ A8 G6 @, J' y. g0 \& k, |) s1 N
从格里高利历日期算儒略日(JDN)的公式是这个样子的:: f3 a0 ^2 y4 P3 m( h2 D+ E

/ j3 z" W! z! y- E2 t7 Y( O先要改一下年月:
* m7 A7 f/ p& f, U: j' a5 L, _9 d- C. n& Y% y, f8 q8 p

" ?& ~% o$ k8 Y1 i/ m4 _上面这组公式的结果呢,差不多是这个意思:
' O) u* y! ]# ^% o三月 m = 0, y=y
8 _) v& U; O8 ^...
4 ~! B; Q% N3 @0 y) O; }4 V十二月 m=9, y=y
8 }- \! j8 t9 f8 C* {: n9 F一月 m = 10, y=y-1
) `6 k5 b7 E% ^& Z$ H二月 m = 11, y=y-1
9 H) w- |+ `# v; E, Z
$ P- f1 p6 `1 ]/ ^( K那个4800,是个计算零点,大概在公元前4801年,是和前面所说的那个零点相关的。
6 _3 i3 ]3 S" G/ I; y4 v- t5 [然后计算儒略日的公式长是这个样子的:
5 t$ c1 e) U$ Y4 z+ T& g3 n6 C" a# I3 g9 S7 ?
6 l6 \; t- R  u9 S! d# l$ @
# Y$ }# n1 v2 D% T
这个公式中最巧的部分是 floor((153m+2)/5), 做出的效果嘛,看这个表:
" B  h1 L: \& H; BMar–Jul:31 30 31 30 31Aug–Dec:31 30 31 30 31Jan–Feb:31 289 b) M, M4 e, I0 r- {
最后面的那个系数,是相对于原点的修正值。原公式算出来的值一般太大,计算中用起来会超过32/64位字长。现在天文计算中一般会选择2000年1月1日为零点,之前有用1900年和1950年的。; v5 ?) ?% @) _; }
3 @" B3 n, J. k: F: v+ |* L# h
从儒略日计算星期几,(JDN+1) mod 7 就好了。6 H3 U9 j- P) a: N* V

- M& N3 B8 D: x这个公式是怎么来的呢? 1582年,教皇格里高利十三(XIII)发现,那一年的春分是3月11日,和儒略历里规定的日期3月21日差了十天。原因嘛,就是回归年的长度是365.2422,儒略历用的365.25。格十三用上了全部的指头,哦,应该是找了很多XX家之后,下令当年10月4日的后一天是10月15日,同时规定在原先四年一闰的基础上,100的整数倍年不是闰年,但400的整数倍年又是闰年。新的历法改名为格里高利历。
( A3 p5 h  C4 b! P& M( f' N9 q; r  S
为了计算转换儒略历和格里高利历,一个法国的教会学者Joseph Justus Scaliger给出了这个公式。“儒略日”中的儒略,是他老爸的名字。
/ q9 ^; \* u. ~, j  i& n* N/ Z
. h9 T( T6 u+ A: ^( H7 M9 i+ @哦,原来的文献中用的是儒略历日期,要算儒略日是这样的:
' c6 R% h" j3 Z0 b0 ~# d9 k8 v! a
0 u/ B9 b, N9 p+ j/ k

4 D3 G1 N/ Z2 r' J( r, U( F1 k从儒略日转格里高利历,也有一组公式,这里有:
' e7 y5 w% W4 ^# p: N; R! b- f; p) @
其实这些偏差,在儒略历启用之前是有人知道的。但是,始皇三十五年的某一天,一个罗马士兵在西西里岛上,拔出刀来,朝一个老人身上刺下去。这一刺,西方的科技文明停滞了一千多年。[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:093 H* I1 a9 A& c) Q# D' q) V1 P, t8 U$ H
看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...
& z* ~; a% d! p5 F5 N& y( z
试试计算下一千年每年复活节是哪一天,我又得昏过去了。。。哈哈哈。。。
作者: 橡树村    时间: 2015-2-3 01:49
老兵帅客 发表于 2015-2-2 13:097 d: V8 e8 [, O1 L. ~7 w
看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...
& R8 E3 }7 m/ h% h1 L5 ~
这个在当年可以用的日期函数还很罕见的时候有用。后来系统本身就提供这些计算了,自然没必要记。
% I; i5 |# a# J% d4 X" q
( a% ^" H7 V: A$ T+ ?我最早见到也是学BASIC的时候。
3 C, j6 G" \: H$ ~) c9 R" D: `5 [) |: u4 R, A

作者: 橡树村    时间: 2015-2-3 01:53
本帖最后由 橡树村 于 2015-2-3 01:55 编辑 6 U) j3 W+ Q3 B4 K5 b* e9 e
hotmen 发表于 2015-2-2 11:275 l; k7 U) d5 ?3 ^
能换算干支就更好了。
+ K/ `! m* w# p3 r
8 |  b2 {0 \1 r( a2 \9 w
计算干支里面的日期不难,时辰是从日期推算的,也不难。
' ~2 i: @* d7 Q3 a) U5 S$ n% ~月份是按照年来推算的,说起来简单,难点在于一年以及一个月的开始时间的计算。这个很难有通用公式。不过还是比农历要简单,干支记年实际上是阳历,每年开始于立春,然后每间隔一个节气就换一个月,与农历的月份并不相同。这样只要有了节气的准确时间数据库,干支的问题也就解决了。1 N  }5 N1 S+ z( C- |& p, e
# D, {6 b0 v8 C
农历复杂在于,这个历法经常被改动,要准确把历史上的某一天与西历进行换算,需要把曾经使用过的历法都考虑进去,这个麻烦就大了去了。而且每个月的开始取决于月亮的朔望,这就更要把历史上月亮的运行轨道都考虑进去了。& u* X! k- _& |: D! \

* d$ }+ M$ M/ h
作者: 老兵帅客    时间: 2015-2-3 02:13
橡树村 发表于 2015-2-2 12:496 W8 T3 d* h+ ]9 U
这个在当年可以用的日期函数还很罕见的时候有用。后来系统本身就提供这些计算了,自然没必要记。
& Y; W( U/ u' m) J5 D+ |1 h5 Z) r: m: y3 e+ Y0 e% B
我最早 ...
) s# E/ ~' c6 S( A$ h
问题是DOS下面的BASIC已经提供日期函数了啊,程序员何必再用这个?
作者: 橡树村    时间: 2015-2-3 02:21
老兵帅客 发表于 2015-2-3 02:13+ b" q/ ?3 [) B( N* p. h
问题是DOS下面的BASIC已经提供日期函数了啊,程序员何必再用这个?
4 w3 O7 H+ {2 H/ A1 L* |
不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程,最多弄个日历啥的。
作者: 老兵帅客    时间: 2015-2-3 02:29
橡树村 发表于 2015-2-2 13:21/ O5 p( ~0 A3 q! J. w" e
不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程 ...

  |+ P' n1 I0 Q' a/ N4 }我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找出当前日期,然后自己计算闰年。计算闰年的算法很简单的,远比楼主的简单。
作者: 老兵帅客    时间: 2015-2-3 02:29
橡树村 发表于 2015-2-2 13:21' L0 f9 D5 S* v, S% h5 B6 q
不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程 ...
* p7 @6 ]  e, X) |: v* B2 C
我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找出当前日期,然后自己计算闰年。计算闰年的算法很简单的,远比楼主的简单。
作者: 橡树村    时间: 2015-2-3 02:42
老兵帅客 发表于 2015-2-3 02:29( A0 r/ y  y) u! P' q$ E4 W
我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找 ...

6 h' [+ \. J8 qTurbo Pascal?( q& w) O$ Z; g4 ~' P  v

5 g$ j& s/ i7 v/ R- ?最早PC机带的BASIC函数很少的,和Pascal比不了。
作者: 老兵帅客    时间: 2015-2-3 02:44
橡树村 发表于 2015-2-2 13:42
  E" Q" E0 f5 L, hTurbo Pascal?; A& _! x8 V) Y3 G- @( w7 O
! ~" o9 d3 i( s5 A" K
最早PC机带的BASIC函数很少的,和Pascal比不了。

5 }4 X$ b9 K* n  i$ ]* ~不,是标准PASCAL,用的是微软的编译器。我上学的时候还没出turbo pascal呢,后来这东西出来了,拿来一试,发现丫不兼容标准PASCAL嘿,于是再也没碰它。
作者: 橡树村    时间: 2015-2-3 02:52
老兵帅客 发表于 2015-2-3 02:449 d! g4 K4 ]/ {6 m5 X
不,是标准PASCAL,用的是微软的编译器。我上学的时候还没出turbo pascal呢,后来这东西出来了,拿来一试 ...

" I0 v. a0 c8 X, w- }, V( Z2 ~4 I, aTurbo pascal 是83年的,那时候我还不知道计算机长啥样呢。
  a+ P" x1 d% _6 o$ _
) p. T/ F" y1 R我最早是在Comx35机器上接触的BASIC,84年。
作者: 东湖珞珈    时间: 2015-2-3 04:01
橡树村 发表于 2015-2-3 02:52
9 u: `* L8 K6 m( G4 Y3 i/ _Turbo pascal 是83年的,那时候我还不知道计算机长啥样呢。
5 R" R6 L. B" N' y" u: ~  R- ?  @* A3 M* k% O5 L
我最早是在Comx35机器上接触的BASIC,84年。 ...
1 ?7 |& T6 c, x2 b
村子老资格啊
作者: shijz    时间: 2015-2-3 07:41
原来在「十万个为什么」第一册上看过这个算法。- q" `2 D: R4 P
后来看unix上也是以某一点作起点,以该点到现在的总秒数计算当前日期的。
作者: heinsect    时间: 2015-2-3 10:21
孟词宗 发表于 2015-2-1 20:21
4 D0 g1 }* J. t6 a: O/ w2 ~% F* j程序员为啥不直接用格历?

- M$ C: M1 W% |1 g% Y2 `% C' V5 q用儒略日的公式,无论是从格历到儒略日,还是从儒略日到格历,只有整数运算,没有一个跳转。$ V, C  g+ l8 D# G
跳转在计算机程序的低层优化里是个大问题。
: J5 b0 ~! m0 g
作者: heinsect    时间: 2015-2-3 10:21
老兵帅客 发表于 2015-2-2 13:09
! k8 @* l3 B0 _: [& ^( P看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...
4 d1 W6 `' k. J9 g2 s& R
这个方法是天文计算里常用的。不过俺现在的项目中有一个完整的数据库实现,别人做的,中间要计算日期间隔就用了这个方法。系统函数和类库中算星期、日期间隔、日期加偏移应该也是用了这个方法的。
作者: 懒猫猫    时间: 2015-2-3 12:23
抄袭水风语录:
3 U+ H5 ]( N% f$ D( v5 j假装我看懂了,然后评分
作者: 橡树村    时间: 2015-2-3 13:55
heinsect 发表于 2015-2-3 10:21
6 D* v4 C) s9 E$ E/ G这个方法是天文计算里常用的。不过俺现在的项目中有一个完整的数据库实现,别人做的,中间要计算日期间隔 ...

' Y& h/ S' M' e0 q0 q' y$ D0 q这个计算方法的出现和规范,扩展,一直是天文领域的事情。计算机出现的年头毕竟还是太短。
作者: 橡树村    时间: 2015-2-3 13:56
shijz 发表于 2015-2-3 07:41
( Z# N3 u0 R# k6 m+ W# p原来在「十万个为什么」第一册上看过这个算法。0 u! f( T  N" j
后来看unix上也是以某一点作起点,以该点到现在的总秒数计 ...

" h4 I. V3 I' N2 c' v) l. N  f0 Y' q9 |, B6 C
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! M  ^% K( r5 R; g6 t+ E3 C# f1 y
提问:那个floor的功能是怎么算的?

2 z& P9 G5 T5 b  B, n小于或等于这个实数的整数中最大的那一个
作者: 喜欢    时间: 2015-2-27 00:26
heinsect 发表于 2015-2-26 08:58
. k8 q5 [  L, S- U小于或等于这个实数的整数中最大的那一个

5 T# G$ I  ]4 f: l2 x( |0 }6 h* p8 e6 f5 M我算出来的结果是:7 b! u4 G7 o9 E8 v5 Q
1        31: ?2 h6 B/ Z2 E9 ~% m
2        61
# U4 b8 p* t' l+ F: B5 g% e1 p0 Y" P# F# }3        924 H6 K0 `& w, R2 e
4        122
+ Z5 L# v* @1 o2 ]& j; Q7 ?5        153$ O& ?* i1 Y  g& M) j8 d. X
6        184
( Z6 Y! ?( N2 A$ j7        214
  N9 @3 x& s$ k: g2 P3 _" o8        245' ]) L) W! c' y# O3 i" {
9        275
) `7 C8 l  d0 ]; |  u, [10        306
& t" p5 i0 g: J' y/ f4 Y11        337
; Y4 S. V5 W0 i1 y" F12        367
# ?, j; P, @6 ^5 X" f. p
作者: 仁    时间: 2015-2-27 02:45
前人都做好的东西了,我们直接用就好了。我用SAS, 把一个日期就上一个相距的天数就得到了那个日期了。两个日期的差就是相间的天数了。不是所有的计算语言都有这个功能吗?




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