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

1858 年 11 月 17 日 应该是简化后的吧
儒略日应该推前-4712年1月1日 是儒略日的第0日
这里的 306 是否可看作是30.5833333的近似值  也就是 30.5833333 * M  
这里 (306*m+5)/10 可看作 30.6*M+0.5

这里的 0.5 应该是一个修正 就是+0.5天 因为儒略日简化前 是以中午12点起始计算的

现在计算儒略日公式  譬如(SET /A "M=(M+9)%%12+3,Y=Y-M/13,JDB=365*(4712+Y)+(4712+Y)/4+153*(M+1)/5-Y/100+Y/400+D-61")

只是计算 1582年10月15后的 之前的要+10天  也就是 1582年10月4日至1582年10月14日是空白的

TOP

本帖最后由 plp626 于 2012-4-7 19:03 编辑

http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html
月份转当年天数:(306*m+5)/10是如何构造的?常数5很像是凑出来的,换成4也可以;

TOP

大家 讨论下 儒略日 的思想,为何 “参考日期(第零天)是 1858 年 11 月 17 日”

TOP

本帖最后由 plp626 于 2012-4-7 19:54 编辑

回复 15# neorobin

算法思想体现出了数学之美,深刻,很欣赏;之前用(365.2422±x)常数*year 试着矫正year2day, 对这类整数域上的曲线拟合缺少资料。。。该问题搁置。。。

看了下matlab里面的datenummx.c源代码,year2day用了一个函数,ceil(x),向右取整;
ceil函数在cmd中实现相对繁琐;t=ceil(a/b)  同 set/a "t=a/b+!!(a%b)";
  1. static double cdm[] = {0,31,59,90,120,151,181,212,243,273,304,334};
  2.         if (mon < 1) {
  3.             mon = 1;
  4.         }
  5.         if (mon > 12) {
  6.             y += (mon-1)/12;
  7.             mon = ((mon-1) % 12) + 1;
  8.         }
  9.         *t = 365.*y + ceil(y/4.) - ceil(y/100.) + ceil(y/400.) + cdm[mon-1] + *d;
  10.         if (mon > 2) {
  11.             iy = (int) y;
  12.             if ((iy%4 == 0) && (iy%100 != 0) || (iy%400 == 0)) {
  13.                 *t += 1.;
  14.             }
  15.         }
复制代码
对于月份的转换,没有像儒略日那样,把1,2月份当做上年的末月;而是传统思路,把其当做平年处理,再判断,当所在年份为闰年,且月份大于2时,再+1;

neorobin给出的资料把1,2月份当做上年月份处理,可以将ceil函数转换为fix函数
(fix(a/b)=set/a "a/b",向零取整;15楼的int()取整函数就是向零取整)
也使得月份转换为天数之间建立了向整数近似的线性关系(如果把1,2月份当做当年月份处理,映射关系不是单调的)
从而大大简化了代码;

比较了下,matlab里的datenummx.c的源代码兼容公元前,月份为任意整数,天数为任意整数的情形,但思想在cmd中实现挺繁琐;
附件: 您需要登录才可以下载或查看附件。没有帐号?注册

TOP

回复 15# neorobin


    叹为观止,m*306 真是绝妙的一招

TOP

日期序号算法分析

本帖最后由 neorobin 于 2012-4-9 15:47 编辑
  1. set /a "m+=9, m%%=12, y-=m/10, index=365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + d - 1
复制代码
计算年: 从历法年 x.3.1 日到 (x+1).2.(28/29) 日, 定义为一个计算年,
一个计算年的长度不固定:
365: 当 x+1 是平年份
366: 当 x+1 是闰年份

一个计算年中, 3.1 日称为年始日, 2.(28/29) 日称为年终日
以 0 年 3 月 1 日为基准日期, 序号设为 0, 其后所有日期的序号依次加 1.
基准日期就是第 1 个计算年的年始日.
前 400 个计算年的始日, 终日, 长度, 始日序号, 终日序号如下:
pfirstlastleni(first)i(last)
10.3.11.2.283650364
21.3.12.2.28365365729
32.3.13.2.283657301094
43.3.14.2.2936610951460
...
87.3.18.2.2936625562921
...
9998.3.199.2.283653579436158
10099.3.1100.2.283653615936523
...
400399.3.1400.2.29366145731146096


从基准日期开始, p 个计算年的总天数公式为(全作平年算, 加上 4 的倍数的闰年的个数, 减去整百年份的个数, 再加上 400 的倍数闰年份的个数):

365*p + p/4 - p/100 + p/400

其中包含的最后的年终日和年始日的序号为:

最后一个年终日的序号 = 365*p + p/4 - p/100 + p/400 - 1

最后一个年始日的序号 = 365*q + q/4 - q/100 + q/400      (其中, q=p-1)

对于历法日期 y.m.d, 首先假定它不是一个年终日,
从基准日期到 小于这个日期的最后一个年终日, 就得到 t 个计算年:

t = y:  当 m >= 3 时; 或者 t = y-1:  当 m < 3 时.

计算 t 的代码:
  1. set /a n=m, n+=9, n%=12, t=y, t-=n/10
复制代码
第 t 个年终日的序号 = 365*t + t/4 - t/100 + t/400 - 1
第 t+1 个年始日的序号 = 365*t + t/4 - t/100 + t/400

上述计算中的
  1. set /a n=m, n+=9, n%=12
复制代码
将 m 线性映射到一个新的变量 n, 映射关系为:
m -> n : {3, 4, 5, ... 12, 1, 2} -> {0, 1, 2, ... 9, 10, 11}

n 不再是历法月份的意义, 而是一个计算年中每一个月份对起始月份 3 月的偏移数.
这个 n 将有助于我们构造一个结构简单的离散函数来计算每个月的起始日对 3.1 日的偏移量.

于是:

y.m.d 的序号 = 第 t+1 个年始日(3.1 日)的序号 + n.1 日对 3.1 日的偏移 + d 日对 1 日的偏移

m.1 日对 3.1 日的偏移 用一个关于 n 的近似线性的离散函数来实现:

int(f(n)) = int((n*306 + 5)/10)

这里 int() 是截尾式取整, 即把参数的小数部分直接去掉, 而不是四舍五入的方式.
Monthnf(n)*10int(f(n))int(f(n+1))-int(f(n))
Mar05031
Apr13113130
May26176131
Jun39239230
Jul.4122912231
Aug5153515331
Sep6184118430
Oct7214721431
Nov8245324530
Dec9275927531
Jan10306530631
Feb113371337

第 n 个计算月的天数: Δint(f(n)) = int(f(n+1))-int(f(n))   (n < 11)
Δint(f(n)) 的平均值 = 30.6 ≈ (31+30+31+30+31+31+30+31+30+31+31)/11

上面表中 f(n)*10, int(f(n)), int(f(n+1))-int(f(n)) 这 3 列可用如下代码在命令行运行得到
  1. for /l %a in (0 1 11) do >nul set /a "fn=%a*306+5" & set fn
  2. for /l %a in (0 1 11) do >nul set /a "fn=%a*306+5, ifn=fn/10" & set ifn
  3. for /l %a in (0 1 10) do >nul set /a "delta=((%a+1)*306+5)/10-(%a*306+5)/10" & set delta
复制代码
以上, 对 年始日序号 和 月始日序号偏移 的计算, 为了方便而引入了变量 t 和 n, 现在我们去掉这两个变量,
给出计算日期 y.m.d 的序号 i 的伪代码:

i(y, m, d) {
  set /a "m+=9, m%=12, y-=int(m/10)"
  return 365*y + y/4 - y/100 + y/400 + int((m*306 + 5)/10) + d - 1
}

仅就此函数而言, 返回值也可不要最后的 "- 1", 只是把任意日期的序号增长了 1, 或者认为将日期序号轴的原点左移了一天.

cmd 的代码在本文开头已给出.

下接 25# 序号求日期的算法分析

参考原文:
http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html
http://alcor.concordia.ca/~gpkatch/gdate-method.html
2

评分人数

    • plp626: 好资料;PB + 10
    • CrLf: 赞!叹为观止技术 + 2 PB + 15

TOP

希望有更多的函数,可是这些函数怎么调用啊?

TOP

关于测试:

对于所有 日期序号 类型的算法: (任何日期对应一个整数, 即序号; 任何一个"明天"的序号=任何一个"今天"的序号+1)
所有正确的算法对两个给定的日期的 差都会是一致的,  
所有正确的算法 得出任何给定的日期的 星期都会是一致的
不同的算法可以取不同的基准日期(可计算范围也可能不同),  用相同的日期参数得到的日期序号可以不同

用 两个日期的差 和 任何日期的星期 在算法间作对比, 可以找出错误的算法, 但不能证明某算法是正确的.

把一个思路非常简单, 非常容易理解的算法 假设 为一个正确的算法.
1

评分人数

    • plp626: 说得不错。。PB + 10

TOP

本帖最后由 CrLf 于 2012-4-4 12:01 编辑

根据儒略日公式化简所得的 Q1 方案:
  1. @echo off&setlocal enabledelayedexpansion
  2. set/a y1=2012,m1=4,d1=3,h1=1,f1=2,s1=9
  3. set/a y2=y1,m2=m1,d2=d1,h2=1,f2=3,s2=9
  4. set /a "out=(1461*(y2+(m2-14)/12)/4+367*(m2-2-(m2-14)/12*12)/12-3*((y2+(m2-14)/12)/100+1)/4-1461*(y1+(m1-14)/12)/4-367*(m1-2-(m1-14)/12*12)/12+3*((y1+(m1-14)/12)/100+1)/4-d1+d2)*86400+(h2-h1)*3600+(f2-f1)*60+s2-s1"
  5. echo %out%
  6. rem 输出值out=60
  7. pause
复制代码
确实比早先那个从元年开始硬算的方案简洁

TOP

本帖最后由 terse 于 2012-4-4 03:35 编辑

个人较倾向于转换成儒略日 然后计算
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%i in (1 1 31) do set "D%%i=0%%i"
  3. set "Xq=一二三四五六日"
  4. set "Ymda=1990 1 1"&set "Ymdb=2012 4 3"
  5. for /f "tokens=1-6" %%i in ("%Ymda% %Ymdb%") do set /a "JDA=%%k-32075+1461*(%%i+4800+(%%j-14)/12)/4+367*(%%j-2-(%%j-14)/12*12)/12-3*((%%i+4900+(%%j-14)/12)/100)/4,JDB=%%n-32075+1461*(%%l+4800+(%%m-14)/12)/4+367*(%%m-2-(%%m-14)/12*12)/12-3*((%%l+4900+(%%m-14)/12)/100)/4,JD=JDB-JDA"
  6.    echo %Ymda% 至 %Ymdb% 相隔  %JD% 天
  7.    REM 查询N天前或后的日期;
  8.    set/p N=请输入天数(往前查询加上-如:-10000 表示10000天前的日期。):
  9.    set /a JD=N+JDA,W=JD%%7,JD+=68569,N=(4*JD)/146097,JD-=(146097*N+3)/4,I=(4000*JD+1)/1461001,JD-=(1461*I)/4-31,J=(80*JD)/2447,D=JD-(2447*J)/80,JD=J/11,M=J+2-(12*JD),Y=100*(N-49)+I+JD
  10.    for /f "tokens=1-3" %%i in ("!M! !D! !W!") do echo;你查询的是:!Y!年!D%%i:~-2!月!D%%j:~-2!日 星期!Xq:~%%k,1!
  11. pause
复制代码
1

评分人数

    • CrLf: 这个好PB + 5 技术 + 1

TOP

各种大神
各种葱白
早中晚各问自己一遍:你平均每周帮助别人解决几个问题?

TOP

Q6 照搬函数里的现成公式...
  1. @echo off
  2. set/a y=2012,m=4,d=3
  3. set /a m+=!(m/=3)*12,out=(d+2*m+3*(m+1)/5+(y-=m/13)+y/4-y/100+y/400+1)%%7
  4. echo %out%
  5. rem 输出值out=2
  6. pause
复制代码

TOP

Q4:
  1. @echo off
  2. set/a y=2012,m=4,d=3,h=1,f=2,s=9
  3. set/a x=-4
  4. set/a d=d-397+30*m+m/9*-~m/2+!(m/9)*m/2+!!(m/3)*(!(y%%4)-!(y%%100)+!(y%%400)-2)+y*365+~-y/4-~-y/100+~-y/400+x,d-=d/1461-d/36524+d/146097,y=d/365+1,r=!(y%%4)-!(y%%100)+!(y%%400),d+=(d%%=365)/(212+r)*30+r,d+=!!(d/(59+r))*(2-r),m=d/61*2+d%%61/31,d-=m*61/2+m*61%%2-1,m+=1-m/8
  5. set out=%y% %m% %d% %h% %f% %s%
  6. echo %out%
  7. rem 输出值out=2012 4 3 1 1 8
  8. pause
复制代码
思路:
  1. 取天数加上偏移量
  2. 计算闰年数量取得年数
  3. 取得月份数量
  4. 取得天数(好像都是废话...)
复制代码
大同小异的 Q5:
  1. set/a s=h*3600+f*60+s+x,d=d-397+30*m+m/9*-~m/2+!(m/9)*m/2+!!(m/3)*(!(y%%4)-!(y%%100)+!(y%%400)-2)+y*365+~-y/4-~-y/100+~-y/400+s/86400,d-=d/1461-d/36524+d/146097,y=d/365+1,r=!(y%%4)-!(y%%100)+!(y%%400),d+=(d%%=365)/(212+r)*30+r,d+=!!(d/(59+r))*(2-r),m=d/61*2+d%%61/31,d-=m*61/2+m*61%%2-1,m+=1-m/8,h=s/3600%%60,f=s/60%%60,s%%=60
复制代码

TOP

本帖最后由 CrLf 于 2012-4-4 00:41 编辑

Q1:
  1. @echo off
  2. set/a y1=2012,m1=4,d1=3,h1=1,f1=2,s1=9
  3. set/a y2=y1,m2=m1,d2=d1,h2=1,f2=3,s2=9
  4. set/a out=(d2-d1+30*(m2-m1)+m2/9*-~m2/2+!(m2/9)*m2/2+!!(m2/3)*(!(y2%%4)-!(y2%%100)+!(y2%%400)-2)-m1/9*+~m1/2-!(m1/9)*m1/2-!!(m1/3)*(!(y1%%4)-!(y1%%100)+!(y1%%400)-2)+(y2-y1)*365+~-y2/4-~-y2/100+~-y2/400-~-y1/4+~-y1/100-~-y1/400)*86400+(h2-h1)*3600+(f2-f1)*60+s2-s1
  5. echo %out%
  6. rem 输出值out=60
  7. pause
复制代码
思路:
  1. 年份之差*365+天数之差
  2. 按大小月、平闰年修正天数
  3. 所得天数乘一日总秒数
  4. 最后加上秒数差即可
复制代码
其余几个命题同样可依照几个原型公式稍加修改


与 Q1 同类的:
Q2:
  1. @echo off
  2. set/a y1=2012,m1=4,d1=3,h1=1,f1=2,s1=9
  3. set/a y2=y1,m2=5,d2=3,h2=1,f2=2,s2=9
  4. set/a out=d2-d1+30*(m2-m1)+m2/9*-~m2/2+!(m2/9)*m2/2+!!(m2/3)*(!(y2%%4)-!(y2%%100)+!(y2%%400)-2)-m1/9*+~m1/2-!(m1/9)*m1/2-!!(m1/3)*(!(y1%%4)-!(y1%%100)+!(y1%%400)-2)+(y2-y1)*365+~-y2/4-~-y2/100+~-y2/400-~-y1/4+~-y1/100-~-y1/400+((h2-h1)*3600+(f2-f1)*60+s2-s1)/86400
  5. echo %out%
  6. rem 输出值out=31
  7. pause
复制代码
Q3:
  1. @echo off
  2. set/a y1=2012,m1=4,d1=3,h1=1,f1=2,s1=9
  3. set/a y2=2012,m2=4,d2=5,h2=1,f2=2,s2=10
  4. set/a days=d2-d1+30*(m2-m1)+m2/9*-~m2/2+!(m2/9)*m2/2+!!(m2/3)*(!(y2%%4)-!(y2%%100)+!(y2%%400)-2)-m1/9*+~m1/2-!(m1/9)*m1/2-!!(m1/3)*(!(y1%%4)-!(y1%%100)+!(y1%%400)-2)+(y2-y1)*365+~-y2/4-~-y2/100+~-y2/400-~-y1/4+~-y1/100-~-y1/400,secs=(h2-h1)*3600+(f2-f1)*60+s2-s1
  5. set out=%days% %secs%
  6. echo %out%
  7. rem 输出值out=2 1
  8. pause
复制代码
顺便说一下,楼主似乎没有明确说明天数和秒数的概念,天数的计算是否要精确到秒?秒数的取值范围有多大(需要将相隔天数转为对应秒数吗)?

TOP

这个很感兴趣,占位子
#&cls&@powershell "Invoke-Expression ([Io.File]::ReadAllText('%~0',[Text.Encoding]::UTF8))" &pause&exit

TOP

返回列表