[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
本帖最后由 plp626 于 2012-4-11 21:02 编辑

C测试,40万年以内测试用时秒杀
  1. //&cls&@type %~fs0|tcc -run -
  2. // 把tcc.exe 和 lib\msvcrt.def 放在当前目录下;双击本脚本解释执行下面C代码
  3. int i2date(int i, int *, int *, int *);
  4. int date2i(int, int, int);
  5. /*
  6. * 把0-3-1作为参考日历{0-0-0}的0号索引,后面依次类推;
  7. * 参考日历月份数为 0,1,2,...,11;
  8. * 11月分闰月和平月;当所在年份+1为闰年时为闰月;
  9. * 参考日历日期数为 0,1,...,30;
  10. * 大月最大偏移30,小月最大偏移29;
  11. * 11月中,闰月最大偏移28;平月最大偏移27;
  12. */
  13. int main(){
  14. int i,j;
  15. int y,t,m,d;
  16. for (i=0; i<1461*100000; i++){ // 0 ~ 40万年索引号
  17. i2date(i,&y,&m,&d);
  18. j=date2i(y,m,d);
  19. if (i!=j) { // 测试,寻找不对称转换的具体值。。
  20. printf("/%5d:%4d/%2d/%2d\n",i,y,m,d);
  21. getchar();
  22. }
  23. }
  24. return 0;
  25. }
  26. int i2date(int i, int *year, int *month, int *day){ // 索引转日期
  27. int t,y,m,d;
  28. y=(i*4+999)/1461;          // 1/365.2425的前6位小数最佳有理逼近
  29. // y=(i*99+145)/36159;
  30. // y=i*33/12053;            //1/365.2425的前9位小数最佳有理逼近
  31. t=i-y*365-y/4+y/100-y/400;
  32. y+=t>>9; // 获得参考年份;若t为负数,将参考年减1
  33. t=i-y*365-y/4+y/100-y/400;  // 获得参考年0月0日的偏移数
  34. m=(t*5+2)/153; // 获得参考月份
  35. d=t-(m*153+2)/5; // 获得参考日期
  36. d=d+1; // 获得实际日期
  37. y+=(m+2)/12; // 获得实际年份
  38. m=(m+2)%12+1; // 获得实际月份
  39. *year=y;
  40. *month=m;
  41. *day=d;
  42. return 0;
  43. }
  44. int date2i(int y, int m, int d){// 日期转索引
  45. int i;
  46. m+=9;
  47. m%=12;
  48. y-=m/10;
  49. //上面三句做平移取模,3月作为当年的0月;2月作为上1年的11月;
  50. i=365*y+y/4-y/100+y/400+(m*153+2)/5+d-1;
  51. // 11月初的偏移为337;337/11≈153/5;
  52. // (m*153+2)/5 将 {0,1,2,...,11}映射为{0,31,61,...,337}
  53. return i;
  54. }
复制代码
可是,这样的对称测试 有意义吗? 即使在date2index正确的条件下,也存在index2date出错;

即使date2index正确,date2index它也只是保证正确的日期生成正确的索引;
错误的日期也有可能生成正确的索引;2002-2-29号是错的,但它在意义上式2002-3-1;生成的索引也是2002-3-1日的索引;
如此以来,还需验证index2date生成正确的日期。。。

TOP

进一步测试发现,i2date函数很是问题;

bug丛生;

问题出在 i2date函数的第一句; 不是随便可以精简的。。。

TOP

回复 52# terse


不同年份阶段,要不同的历法,    太复杂了; 很不好计算;

反正matlab里面100-2-28的下一天是100-3-1;
  1. >> datestr(36584,26)
  2. ans =
  3. 0100/02/28
  4. >> datestr(36585,26)
  5. ans =
  6. 0100/03/01
  7. >>
复制代码

TOP

i2date中,
  1. y=(i*99+145)/36159;
复制代码
通过9999年以内的索引转日期测试;
我用matlab生成标准日期文件69M+;
再用测试代码用C生成了标准日期文件69M+;
两个文件md5值相同均为 a6740e69a45b825334b8c4c4c986fe2f;

标准打印格式:

fid 模式wt;
fprintf(fid,"%7d:%.4d/%.2d/%.2d\n",i,y,m,d);
末行 3652060:9999/03/02

索引范围 0~3652060 [0-3-1 ~ 9999-3-2]

TOP

回复 51# neorobin


    我相信那个比值 (33*i+x)/12053可代替 (i*99+145)/36159;且范围不会大幅缩减;

找3000年以内的;我考虑 (4*i+x)/1461

TOP

你测试 (i * 4 + 93) / 1461 兼容的最大范围是?

TOP

回复 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

本帖最后由 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

有道理。。。

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

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

是现历;

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

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

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

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

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

TOP

回复 73# fatcat


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

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

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

返回列表