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

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

本帖最后由 plp626 于 2012-4-15 11:55 编辑

原来讨论的问题描述较长,为方便大部分人分享讨论结果,把代码放在一楼,原讨论的问题移至2楼;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

下面仅是零星的一些函数;功能比较单一,是为了大家可以根据需求自己定制功能;
核心的两个函数date2i和i2date算法思想参加15楼分析;

生活实用版:1900年3月1日~2100年3月1日(不含)之间的日期推算
  1. :: date2i // 求1900-3-1到y-m-d所经历的天数i; y-m-d 范围:[1900-3-1, 2100-3-1)
  2. set/a "m=(m+9)%%12,y-=m/10+1900,i=365*y+y/4+(m*153+2)/5+d-1"
复制代码
  1. :: i2date // 求1900-3-1日后第i天的日期; i 的范围:[0, 73049)
  2. set/a "y=(4*i+3)/1461,t=i-y*365-y/4,m=(t*5+2)/153+2,d=t-(m*153-304)/5+1,y+=m/12+1900,m=m%%12+1"
复制代码
数值计算版(欢迎大家测试):0-3-1 (序号0)~ 33301-3-1(序号12162940,不含该序号) 之间的日期推算
  1. :date2i <year> <month> <day> <RetVarName>
  2. setlocal&set/a y=%1,m=%2,d=%3
  3. set/a m=(m+9)%%12,y-=m/10,i=365*y+y/4-y/100+y/400+(m*153+2)/5+d-1
  4. endlocal&set %4=%i%&goto:eof
复制代码
  1. :i2date <index> <Ret1> <Ret2> <Ret3>
  2. setlocal&set/a i=%1, y=(4*i+999)/1461
  3. set/a "y+=i-y*365-y/4+y/100-y/400>>9,t=i-y*365-y/4+y/100-y/400"
  4. set/a "m=(t*5+2)/153,d=t-(m*153+2)/5+1,y+=(m+2)/12,m=(m+2)%%12+1"
  5. endlocal&set/a %2=%y%,%3=%m%,%4=%d%&goto:eof
复制代码
-----------------------------定制---------------------------------------
  1. ::  获取指定日期的星期数;返回值为0表示星期日,返回值为1表示星期1,。。。类推
  2. :date2week <year> <month> <day> <RetVarName>
  3. call:i2date %*&set/a %4=(%4+3)%%7&goto:eof
复制代码
  1. @echo off
  2. :: # 获取指定日期之前或之后的日期 nextdate
  3. :: 日期格式(仅支持月数,日数前面 有1位0的格式):
  4. ::           2012-1-01,2012-01-11,2012-1-1,1941-10-01,...
  5. :: 适用范围: 不好描述,完全够日常生活使用;
  6. :: 要计算100年后或100年前的,请选择数值计算版date2i;i2date进行定制;
  7. rem  函数调用格式演示:
  8. call:nextdate mydate="2012-1-1" -7
  9. echo %mydate%
  10. call:nextdate mydate="2012-01-1" -7
  11. echo %mydate%
  12. call:nextdate mydate="2012-1-01" -7
  13. echo %mydate%
  14. call:nextdate mydate="2012-01-01" -7
  15. echo %mydate%
  16. pause&goto:eof
  17. :nextdate <RetVarName> <"DATE"> <[+|-]int> // 获得 给定日期第n天 的日期
  18. setlocal&set tp=%~2
  19. for /f "tokens=1-3delims=-" %%a in ("%tp:-0=-%")do (
  20.   set/a "y=%%a,m=%%b,d=%%c+%3,m=(m+9)%%12,y-=m/10+1900"
  21.   set/a "i=365*y+y/4+(m*153+2)/5+d-1,y=(4*i+3)/1461,t=i-y*365-y/4"
  22.   set/a "m=(t*5+2)/153+2,d=t-(m*153-304)/5+1,y+=m/12+1900,m=m%%12+1"
  23. )
  24. endlocal&set %1=%y%-%m%-%d%&goto:eof
复制代码
  1. :: 求两个日期相隔的天数
  2. :edate <RetVarName> <"DATE1"> <"DATE2">  
  3. setlocal&set d1=%~2&set d2=%~3
  4. :: 参考代码
  5. set d1=%d1:-= %&set d2=%d2:-= %
  6. call :date2i %d1: 0= % r1
  7. call :date2i %d2: 0= % r2
  8. set/a c=r2-r1
  9. endlocal&set ans.%~1=%c%&set ans.%~1&goto:eof
复制代码
2

评分人数

    • CrLf: 有意义,有技术PB + 15 技术 + 2
    • Batcher: Good work!技术 + 1

再次向前辈致敬,这些东西如果每个人都自己去想去算去写,真的麻烦。
但胡乱复制网上经验又不放心。
这里居然有这么优质的资料分享和经验交流!
阅读完毕,心情无以言表。

TOP

回复 79# plp626


    哦,我还以为真的有bug呢

TOP

研究了下,两个算法等价,都正确
  1. :date2i <year> <month> <day> <RetVarName>  // 一楼计算公式
  2. setlocal&set/a y=%1,m=%2,d=%3
  3. set/am+=9,m%%=12,y-=m/10,i=365*y+y/4-y/100+y/400+(m*153+2)/5+d-1
  4. endlocal&if %4.==. (echo %i%)else set %4=%i%
  5. goto:eof
复制代码
  1. :Gauss_date2i <year> <month> <day> [RetVarName]  // Gauss 日期计算公式
  2. setlocal&set/a "y=%1,m=%2-2,d=%3
  3. if %m% leq 0 set/a m+=12,y-=1
  4. set/a i=365*y+y/4-y/100+y/400+367*m/12+d-31
  5. endlocal&if %4.==. (echo %i%)else set %4=%i%
复制代码
1

评分人数

    • CrLf: 真服了你,杀死脑细胞...技术 + 1

TOP

回复 77# plp626


    能给个例子证明一下不?

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

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

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

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

TOP

回复 73# fatcat


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

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

TOP

回复 1# plp626

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

TOP

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

是现历;

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

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

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

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

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

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 15:29 编辑

回复 69# terse


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

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

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

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

TOP

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

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

先前的代码先删吧

TOP

有道理。。。

TOP

返回列表