Board logo

标题: 求N天前日期的批处理 [打印本页]

作者: namejm     时间: 2007-6-7 21:29    标题: 求N天前日期的批处理

  经常有人询问N天前的日期如何计算,因为牵涉到大月小月、平年闰年的问题,代码将会比较复杂,让人望而却步。

  虽然两年前willsort曾经发出过这样的代码(请参考:(已结)如果在WINDOWS下DOS命令行删除N天以前的文件),尽管思路极其简单:把日期转化为相对数字,做完减法之后,再把相对数字转化为日期,但是,2472632、146097之类的数字来得太过突兀,让人摸不着头脑,说实话,限于水平,我现在都还看得一头雾水。

  现在发三个求N天前日期的批处理( 日期可以带星期,但是,必须以年月日的顺序排列,年份必须是四位,否则会出错),在关键部分都有注释,比willsort的容易懂一点:

  代码1:
@echo off
:: 求N天前的日期
:: 思路:
::   模拟手工运算,用日期数减去天数,差值为负时向高位借1。
:: 特点:
::   代码简洁,容易理解,速度快慢取决于指定的天数大小

setlocal enabledelayedexpansion

:Main
cls
:: 日期可以带星期,但是,必须以年月日的顺序排列
:: 年份必须是四位,否则会出错
set day=%date%
set days=0
echo.&echo.
echo       指定的日期是:%day%
echo.
set /p input=      请指定要追溯的天数:

:: 提取日期
for /f "tokens=1-3 delims=-/. " %%i in ("%day%") do (
    set /a sy=%%i, sm=100%%j %% 100, sd=100%%k %% 100
)

set /a sd-=input

if %sd% leq 0 call :count

cls
echo.&echo.
echo       指定的日期是:%day%
echo.
set sm=0%sm%
set sd=0%sd%
echo       %input% 天前的日期是:%sy%-%sm:~-2%-%sd:~-2%
pause>nul
goto Main

:count
set /a sm-=1
if !sm! equ 0 set /a sm=12, sy-=1
call :days
set /a sd+=days
if %sd% leq 0 goto count
goto :eof

:days
:: 获取指定月份的总天数
set /a leap="^!(sy %% 4) & ^!(^!(sy %% 100)) | ^!(sy %% 400)"
set /a max=28+leap
set num=0
set str=31 %max% 31 30 31 30 31 31 30 31 30 31
for %%i in (%str%) do (
    set /a num+=1
    if %sm% equ !num! set days=%%i&goto :eof
)
goto :eof
  代码2:
@echo off
:: 求N天前的日期
:: 思路:
::   把当前日期转换为天数
::   用当前天数减去N天
::   把结果再转换为日期
:: 具体操作是:
::   逐年计算天数
:: 特点:
::    代码简洁,容易理解,速度较慢,但是不受指定天数大小的影响

setlocal enabledelayedexpansion

:Main
cls
:: 日期可以带星期,但是,必须以年月日的顺序排列
:: 年份必须是四位,否则会出错
set day=%date%
set days=0
echo.&echo.
echo       指定的日期是:%day%
echo.
set /p input=      请指定要追溯的天数:

rem ========把指定日期转换为天数========
:: 提取日期
for /f "tokens=1-3 delims=-/. " %%i in ("%day%") do (
    set /a sy=%%i, sm=100%%j %% 100, sd=100%%k %% 100
)
:: 把年份转化为天数
for /l %%i in (1,1,%sy%) do (
    set /a leap="^!(%%i %% 4) & ^!(^!(%%i %% 100)) | ^!(%%i %% 400)"
    set /a days=days+365+leap
)
:: 把月份转化为天数
set /a num=0, mday=0, max=28+leap
set str=0 31 %max% 31 30 31 30 31 31 30 31 30
for %%i in (%str%) do (
    set /a num+=1
    if !num! leq !sm! set /a mday+=%%i
)

set /a days=days+mday+sd

set /a days-=input


rem ========把天数转换为日期========
:: 获取年份
for /l %%i in (1,1,%sy%) do (
    set /a leap="^!(%%i %% 4) & ^!(^!(%%i %% 100)) | ^!(%%i %% 400)"
    set /a days_tmp=365+leap
    if !days! gtr !days_tmp! (
        set /a days-=days_tmp
        set y=%%i
    )
)
:: 获取月份及日期
set /a m=1, max=28+%leap%
set str=31 %max% 31 30 31 30 31 31 30 31 30
for %%i in (%str%) do (
    if !days! gtr %%i (
        set /a days-=%%i
        set /a m+=1
    ) else goto next
)
:next
set m=0%m%
set d=0%days%

cls
echo.&echo.
echo       指定的日期是:%day%
echo.
echo       %input% 天前的日期是:%y%-%m:~-2%-%d:~-2%
pause>nul
goto Main
  代码3:
@echo off
:: 求N天前的日期
:: 思路:
::   把当前日期转换为天数
::   用当前天数减去N天
::   把结果再转换为日期
:: 具体操作是:
:    先按照每年365天计算,然后再计算闰年的个数
::   最后再做转换
:: 特点:
::   代码复杂,不便于理解,但是速度很快
:: 计算上还存在bug,可以用 11111 来和头两个做对比测试,暂时没时间更新

setlocal enabledelayedexpansion

:Main
cls
:: 日期可以带星期,但是,必须以年月日的顺序排列
:: 年份必须是四位,否则会出错
set day=2007-01-02
set /a days=0, sum=0
echo.&echo.
echo       指定的日期是:%day%
echo.
set /p input=      请指定要追溯的天数:

rem =======把日期转换为天数=======
:: 提取日期
for /f "tokens=1-3 delims=/-. " %%i in ("%day%") do (
    set /a sy=%%i, sm=100%%j %% 100, sd=100%%k %% 100, y=%%i
)

:: 把年份抓换为天数
set num=0
if %sy% lss 400 goto next1
call :leap_num
:next1
set /a leap_num=sy/4-num

:: 把月份转换为天数
set /a leap="^!(sy %% 4) & ^!(^!(sy %% 100)) | ^!(sy %% 400)"
set /a max=28+leap
set num=0
set str=31 %max% 31 30 31 30 31 31 30 31 30 31
for %%i in (%str%) do (
    set /a num+=1
    if !num! lss %sm% set /a sum+=%%i
)

set /a days=365*sy+leap_num+sum+sd

set /a days-=input

rem =======把天数转换为日期=======

:: 获取年份
set /a y=days/365
set /a mod1=days %% 365
call :leap_num
set /a mod2=mod1-leap_num
:loop2
if %mod2% lss 0 (
    set /a y-=1
    set /a mod2=365+mod2
) else goto next2
goto loop2

:: 获取月份及日期
:next2
set /a m=1, days=mod2+1, max=28+%leap%
set str=31 %max% 31 30 31 30 31 31 30 31 30
for %%i in (%str%) do (
    if !days! gtr %%i (
        set /a days-=%%i
        set /a m+=1
    ) else goto next3
)
:next3
set m=0%m%
set d=0%days%

cls
echo.&echo.
echo       指定的日期是:%day%
echo.
echo       %input% 天前的日期是:%y%-%m:~-2%-%d:~-2%
pause>nul
goto Main

:leap_num
:: 计算能被100整除但是不能被400整除的年次
set /a y_tmp=(%y:~0,1%%y%-%y%)/10
set num=0
:loop1
set /a mod=y_tmp %% 400
if %mod% neq 0 set /a num+=1
set /a y_tmp-=100
if %y_tmp% geq 100 goto loop1
set /a leap_num=y/4-num
goto :eof
[ Last edited by namejm on 2007-6-8 at 11:15 PM ]
作者: vkill     时间: 2007-6-7 21:46
用 ruby 简单多了,我只是想追求 “用最短的代码最快时间最合适的工具解决”,没有别的意思~

puts Data.today-3

[ Last edited by vkill on 2007-6-7 at 10:28 PM ]
作者: HAT     时间: 2007-6-7 21:50
学习
发一个使用VBS计算日期的批处理
@echo off
rem 删除三天前的文件夹
rem 目标目录为C:\test
set Target=C:\test
dir /b %Target%>%temp%\FolderList.txt
echo dt=date()-3>%temp%\OldDate.vbs
echo s=right(year(dt),4) ^& right("0" ^& month(dt),2) ^&  right("0" ^& day(dt),2)>>%temp%\OldDate.vbs
echo wscript.echo s>>%temp%\OldDate.vbs
for /f %%f in ('cscript /nologo %temp%\OldDate.vbs') do @set DelFlag=%%f
for /f %%i in (%temp%\FolderList.txt) do (
  if %%i lss %DelFlag% (
    rd /s /q %Target%\%%i
    )
  )
del %temp%\FolderList.txt
del %temp%\OldDate.vbs

作者: wwwyangxm     时间: 2008-12-2 09:12
我想问一下,楼主的第三个方案中set dat=2007-01-02是否为初始时间?昨晚我想了一个方案,不知道是否和楼主的一样?望解答一下,另:楼主的代码我看的不是很明白(方案三)

下面是的我方案:

假设初始时间坐标为1900-01-01   输入时间为date1 (任意时间) ,这样比较容易计算两个时日的差额(天数)tmp1,另外再输入想要查询的在date1前或者后的任意天数tmp2,如果是在date1前N天的,就计算结果为sum=tmp1+tmp2,(tmp2可正或者负,在date1日期后为正,在date1日期前面的为负),这样最后就可以得到一个距离初始时间的时间差额sum,根据1900-01-01为起止时间,计算差额在sum天的日期,应该是比较简单的。

以上的思路可以解决在任意日期的前后N天的日期,起止时间可以根据计算差额多少来设定,以便减少计算量。

由于知识有限,只能提供思路,望大侠们指导下,我的思路不知道是不是和第三个方案一样?说实话,第三个方案我还没完全看明白...
作者: sea1112     时间: 2008-12-27 11:06
::判断当前日期的前一天
::首先直接把天数减1天
::如果出来的是0天就把月减1天,天数是当月的最后一天。
::如果出来的是0月就把年减1年,月数是当年的最后一月。
@echo off&setlocal enabledelayedexpansion
set yyyy=%date:~0,4%
set mm=%date:~5,2%
set dd=%date:~8,2%
set /a nd=!dd!-1
::如果象3月1日减一天是0天就...
if !nd!==0 call :dd0
if !mm!==0 call :mm0
set yyyymmdd=!yyyy!-!mm!-!nd!
echo 今天是:%date:~0,10%
echo 昨天是:!yyyymmdd!
pause

:dd0
set /a mm=!mm!-1
for %%a in (1 3 5 7 8 10 12)do set %%add=31
set /a pddd=!yyyy!*10/4
set pd2d=!pddd:~-1,1!
set 2dd=28
if !pd2d!==0 set 2dd=29
for %%b in (4 6 9 11)do set %%bdd=30
set nd=!%mm%dd!
goto :eof
:mm0
set /a yyyy=!yyyy!-1
set mm=12
set nd=31
goto :eof