找回密码
 注册
搜索
[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
楼主: plp626

[日期时间] [已更新]批处理单行版时间日期计算 算法讨论

[复制链接]
 楼主| 发表于 2012-4-12 00:22:24 | 显示全部楼层
回复 60# neorobin


    我的测试结果和你不一致;问题比较大。。。
 楼主| 发表于 2012-4-12 00:58:28 | 显示全部楼层
本帖最后由 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 溢出导致的报错;
发表于 2012-4-12 01:06:51 | 显示全部楼层
回复 64# plp626

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

比如: 由 i 得到了 2012.4.10, 而接着 i+1 得到的却是 2011.4.11, ??? 日期都是合法的, 但序列乱了, 也许可以证明出现 序列乱了 也必然出现非法日期, 但至少现在没有证明这一点
 楼主| 发表于 2012-4-12 01:11:08 | 显示全部楼层
本帖最后由 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. }
复制代码
发表于 2012-4-12 01:21:14 | 显示全部楼层
回复 66# plp626


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

形如上面的互逆映射关系, 也许这里我们讨论的算法不会形成, 但如果某算法造成这样的关系, 那么不能保证日期的连续增 1 的检测方式是检测不出那种算法的错误的.
 楼主| 发表于 2012-4-12 01:38:23 | 显示全部楼层
有道理。。。
发表于 2012-4-12 01:42:20 | 显示全部楼层
本帖最后由 terse 于 2012-4-12 12:49 编辑

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

先前的代码先删吧
 楼主| 发表于 2012-4-12 15:14:08 | 显示全部楼层
本帖最后由 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天;
发表于 2012-4-12 16:15:10 | 显示全部楼层
回复 70# plp626
在56楼 我对 现历转儒略历,和儒略历转现历 发的测试代码
如你所说 我想 365.25 和365.2425 差距是否就是儒略历 一直4年一闰  现历也就是(格历)比较 儒略历 应是 400年 少3 闰
我不知道现在讨论的 是儒略历和现历的互转 还是以现历 序号的互转
如是序号现历的互转 是否看少个 1582年10月的判断 只需每400年多一闰
把每年的3月 看做是一年的开始 然后2月放在做后 管他28天还是29天 反正算来是最后月了 最后按4年一个循环计算也就是 1461 天
感觉算法应该差不多 只要有一个统一下序号是按儒略历或者现历来排列即可吧
现历主要有4年一闰的麻烦(所以有了+Y/400) 而儒略历似乎看作更简便点
 楼主| 发表于 2012-4-12 16:44:06 | 显示全部楼层
本帖最后由 plp626 于 2012-4-12 16:46 编辑

是现历;

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

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

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

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

数值计算版: 公元元年前后的3000年; (研究算法,娱乐使用)
发表于 2012-4-12 22:22:08 | 显示全部楼层
回复 1# plp626

如果 代码中 使用 ">>31" 就有必要说明是针对 32 位有符号整数的, 或者将其改为 ">>63", 或者其它.
 楼主| 发表于 2012-4-12 22:30:12 | 显示全部楼层
回复 73# fatcat


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

就本代码中遇到的情况,因 366 < 2^9=512,把31改为9完全可以;代码还上了1字节;
发表于 2012-4-13 15:06:51 | 显示全部楼层
本帖最后由 CrLf 于 2012-4-13 15:07 编辑

太棒了,佩服得五体投地
相比之下,我那笨办法是在是丢人死了...
 楼主| 发表于 2012-11-16 23:09:14 | 显示全部楼层
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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 发表于 2012-11-16 23:10:53 | 显示全部楼层
注意看这部分:

  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
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|批处理之家 ( 渝ICP备10000708号 )

GMT+8, 2026-3-17 07:00 , Processed in 0.022688 second(s), 6 queries , File On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表