Muitas vezes, existem questões relacionadas a data / hora relativas a serem resolvidas com o arquivo em lote. Mas o interpretador de linha de comando cmd.exe não tem função para cálculos de data / hora. Muitas boas soluções de trabalho usando scripts ou aplicativos de console adicionais já foram postadas aqui, em outras páginas do Stack Overflow e em outros sites.
Comum para operações baseadas em data / hora é o requisito de converter uma sequência de data / hora em segundos desde um determinado dia. Muito comum é 1970-01-01 00:00:00 UTC. Mas qualquer dia posterior também pode ser usado, dependendo do período necessário para dar suporte a uma tarefa específica.
Jay postou 7daysclean.cmd contendo uma solução rápida de "data para segundos" para o interpretador de linha de comando cmd.exe . Mas não leva em consideração os anos bissextos corretos. JR postou um complemento por considerar o dia bissexto no ano corrente, mas ignorando os outros anos bissextos desde o ano base, ou seja, desde 1970.
Uso há 20 anos tabelas estáticas (matrizes) criadas uma vez com uma pequena função C para obter rapidamente o número de dias, incluindo os dias bissextos de 01-01-2009 nas funções de conversão de data / hora em meus aplicativos escritos em C / C ++.
Esse método de tabela muito rápido também pode ser usado no código de lote usando o comando FOR . Por isso, decidi codificar a sub-rotina em lote GetSeconds
que calcula o número de segundos desde 1970-01-01 00:00:00 UTC para uma sequência de data / hora passada para essa rotina.
Nota: Os segundos bissextos não são levados em consideração, pois os sistemas de arquivos do Windows também não suportam segundos bissextos.
Primeiro, as tabelas:
Dias desde 1970-01-01 00:00:00 UTC para cada ano, incluindo os dias bissextos.
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
O cálculo dos segundos para o ano 2039 a 2106 com a época começando em 01-01-2009 só é possível com o uso de uma variável de 32 bits não assinada, ou seja, sem assinatura longa (ou sem assinatura int) em C / C ++.
Mas o cmd.exe usa para expressões matemáticas uma variável de 32 bits assinada. Portanto, o valor máximo é 2147483647 (0x7FFFFFFF), que é 2038-01-19 03:14:07.
Informações sobre o ano bissexto (Não / Sim) para os anos de 1970 a 2106.
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
Número de dias para o primeiro dia de cada mês no ano atual.
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
Converter uma data em um número de segundos desde 01-01-2009 é bastante fácil usando essas tabelas.
Atenção por FAVOR!
O formato das seqüências de data e hora depende das configurações de idioma e região do Windows. Os delimitadores e a ordem dos tokens atribuídos às variáveis de ambiente e Day
, no primeiro loop FOR, devem ser adaptados ao formato local de data / hora, se necessário.Month
Year
GetSeconds
É necessário adaptar a sequência de datas da variável de ambiente se o formato de data na variável de ambiente DATE for diferente do formato de data usado pelo comando FOR ativado %%~tF
.
Por exemplo, quando %DATE%
expande para Sun 02/08/2015
enquanto %%~tF
expande para 02/08/2015 07:38 PM
o código abaixo, pode ser usado com a modificação da linha 4 para:
call :GetSeconds "%DATE:~4% %TIME%"
Isso resulta na passagem para a sub-rotina just 02/08/2015
- a sequência de datas sem as três letras da abreviação do dia da semana e o caractere de espaço separador.
Como alternativa, pode-se usar o seguinte para passar a data atual no formato correto:
call :GetSeconds "%DATE:~-10% %TIME%"
Agora, os últimos 10 caracteres da sequência de datas são passados para a função GetSeconds
e, portanto, não importa se a sequência de datas da variável de ambiente DATE está com ou sem dia da semana, desde que dia e mês estejam sempre com 2 dígitos na ordem esperada, ou seja, no formato dd/mm/yyyy
ou dd.mm.yyyy
.
Aqui está o código do lote com a explicação dos comentários que apenas gera qual arquivo excluir e qual arquivo manter na C:\Temp
árvore de pastas, consulte o código do primeiro loop FOR .
@echo off
setlocal EnableExtensions EnableDelayedExpansion
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=" %%F in ('dir /A-D-H-S /B /S "C:\Temp"') do (
call :GetSeconds "%%~tF:0"
rem if !Seconds! LSS %LastWeek% del /F "%%~fF"
if !Seconds! LEQ %LastWeek% (
echo Delete "%%~fF"
) else (
echo Keep "%%~fF"
)
)
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 "%DateTime: AM=%" NEQ "%DateTime%" (
set "DateTime=%DateTime: AM=%"
) else if "%DateTime: PM=%" NEQ "%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%" EQU "0" ( if "%Month:~1%" NEQ "" set "Month=%Month:~1%" )
if "%Day:~0,1%" EQU "0" ( if "%Day:~1%" NEQ "" set "Day=%Day:~1%" )
if "%Hour:~0,1%" EQU "0" ( if "%Hour:~1%" NEQ "" set "Hour=%Hour:~1%" )
if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" )
if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" 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 2 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
Para um desempenho ideal, seria melhor remover todos os comentários, ou seja, todas as linhas começando com rem após 0-4 espaços à esquerda.
E as matrizes também podem ser menores, ou seja, diminuindo o período de 1980-01-01 00:00:00 para 2038-01-19 03:14:07 conforme atualmente suportado pelo código de lote acima, por exemplo, para 2015-01 -01 a 2019-12-31, como o código abaixo usa, que realmente exclui arquivos com mais de 7 dias na C:\Temp
árvore de pastas.
Além disso, o código do lote abaixo é otimizado para o formato de 24 horas.
@echo off
setlocal EnableDelayedExpansion
call :GetSeconds "%DATE:~-10% %TIME%"
set /A "LastWeek=Seconds-7*86400"
for /F "delims=" %%F in ('dir /A-D-H-S /B /S "C:\Temp"') do (
call :GetSeconds "%%~tF:0"
if !Seconds! LSS %LastWeek% del /F "%%~fF"
)
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%" EQU "0" ( if "%Month:~1%" NEQ "" set "Month=%Month:~1%" )
if "%Day:~0,1%" EQU "0" ( if "%Day:~1%" NEQ "" set "Day=%Day:~1%" )
if "%Hour:~0,1%" EQU "0" ( if "%Hour:~1%" NEQ "" set "Hour=%Hour:~1%" )
if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" )
if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" 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
Para obter mais informações sobre formatos de data e hora e comparações de horário de arquivo no Windows, consulte minha resposta em Descubra se o arquivo tem mais de 4 horas no arquivo em lotes com muitas informações adicionais sobre horários de arquivo.