[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
回复 60# neorobin


    我的测试结果和你不一致;问题比较大。。。

TOP

本帖最后由 plp626 于 2012-4-12 14:55 编辑

加入日期正确性验证;17万年内顺利通过验证;
完美组合:
  1. y=(i*33+999)/12053 //索引反求日期中年份的
复制代码
  1. int isdate(int y, int m, int d){
  2. int mi[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
  3. if ((y%4 == 0) && (y%100 != 0) || (y%100 == 0))
  4. mi[2]+=1;
  5. if (m<=0||m>12)
  6. return 0;
  7. if (d<=0||d>mi[m])
  8. return 0;
  9. return 1;
  10. }
复制代码
  1. (i*4+99)/1461; 3301-2-28以内正确;
复制代码
  1. (i*4+999)/1461; 33301-2-28以内正确;
复制代码
---------------

需要说明的是最上面的报错测试,17万年出错是因为cmd 的最大数字限制;2147483647 溢出导致的报错;

TOP

回复 64# plp626

如果只是以 由序号得到的日期是否合法来测试 index2date, 我认为是不妥的:

比如: 由 i 得到了 2012.4.10, 而接着 i+1 得到的却是 2011.4.11, ??? 日期都是合法的, 但序列乱了, 也许可以证明出现 序列乱了 也必然出现非法日期, 但至少现在没有证明这一点

TOP

本帖最后由 plp626 于 2012-4-12 15:34 编辑

序号是单调增,也就是说日期必须逐一累加才能得到序号;

如果日期有跳跃,那前面的日期必然出现天数多了出来;
只需验证i2date生成的日期正确即可:
  1. //&cls&@type %~fs0|tcc -run -
  2. // 把tcc.exe 和 lib\msvcrt.def 放在当前目录下;双击本脚本解释执行下面C代码
  3. int i2date(int , int *, int *, int *);
  4. int date2i(int, int, int);
  5. int isdate(int, int, int);
  6. int main(){
  7. int i,j,y,t,m,d;
  8. for (i=0; i<1461*100000; i++){ // 0 ~ 40万年索引号
  9. i2date(i,&y,&m,&d);
  10. if (!isdate(y,m,d)){
  11. printf("wrongdate: /%5d:%4d/%2d/%2d\n",i,y,m,d);
  12. getchar();
  13. }
  14. j=date2i(y,m,d);
  15. if (i!=j) {
  16. printf("/%5d:%4d/%2d/%2d\n",i,y,m,d);
  17. getchar();
  18. }
  19. }
  20. return 0;
  21. }
  22. int isdate(int y, int m, int d){
  23. int mi[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
  24. if ((y%4 == 0) && (y%100 != 0) || (y%100 == 0))
  25. mi[2]+=1;
  26. if (m<=0||m>12)
  27. return 0;
  28. if (d<=0||d>mi[m])
  29. return 0;
  30. return 1;
  31. }
  32. int i2date(int i, int *year, int *month, int *day){ // 索引转日期
  33.    int t,y,m,d;      y=(i*33+999)/12053;
  34.     t=i-y*365-y/4+y/100-y/400;  y+=t>>9;  t=i-y*365-y/4+y/100-y/400;
  35.     m=(t*5+2)/153;  d=t-(m*153+2)/5+1;
  36.     y+=(m+2)/12;  m=(m+2)%12+1;
  37.    *year=y;  *month=m;  *day=d;
  38.     return 0;
  39. }
  40. int date2i(int y, int m, int d){// 日期转索引
  41. m+=9;m%=12; y-=m/10;
  42. return 365*y+y/4-y/100+y/400+(m*153+2)/5+d-1;
  43. }
复制代码

TOP

回复 66# plp626


{1,2,3} <--> {2012.4.12, 2012.4.16, 2012.4.30}

形如上面的互逆映射关系, 也许这里我们讨论的算法不会形成, 但如果某算法造成这样的关系, 那么不能保证日期的连续增 1 的检测方式是检测不出那种算法的错误的.

TOP

有道理。。。

TOP

本帖最后由 terse 于 2012-4-12 12:49 编辑

折腾于2月的序列  是一律按现历计算吗  

先前的代码先删吧

TOP

本帖最后由 plp626 于 2012-4-12 15:29 编辑

回复 69# terse


    最好别删,你刚发的代码我还没来得及消化。。。(看你代码有些像现历转儒略历,和儒略历转现历;)

我在看资料儒略日对年数的累加是乘以系数365.25;现历的一年则是365.2425天;(天文上的一年则是365.24219...)
1天时间86400秒;儒略日的一天的时间相比现历的一天时间 365.2425*86400/365.25-86400=-1.78秒
这个误差在现历中,100年左右就马上体现出来;然后要弥补误差就要跳跃日期;

所以我认为仅仅的儒略日来算时差适用范围在百年内;

现历计算时间差;从天文精确时间算,2700年以内的时间差计算误差不超过1天;

TOP

回复 70# plp626
在56楼 我对 现历转儒略历,和儒略历转现历 发的测试代码
如你所说 我想 365.25 和365.2425 差距是否就是儒略历 一直4年一闰  现历也就是(格历)比较 儒略历 应是 400年 少3 闰
我不知道现在讨论的 是儒略历和现历的互转 还是以现历 序号的互转
如是序号现历的互转 是否看少个 1582年10月的判断 只需每400年多一闰
把每年的3月 看做是一年的开始 然后2月放在做后 管他28天还是29天 反正算来是最后月了 最后按4年一个循环计算也就是 1461 天
感觉算法应该差不多 只要有一个统一下序号是按儒略历或者现历来排列即可吧
现历主要有4年一闰的麻烦(所以有了+Y/400) 而儒略历似乎看作更简便点

TOP

本帖最后由 plp626 于 2012-4-12 16:46 编辑

是现历;

儒略日这个需要点背景知识,一般人也没听过;

我在想我们讨论的日期计算是否分两个版本;

毕竟生活中用到的日期计算及其罕见跨千年的,那两个版本:

实用版: 1980年后的近50年; (主要是根据要求删除或寻找指定时间内的文件,还有倒计时灯 使用)

数值计算版: 公元元年前后的3000年; (研究算法,娱乐使用)

TOP

回复 1# plp626

如果 代码中 使用 ">>31" 就有必要说明是针对 32 位有符号整数的, 或者将其改为 ">>63", 或者其它.

TOP

回复 73# fatcat


    是的;这个是32位;64位的未测试;

就本代码中遇到的情况,因 366 < 2^9=512,把31改为9完全可以;代码还上了1字节;

TOP

本帖最后由 CrLf 于 2012-4-13 15:07 编辑

太棒了,佩服得五体投地
相比之下,我那笨办法是在是丢人死了...

TOP

bug, bug!
到底是谁的算法错了???

nginx 关于日期计算的源代码:
  1.     /*
  2.      * shift new year to March 1 and start months from 1 (not 0),
  3.      * it is needed for Gauss' formula
  4.      */
  5.     if (--month <= 0) {
  6.         month += 12;
  7.         year -= 1;
  8.     }
  9.     /* Gauss' formula for Grigorian days since March 1, 1 BC */
  10.     time = (uint64_t) (
  11.             /* days in years including leap years since March 1, 1 BC */
  12.             365 * year + year / 4 - year / 100 + year / 400
  13.             /* days before the month */
  14.             + 367 * month / 12 - 30
  15.             /* days before the day */
  16.             + day - 1
  17.             /*
  18.              * 719527 days were between March 1, 1 BC and March 1, 1970,
  19.              * 31 and 28 days were in January and February 1970
  20.              */
  21.             - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
复制代码
/src/http/ngx_http_parse_time.c
附件: 您需要登录才可以下载或查看附件。没有帐号?注册

TOP

注意看这部分:
  1.   /* days in years including leap years since March 1, 1 BC */
  2.              365 * year + year / 4 - year / 100 + year / 400
  3.             /* days before the month */
  4.             + 367 * month / 12 - 30
  5.             /* days before the day */
  6.             + day - 1
复制代码

TOP

返回列表