A menudo hay preguntas relacionadas con la fecha / hora relativa para resolver con un archivo por lotes. Pero el intérprete de línea de comandos cmd.exe no tiene función para los cálculos de fecha / hora. Ya se han publicado muchas buenas soluciones de trabajo que utilizan aplicaciones o scripts de consola adicionales, en otras páginas de Stack Overflow y en otros sitios web.
Común para operaciones basadas en fecha / hora es el requisito de convertir una cadena de fecha / hora a segundos desde un día determinado. Muy común es 1970-01-01 00:00:00 UTC. Pero cualquier día posterior también podría usarse dependiendo del rango de fechas requerido para respaldar una tarea específica.
Jay publicó 7daysclean.cmd que contiene una solución rápida de "fecha a segundos" para el intérprete de línea de comandos cmd.exe . Pero no tiene en cuenta los años bisiestos correctos. JR publicó un complemento para tener en cuenta el día bisiesto en el año actual, pero ignorando los otros años bisiestos desde el año base, es decir, desde 1970.
Utilizo desde hace 20 años tablas estáticas (matrices) creadas una vez con una pequeña función C para obtener rápidamente la cantidad de días, incluidos los días bisiestos desde 1970-01-01 en funciones de conversión de fecha / hora en mis aplicaciones escritas en C / C ++.
Este método de tabla muy rápido se puede usar también en código por lotes usando el comando FOR . Así que decidí codificar la subrutina por lotes GetSeconds
que calcula el número de segundos desde 1970-01-01 00:00:00 UTC para una cadena de fecha / hora pasada a esta rutina.
Nota: los segundos bisiestos no se tienen en cuenta ya que los sistemas de archivos de Windows tampoco admiten segundos bisiestos.
Primero, las tablas:
Días desde 1970-01-01 00:00:00 UTC para cada año, incluidos los días bisiestos.
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
El cálculo de los segundos para el año 2039 a 2106 con una época que comienza en 1970-01-01 solo es posible con el uso de una variable de 32 bits sin signo, es decir, sin signo largo (o sin signo int) en C / C ++.
Pero cmd.exe utiliza para expresiones matemáticas una variable de 32 bits con signo. Por lo tanto, el valor máximo es 2147483647 (0x7FFFFFFF) que es 2038-01-19 03:14:07.
Información del año bisiesto (No / Sí) para los años 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 días hasta el primer día de cada mes en el año actual.
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
Convertir una fecha en un número de segundos desde 1970-01-01 es bastante fácil usando esas tablas.
¡Atención por favor!
El formato de las cadenas de fecha y hora depende de la configuración de idioma e región de Windows. Los delimitadores y el orden de los tokens asignados a las variables de entorno Day
, Month
y Year
en el primer bucle FOR,GetSeconds
deben adaptarse al formato de fecha / hora local si es necesario.
Es necesario adaptar la cadena de fecha de la variable de entorno si el formato de fecha en la variable de entorno FECHA es diferente al formato de fecha utilizado por el comando FOR activado %%~tF
.
Por ejemplo, cuando se %DATE%
expande a Sun 02/08/2015
mientras se %%~tF
expande al 02/08/2015 07:38 PM
código siguiente, se puede usar modificando la línea 4 para:
call :GetSeconds "%DATE:~4% %TIME%"
Esto da como resultado pasar a la subrutina solo 02/08/2015
: la cadena de fecha sin las 3 letras de la abreviatura del día de la semana y el carácter de espacio de separación.
Alternativamente, lo siguiente podría usarse para pasar la fecha actual en el formato correcto:
call :GetSeconds "%DATE:~-10% %TIME%"
Ahora los últimos 10 caracteres de la cadena de fecha se pasan a la función GetSeconds
y, por lo tanto, no importa si la cadena de fecha de la variable de entorno DATE está con o sin día de la semana, siempre que el día y el mes siempre tengan 2 dígitos en el orden esperado, es decir, en formato dd/mm/yyyy
o dd.mm.yyyy
.
Aquí está el código de lote con comentarios explicativos que solo muestran qué archivo eliminar y qué archivo mantener en el C:\Temp
árbol de carpetas, vea el código del primer bucle 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 un rendimiento óptimo, sería mejor eliminar todos los comentarios, es decir, todas las líneas que comienzan con rem después de 0-4 espacios iniciales .
Y las matrices se pueden hacer también más pequeñas, es decir, disminuyendo el rango de tiempo desde 1980-01-01 00:00:00 hasta 2038-01-19 03:14:07 como lo admite actualmente el código de lote anterior, por ejemplo, a 2015-01 -01 a 2019-12-31 como se usa en el siguiente código, que realmente elimina archivos de más de 7 días en el C:\Temp
árbol de carpetas.
Además, el siguiente código de lote está optimizado para el 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 obtener aún más información sobre formatos de fecha y hora y comparaciones de tiempo de archivo en Windows, vea mi respuesta en Averiguar si el archivo tiene más de 4 horas en un archivo por lotes con mucha información adicional sobre los tiempos de archivo.