爱吱声

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

作者: heinsect    时间: 2015-2-1 18:10
标题: 程序员的历法
本帖最后由 heinsect 于 2015-2-1 18:13 编辑
  G4 W" R6 Y% V6 Z4 L: W) _" r9 S! U2 N
程序员计算日期是用儒略日的。% V6 [* C0 w4 @. c. i" S
# q5 ~8 K! ^$ U' y) L
儒略日中的儒略和儒略历中的儒略的关系嘛,只是因为儒略日的发明人的爷爷葱白凯撒,给儿子用了大帝的名字。刚好儒略历也用了大帝的名字。
4 x; g/ e/ s2 x) W
2 S, Z6 G. W& u. E7 o单用日期的话,儒略日是从某天开始的一个日的整数。两个儒略日的差值就是相差的天数。这样想计算两个日期间的差值,计算星期几就很简单了。至于那个开始日期,儒略日的零点,用起来的时候谁也不关心,我就不讲了。+ a4 h1 C' i6 W0 w
( e% v# r* T2 a3 c: Z
从格里高利历日期算儒略日(JDN)的公式是这个样子的:3 t4 O, t( }5 V3 n* h# a* K

( T- @6 i- C9 o$ {先要改一下年月:# \* e$ Y* z; s* E
# P0 p! e1 u8 i5 N& P

/ H- f" L( b& c上面这组公式的结果呢,差不多是这个意思:( j1 u7 c. U* l' Z8 H! _
三月 m = 0, y=y! O/ Q0 V* b9 O$ |1 \
...* |; K5 n8 @. m. z0 ]6 X
十二月 m=9, y=y) h/ I3 m3 O4 h6 z- G3 L) k
一月 m = 10, y=y-17 z! Q! {8 Y4 e# J+ ]9 ?
二月 m = 11, y=y-1
+ |0 Q, f$ x1 E  |
. q! w5 _& v9 ]那个4800,是个计算零点,大概在公元前4801年,是和前面所说的那个零点相关的。
/ o, w- [( d5 _5 G: B7 t+ z/ G然后计算儒略日的公式长是这个样子的:' G% P* I0 r8 O0 t. E- C& N! w

. b, P$ o" P/ U( j+ P" W: i( _/ \4 R# g* H2 k
* k/ B( T- c7 P1 E/ b* }% b
这个公式中最巧的部分是 floor((153m+2)/5), 做出的效果嘛,看这个表:
* V/ t  K2 q3 o- B" N9 C/ eMar–Jul:31 30 31 30 31Aug–Dec:31 30 31 30 31Jan–Feb:31 28" F& J' j+ {0 ]# f6 o$ e
最后面的那个系数,是相对于原点的修正值。原公式算出来的值一般太大,计算中用起来会超过32/64位字长。现在天文计算中一般会选择2000年1月1日为零点,之前有用1900年和1950年的。
9 M7 n( a0 Q, b- h& ^. E+ L$ g8 M: C/ c
从儒略日计算星期几,(JDN+1) mod 7 就好了。
6 b; S$ `0 j9 f( P- b9 ^; a1 j& w, _: s6 e
这个公式是怎么来的呢? 1582年,教皇格里高利十三(XIII)发现,那一年的春分是3月11日,和儒略历里规定的日期3月21日差了十天。原因嘛,就是回归年的长度是365.2422,儒略历用的365.25。格十三用上了全部的指头,哦,应该是找了很多XX家之后,下令当年10月4日的后一天是10月15日,同时规定在原先四年一闰的基础上,100的整数倍年不是闰年,但400的整数倍年又是闰年。新的历法改名为格里高利历。
0 O+ D% {* Q' y; t. s( a, @4 u6 b
# O- X) p0 l( Y) x为了计算转换儒略历和格里高利历,一个法国的教会学者Joseph Justus Scaliger给出了这个公式。“儒略日”中的儒略,是他老爸的名字。
% S# [: x: ~& r7 H/ c  o! n* H/ U3 u4 d2 O3 i) [- D
哦,原来的文献中用的是儒略历日期,要算儒略日是这样的:
: t2 g* W% E7 Q
; x  k% o7 w5 S5 S% M- X- G8 D" V& X+ k2 G
2 J5 E) @5 E: B$ B# q* I4 B
从儒略日转格里高利历,也有一组公式,这里有:0 r; W- e& l; X. u

0 g0 Q4 E- {& B其实这些偏差,在儒略历启用之前是有人知道的。但是,始皇三十五年的某一天,一个罗马士兵在西西里岛上,拔出刀来,朝一个老人身上刺下去。这一刺,西方的科技文明停滞了一千多年。[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
4 ~( p$ ?" Y. B5 [- |看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...
# y, [, M: F( e+ K. o
试试计算下一千年每年复活节是哪一天,我又得昏过去了。。。哈哈哈。。。
作者: 橡树村    时间: 2015-2-3 01:49
老兵帅客 发表于 2015-2-2 13:09: ~6 V9 G& ~( I1 N& R
看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...
: s2 y/ |) Z6 k
这个在当年可以用的日期函数还很罕见的时候有用。后来系统本身就提供这些计算了,自然没必要记。
5 f4 V/ i9 T$ g( O+ R( R# e: r! G5 x& T! e8 N3 R5 t
我最早见到也是学BASIC的时候。
9 N# W' N9 L! g2 e& j) Y! B" N* b# ^

作者: 橡树村    时间: 2015-2-3 01:53
本帖最后由 橡树村 于 2015-2-3 01:55 编辑 # e. w- B4 Y) z
hotmen 发表于 2015-2-2 11:27; u( {7 @, h" f+ O
能换算干支就更好了。

: n( `- m* T* ~' u6 y  g# r- x; M2 i$ \6 x7 B# l3 d5 @
计算干支里面的日期不难,时辰是从日期推算的,也不难。$ v4 x! G& [0 p6 ?
月份是按照年来推算的,说起来简单,难点在于一年以及一个月的开始时间的计算。这个很难有通用公式。不过还是比农历要简单,干支记年实际上是阳历,每年开始于立春,然后每间隔一个节气就换一个月,与农历的月份并不相同。这样只要有了节气的准确时间数据库,干支的问题也就解决了。
- z# \- l3 V* \9 x; C/ a. K2 x; Z8 e% q9 R! e/ J% ]. A$ H
农历复杂在于,这个历法经常被改动,要准确把历史上的某一天与西历进行换算,需要把曾经使用过的历法都考虑进去,这个麻烦就大了去了。而且每个月的开始取决于月亮的朔望,这就更要把历史上月亮的运行轨道都考虑进去了。
" N  v* ?4 B& ?/ T3 v: I( H1 `% }1 U; v

作者: 老兵帅客    时间: 2015-2-3 02:13
橡树村 发表于 2015-2-2 12:49
6 H! G  s+ ]# H$ v, Q这个在当年可以用的日期函数还很罕见的时候有用。后来系统本身就提供这些计算了,自然没必要记。
: Z$ d# U; [7 W6 i* Q
  }  u* ^; S% P# [7 d( }" t我最早 ...
7 A! \) L$ a% x- Q* @
问题是DOS下面的BASIC已经提供日期函数了啊,程序员何必再用这个?
作者: 橡树村    时间: 2015-2-3 02:21
老兵帅客 发表于 2015-2-3 02:13/ _2 f" F; H% O1 g% f
问题是DOS下面的BASIC已经提供日期函数了啊,程序员何必再用这个?

8 @& \5 e0 i& ~( V2 L1 }6 T/ H不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程,最多弄个日历啥的。
作者: 老兵帅客    时间: 2015-2-3 02:29
橡树村 发表于 2015-2-2 13:21" N6 I/ b6 s  @' a; Q0 b! t
不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程 ...
3 U& }  q7 [. T8 n4 s7 q2 s
我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找出当前日期,然后自己计算闰年。计算闰年的算法很简单的,远比楼主的简单。
作者: 老兵帅客    时间: 2015-2-3 02:29
橡树村 发表于 2015-2-2 13:21
" n, e1 T0 x( T: ]% Q; U  I9 K不记得当年BASIC有计算两个日期之间有几天的函数。也许有但从来没用过,当年就没有使用BASIC做过这类编程 ...

1 B+ @0 e1 }# b/ d! ]我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找出当前日期,然后自己计算闰年。计算闰年的算法很简单的,远比楼主的简单。
作者: 橡树村    时间: 2015-2-3 02:42
老兵帅客 发表于 2015-2-3 02:299 C9 v) w7 E; ?; H
我当年学PASCAL的时候,一个作业就是编万年历,从你的当前日期开始。因此我们就用PASCAL自带的日期函数找 ...
4 r1 \! r4 Z) |
Turbo Pascal?
6 b9 [4 Y/ t% f+ X: |0 {- _) F+ k
1 f6 v) O% P8 T9 i7 B最早PC机带的BASIC函数很少的,和Pascal比不了。
作者: 老兵帅客    时间: 2015-2-3 02:44
橡树村 发表于 2015-2-2 13:42/ E0 E9 C8 Z. Y+ T7 A
Turbo Pascal?
  I; O4 M* e. p9 k. V
$ v1 X( r. r; L9 y+ C最早PC机带的BASIC函数很少的,和Pascal比不了。

4 N0 x* U5 `+ D1 l不,是标准PASCAL,用的是微软的编译器。我上学的时候还没出turbo pascal呢,后来这东西出来了,拿来一试,发现丫不兼容标准PASCAL嘿,于是再也没碰它。
作者: 橡树村    时间: 2015-2-3 02:52
老兵帅客 发表于 2015-2-3 02:44
5 L( K' s2 r( H5 x, i不,是标准PASCAL,用的是微软的编译器。我上学的时候还没出turbo pascal呢,后来这东西出来了,拿来一试 ...
9 X8 Q) ~) S! }" `; G& F* \
Turbo pascal 是83年的,那时候我还不知道计算机长啥样呢。
( }% j9 @4 D- w& |. D0 ^) ~  X* Z! U/ {" K" }7 z0 |+ B
我最早是在Comx35机器上接触的BASIC,84年。
作者: 东湖珞珈    时间: 2015-2-3 04:01
橡树村 发表于 2015-2-3 02:52
/ p8 y3 f; q# `. Q. n+ ETurbo pascal 是83年的,那时候我还不知道计算机长啥样呢。0 P/ q+ d4 W4 W9 l7 G, `* l3 L
# q& O( @7 a) i0 h; F1 r
我最早是在Comx35机器上接触的BASIC,84年。 ...
  U, b" y2 ]) X# A
村子老资格啊
作者: shijz    时间: 2015-2-3 07:41
原来在「十万个为什么」第一册上看过这个算法。) u( x9 e( o4 j  R8 i
后来看unix上也是以某一点作起点,以该点到现在的总秒数计算当前日期的。
作者: heinsect    时间: 2015-2-3 10:21
孟词宗 发表于 2015-2-1 20:21. k. W7 Q4 k/ ~9 ^! Z3 s: _" q
程序员为啥不直接用格历?

. \! O; K2 C9 O* z3 i4 m用儒略日的公式,无论是从格历到儒略日,还是从儒略日到格历,只有整数运算,没有一个跳转。
; X+ V4 M6 R9 z; ^  q$ E9 h跳转在计算机程序的低层优化里是个大问题。
9 l) |* a" n* r1 O/ F
作者: heinsect    时间: 2015-2-3 10:21
老兵帅客 发表于 2015-2-2 13:095 d: ~. @  ~$ g7 Z- d
看来俺一定不是程序员了,因为俺从来就没这么复杂地玩过。日期可以从系统函数或者类库中的方法得到,我最多 ...

- t- ^/ N* x) I& W这个方法是天文计算里常用的。不过俺现在的项目中有一个完整的数据库实现,别人做的,中间要计算日期间隔就用了这个方法。系统函数和类库中算星期、日期间隔、日期加偏移应该也是用了这个方法的。
作者: 懒猫猫    时间: 2015-2-3 12:23
抄袭水风语录:
8 _3 p+ U% M- j3 a7 b, [假装我看懂了,然后评分
作者: 橡树村    时间: 2015-2-3 13:55
heinsect 发表于 2015-2-3 10:21
0 Q+ M  C; \6 d* ?: H2 |3 e6 ]. b这个方法是天文计算里常用的。不过俺现在的项目中有一个完整的数据库实现,别人做的,中间要计算日期间隔 ...
! N* z3 p% {- C# `/ Q5 ~! n2 b
这个计算方法的出现和规范,扩展,一直是天文领域的事情。计算机出现的年头毕竟还是太短。
作者: 橡树村    时间: 2015-2-3 13:56
shijz 发表于 2015-2-3 07:41
( ]& p2 m9 e6 o原来在「十万个为什么」第一册上看过这个算法。
, w$ {. s# a" h" {后来看unix上也是以某一点作起点,以该点到现在的总秒数计 ...

5 r# A' p- s; ~6 V3 m" b9 E, c+ i
/ }& b1 l, C8 ]+ WUnix的起点是1970年一月一日UTC零时,以秒为单位,不计算闰秒,所以计算机行业可以用。天文反而用不了。
作者: 穿着裤衩裸奔    时间: 2015-2-3 14:23
原来这是钓鱼+年龄暴露贴
作者: 喜欢    时间: 2015-2-26 21:50
提问:那个floor的功能是怎么算的?
作者: heinsect    时间: 2015-2-26 21:58
喜欢 发表于 2015-2-26 21:50  v, e* y) g8 d) A
提问:那个floor的功能是怎么算的?
$ {& W- |, M5 c0 V) ~2 p% c" ]
小于或等于这个实数的整数中最大的那一个
作者: 喜欢    时间: 2015-2-27 00:26
heinsect 发表于 2015-2-26 08:58
# V, n+ k1 T8 @+ U6 g小于或等于这个实数的整数中最大的那一个

* j$ |! e1 S0 `* `6 K) e我算出来的结果是:7 R9 q% |% e7 @
1        31
! B& \# _0 |2 D. E2        611 A" b! x$ |9 u
3        920 t0 ?, s4 ~  D4 w. Q; W: ?- P* b
4        1227 s9 Q& W" f& a) B5 D
5        153
2 J" `/ w6 `9 h: E- E# B0 s3 _& E6        184
& R: f2 q. c0 _- K  k7        214* p! x1 |) R% Z$ T! h
8        245% y4 z" x4 w) h( A7 }& z
9        275
* [. w) ^6 w. u1 _- j) T10        306
2 f* y, c9 p! v/ k. d! w11        337
" Z' s/ t, z  E9 s. H  v) @% Z12        367' U* n1 n9 H6 `

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




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