バッチ ファイルを使用して解決する相対日付/時刻関連の質問が非常によくあります。ただし、コマンド ライン インタープリターcmd.exeには、日付/時刻の計算機能がありません。追加のコンソール アプリケーションまたはスクリプトを使用した多くの有効なソリューションが、既にここ、Stack Overflow の他のページ、および他の Web サイトに投稿されています。
日付/時刻に基づく操作に共通するのは、日付/時刻文字列を決定された日からの秒数に変換する必要があることです。非常に一般的なのは 1970-01-01 00:00:00 UTC です。ただし、特定のタスクをサポートするために必要な日付範囲に応じて、任意の日付を使用することもできます。
Jayは、コマンド ライン インタープリターcmd.exeの高速な "日付から秒まで" ソリューションを含む7daysclean.cmdを投稿しました。ただし、うるう年は考慮されません。JRは、現在の年の閏日を考慮に入れるためのアドオンを投稿しましたが、基準年以降、つまり 1970 年以降の他の閏年は無視します。
C/C++ で記述されたアプリケーションの日付/時刻変換関数で、1970 年 1 月 1 日からのうるう日を含む日数をすばやく取得するために、小さな C 関数で一度作成された静的テーブル (配列) を 20 年間使用しています。
この非常に高速なテーブル メソッドは、FORコマンドを使用したバッチ コードでも使用できます。GetSeconds
そこで、このルーチンに渡された日付/時刻文字列の 1970-01-01 00:00:00 UTC からの秒数を計算するバッチ サブルーチンをコーディングすることにしました。
注: Windows ファイル システムもうるう秒をサポートしていないため、うるう秒は考慮されません。
まず、テーブル:
うるう日を含む各年の 1970-01-01 00:00:00 UTC からの日数。
1970 - 1979: 0 365 730 1096 1461 1826 2191 2557 2922 3287
1980 - 1989: 3652 4018 4383 4748 5113 5479 5844 6209 6574 6940
1990 - 1999: 7305 7670 8035 8401 8766 9131 9496 9862 10227 10592
2000 - 2009: 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245
2010 - 2019: 14610 14975 15340 15706 16071 16436 16801 17167 17532 17897
2020 - 2029: 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550
2030 - 2039: 21915 22280 22645 23011 23376 23741 24106 24472 24837 25202
2040 - 2049: 25567 25933 26298 26663 27028 27394 27759 28124 28489 28855
2050 - 2059: 29220 29585 29950 30316 30681 31046 31411 31777 32142 32507
2060 - 2069: 32872 33238 33603 33968 34333 34699 35064 35429 35794 36160
2070 - 2079: 36525 36890 37255 37621 37986 38351 38716 39082 39447 39812
2080 - 2089: 40177 40543 40908 41273 41638 42004 42369 42734 43099 43465
2090 - 2099: 43830 44195 44560 44926 45291 45656 46021 46387 46752 47117
2100 - 2106: 47482 47847 48212 48577 48942 49308 49673
1970-01-01 から始まるエポックで 2039 年から 2106 年の秒を計算するには、符号なし 32 ビット変数、つまり C/C++ の unsigned long (または unsigned int) を使用する必要があります。
ただし、cmd.exeは数式に符号付き 32 ビット変数を使用します。したがって、最大値は 2147483647 (0x7FFFFFFF) で、2038-01-19 03:14:07 です。
1970 年から 2106 年までの閏年情報 (No/Yes)。
1970 - 1989: N N Y N N N Y N N N Y N N N Y N N N Y N
1990 - 2009: N N Y N N N Y N N N Y N N N Y N N N Y N
2010 - 2029: N N Y N N N Y N N N Y N N N Y N N N Y N
2030 - 2049: N N Y N N N Y N N N Y N N N Y N N N Y N
2050 - 2069: N N Y N N N Y N N N Y N N N Y N N N Y N
2070 - 2089: N N Y N N N Y N N N Y N N N Y N N N Y N
2090 - 2106: N N Y N N N Y N N N N N N N Y N N
^ year 2100
現在の年の各月の最初の日までの日数。
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
Year with 365 days: 0 31 59 90 120 151 181 212 243 273 304 334
Year with 366 days: 0 31 60 91 121 152 182 213 244 274 305 335
これらのテーブルを使用すると、日付を 1970 年 1 月 1 日からの秒数に変換するのは非常に簡単です。
ご注目ください!
日付と時刻の文字列の形式は、Windows の地域と言語の設定によって異なります。環境変数に割り当てられた区切り文字とトークンの順序Day
、Month
およびYear
最初のFORループではGetSeconds
、必要に応じてローカルの日付/時刻形式に適合させる必要があります。
環境変数DATEの日付形式がコマンドFOR on で使用される日付形式と異なる場合は、環境変数の日付文字列を調整する必要があります%%~tF
。
たとえば、%DATE%
expands to Sun 02/08/2015
while %%~tF
expands to02/08/2015 07:38 PM
以下のコードは、4 行目を次のように変更して使用できます。
call :GetSeconds "%DATE:~4% %TIME%"
これにより、サブルーチンに渡される02/08/2015
のは、曜日の省略形の 3 文字と区切りのスペース文字を除いた日付文字列だけです。
または、現在の日付を正しい形式で渡すために以下を使用できます。
call :GetSeconds "%DATE:~-10% %TIME%"
現在、日付文字列の最後の 10 文字が関数に渡されるGetSeconds
ため、環境変数DATEの日付文字列が曜日の有無に関係なく、日と月が常に 2 桁の順序である限り、つまり形式はdd/mm/yyyy
またはdd.mm.yyyy
です。
C:\Temp
これは、削除するファイルとフォルダー ツリーに保持するファイルを出力するコメントを説明するバッチ コードです。最初のFORループのコードを参照してください。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Get seconds since 1970-01-01 for current date and time.
call :GetSeconds "%DATE% %TIME%"
rem Subtract seconds for 7 days from seconds value.
set /A "LastWeek=Seconds-7*86400"
rem For each file in each subdirectory of C:\Temp get last modification date
rem (without seconds -> append second 0) and determine the number of seconds
rem since 1970-01-01 for this date/time. The file can be deleted if seconds
rem value is lower than the value calculated above.
for /F "delims=" %%# in ('dir /A-D-H-S /B /S "C:\Temp"') do (
call :GetSeconds "%%~t#:0"
set "FullFileName=%%#"
setlocal EnableDelayedExpansion
rem if !Seconds! LSS %LastWeek% del /F "!FullFileName!"
if !Seconds! LEQ %LastWeek% (
echo Delete "!FullFileName!"
) else (
echo Keep "!FullFileName!"
)
endlocal
)
endlocal
goto :EOF
rem No validation is made for best performance. So make sure that date
rem and hour in string is in a format supported by the code below like
rem MM/DD/YYYY hh:mm:ss or M/D/YYYY h:m:s for English US date/time.
:GetSeconds
rem If there is " AM" or " PM" in time string because of using 12 hour
rem time format, remove those 2 strings and in case of " PM" remember
rem that 12 hours must be added to the hour depending on hour value.
set "DateTime=%~1"
set "Add12Hours=0"
if not "%DateTime: AM=%" == "%DateTime%" (
set "DateTime=%DateTime: AM=%"
) else if not "%DateTime: PM=%" == "%DateTime%" (
set "DateTime=%DateTime: PM=%"
set "Add12Hours=1"
)
rem Get year, month, day, hour, minute and second from first parameter.
for /F "tokens=1-6 delims=,-./: " %%A in ("%DateTime%") do (
rem For English US date MM/DD/YYYY or M/D/YYYY
set "Day=%%B" & set "Month=%%A" & set "Year=%%C"
rem For German date DD.MM.YYYY or English UK date DD/MM/YYYY
rem set "Day=%%A" & set "Month=%%B" & set "Year=%%C"
set "Hour=%%D" & set "Minute=%%E" & set "Second=%%F"
)
rem echo Date/time is: %Year%-%Month%-%Day% %Hour%:%Minute%:%Second%
rem Remove leading zeros from the date/time values or calculation could be wrong.
if "%Month:~0,1%" == "0" if not "%Month:~1%" == "" set "Month=%Month:~1%"
if "%Day:~0,1%" == "0" if not "%Day:~1%" == "" set "Day=%Day:~1%"
if "%Hour:~0,1%" == "0" if not "%Hour:~1%" == "" set "Hour=%Hour:~1%"
if "%Minute:~0,1%" == "0" if not "%Minute:~1%" == "" set "Minute=%Minute:~1%"
if "%Second:~0,1%" == "0" if not "%Second:~1%" == "" set "Second=%Second:~1%"
rem Add 12 hours for time range 01:00:00 PM to 11:59:59 PM,
rem but keep the hour as is for 12:00:00 PM to 12:59:59 PM.
if %Add12Hours% == 1 if %Hour% LSS 12 set /A Hour+=12
set "DateTime="
set "Add12Hours="
rem Must use two arrays as more than 31 tokens are not supported
rem by command line interpreter cmd.exe respectively command FOR.
set /A "Index1=Year-1979"
set /A "Index2=Index1-30"
if %Index1% LEQ 30 (
rem Get number of days to year for the years 1980 to 2009.
for /F "tokens=%Index1% delims= " %%Y in ("3652 4018 4383 4748 5113 5479 5844 6209 6574 6940 7305 7670 8035 8401 8766 9131 9496 9862 10227 10592 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245") do set "Days=%%Y"
for /F "tokens=%Index1% delims= " %%L in ("Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N") do set "LeapYear=%%L"
) else (
rem Get number of days to year for the years 2010 to 2038.
for /F "tokens=%Index2% delims= " %%Y in ("14610 14975 15340 15706 16071 16436 16801 17167 17532 17897 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550 21915 22280 22645 23011 23376 23741 24106 24472 24837") do set "Days=%%Y"
for /F "tokens=%Index2% delims= " %%L in ("N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N") do set "LeapYear=%%L"
)
rem Add the days to month in year.
if "%LeapYear%" == "N" (
for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M"
) else (
for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M"
)
rem Add the complete days in month of year.
set /A "Days+=Day-1"
rem Calculate the seconds which is easy now.
set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second"
rem Exit this subroutine.
goto :EOF
最適なパフォーマンスを得るには、すべてのコメントを削除することをお勧めします。つまり、先頭のスペースが 0 ~ 4 個の後にremで始まるすべての行を削除することです。
また、配列を小さくすることもできます。つまり、上記のバッチ コードで現在サポートされている 1980-01-01 00:00:00 から 2038-01-19 03:14:07 までの時間範囲を、たとえば 2015-01 に減らすことができます。 -01 から 2019-12-31 は、以下のコードが使用するため、C:\Temp
フォルダー ツリーで 7 日より古いファイルを実際に削除します。
さらに、以下のバッチ コードは 24 時間形式に最適化されています。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
call :GetSeconds "%DATE:~-10% %TIME%"
set /A "LastWeek=Seconds-7*86400"
for /F "delims=" %%# in ('dir /A-D-H-S /B /S "C:\Temp"') do (
call :GetSeconds "%%~t#:0"
set "FullFileName=%%#"
setlocal EnableDelayedExpansion
if !Seconds! LSS %LastWeek% del /F "!FullFileName!"
endlocal
)
endlocal
goto :EOF
:GetSeconds
for /F "tokens=1-6 delims=,-./: " %%A in ("%~1") do (
set "Day=%%B" & set "Month=%%A" & set "Year=%%C"
set "Hour=%%D" & set "Minute=%%E" & set "Second=%%F"
)
if "%Month:~0,1%" == "0" if not "%Month:~1%" == "" set "Month=%Month:~1%"
if "%Day:~0,1%" == "0" if not "%Day:~1%" == "" set "Day=%Day:~1%"
if "%Hour:~0,1%" == "0" if not "%Hour:~1%" == "" set "Hour=%Hour:~1%"
if "%Minute:~0,1%" == "0" if not "%Minute:~1%" == "" set "Minute=%Minute:~1%"
if "%Second:~0,1%" == "0" if not "%Second:~1%" == "" set "Second=%Second:~1%"
set /A "Index=Year-2014"
for /F "tokens=%Index% delims= " %%Y in ("16436 16801 17167 17532 17897") do set "Days=%%Y"
for /F "tokens=%Index% delims= " %%L in ("N Y N N N") do set "LeapYear=%%L"
if "%LeapYear%" == "N" (
for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M"
) else (
for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M"
)
set /A "Days+=Day-1"
set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second"
goto :EOF
Windows での日付と時刻の形式とファイル時間の比較の詳細については、ファイル時間に関する多くの追加情報を含むバッチ ファイルでファイルが 4 時間以上古いかどうかを調べるに関する私の回答を参照してください。