Find out if file is older than 4 hours in Batch fi

2020-01-23 04:01发布

Quite simply, how would I find out if a specific file in a folder is older than X hours? The file name and location will always be the same.

Thanks

4条回答
\"骚年 ilove
2楼-- · 2020-01-23 04:32

The solution to this will depend much on the expected time-range, since AFAIK you don't get time as one integer in a batch file without external programs. This means you have to parse the time value in your script, and the amount of parsing and the number of calculations needed, depend on the range you require it to work for. Here is one simple solution. It assumes that the file can NOT be older than 24hours.

set "filename=myfile.txt"
rem extract current date and time
for /f "tokens=1-5 delims=.:, " %%a in ("%date% %time%") do (
  set day=%%a&set mon=%%b&set yr=%%c&set hr=%%d&set min=%%e
)
rem extract file date and time
for /f "tokens=1-5 delims=.:, " %%a in ('"dir %filename%|find "%filename%""') do (
  set fday=%%a&set fmon=%%b&set fyr=%%c&set fhr=%%d&set fmin=%%e
)
rem calculate age of file (in minutes)
set /a "age=((hr*60+min)-(fhr*60+fmin)+(24*60))%%(24*60)"
set /a "max=4*60"
if %age% geq %max% echo.file is older than 4 hours
查看更多
Summer. ? 凉城
3楼-- · 2020-01-23 04:40

On comparing file times with batch code following must be taken into account:

  1. Date and time format settings
  2. File system of storage media
  3. Time zone, daylight saving time
  4. Automatic adjustment for daylight saving time
  5. Version of Windows

1. Date and time format settings

There are the environment variables DATE and TIME which on access (and not on start of batch execution) return current date and time in a format depending on Windows Region and Language settings. The country defines the date and time format.

It is even possible there to define for example for German short date format with DD.MM.YYYY (day and month with leading zero) and time format with HH:mm:ss (24 hour format with leading zero for hour, minute and second). But this does not mean that value of DATE and TIME are really using this format. For example TIME is in format H:mm:ss,ms when having selected a German country even with HH:mm:ss defined which means no leading zero on hour, but on minute and second, and additionally milliseconds after a comma in time string. The date string can be also with or without weekday depending on region settings.

And there is the pattern %~t to get last modification date and time of a directory or file. Run in a command prompt window call /? and for /? and read all help pages displayed for those two commands for details. The format of the date and time string returned by %~t as well as displayed on output of command DIR depends also on Windows region settings, but is usually not equal the format of variables DATE and TIME. The time of last modification time of a file or directory is usually returned without second value in date/time string.

Best is to find out with following batch file best executed before 10:00 AM what are the formats on current machine for current user.

@echo off
echo Current date/time: %DATE% %TIME%
for %%I in ("%~f0") do echo Batch file time:   %%~tI
pause

2. File system of storage media

On Windows file systems two storage formats are used for the file times:

  • On storage media using NTFS the NTFS precision time is used which is a 64-bit number of 100-nanosecond intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC).

  • On storage media using FAT, FAT16, FAT32 or exFAT the file times are stored using DOS Date Time format with two 16-bit values with a resolution of 2 seconds and using current local time.

This means an odd second is not possible for a file on FAT media, but on NTFS media. Copying a file on an NTFS drive with an odd second in last modification time to a FAT32 drive results in having identical files with 1 second difference in last modification time.

The file times on NTFS media are stored in UTC. So what is returned by %~t or command DIR as well as what is displayed for example in Windows Explorer depends on

  • current time zone,
  • daylight saving time for this time zone,
  • if current time is within daylight saving time period,
  • and if automatic adjustment for daylight saving time is enabled currently.

Changing any of those 4 parameters results in an immediate change of ALL displayed file times of files and directories on NTFS storage media.

File times on FAT media are more or less constant as using local time on creation, modification or last access, but read further for details.


3. Time zone, daylight saving time

As file times on NTFS media are stored in UTC, but displayed in local time, the current time zone and the daylight saving time for this time zone are important for file time comparisons, especially when done for files on NTFS and FAT32 drives.

But also for files on a FAT media, the time zone and daylight saving time settings could be important depending on version of Windows. Read below for details.


4. Automatic adjustment for daylight saving time

There is the setting for automatic adjustment for daylight saving time which becomes important when being enabled as by default and currently set time zone defines a daylight saving time adjustment.

For example daylight saving time is active currently in Europe. Disabling this option in clock settings of Windows results in an immediate change of the displayed file times on NTFS media by -1 hour, and also many files on FAT media depending on version of Windows.

This 1 hour time difference caused by the daylight saving time adjustment must be taken into account on comparing file times on NTFS and FAT media.


5. Version of Windows

NTFS is supported by all Windows NT versions. File times behavior is equal for files and directories on all Windows versions supporting NTFS.

But how file times on FAT media are interpreted depends on version of Windows. Prior Windows Vista the file times on FAT media are independent on change of time zone, daylight saving time setting and automatic adjustment of daylight saving time. Last modification date/time of a file is constant on Windows 2000 and Windows XP.

But on Windows Vista and later the displayed file times on FAT media depend on daylight saving time and automatic adjustment for daylight saving time. The time zone does not directly matter. Selecting another time zone as currently used does not change the displayed file times of files on FAT media as it does for files on NTFS media. But if current time is within daylight saving time period of the active time zone and the automatic adjustment for daylight saving time is enabled and the local time of the file or directory is within the daylight saving time period, +1 hour is added by Windows Vista and later. The files last modified on FAT drives within standard time are constant over the year; just the files last modified within DST period are displayed with or without +1 hour depending on current time with enabled automatic DST adjustment.

That different FAT file time management can be extremely confusing when having for example a public share of a folder on a FAT32 drive on a Windows 7 machine and being connected to this public folder with a Windows XP machine. A file last modified on 2015-09-19 17:39:08 stored on FAT32 drive on Windows 7 PC is displayed today by Windows 7 with German time zone with file time 19.09.2015 17:39:08, but in 2 months although not modified with 19.09.2015 16:39:08, and Windows XP displays same file on connected Windows 7 PC today and in future constant with file time 19.09.2015 17:39:08.

Comparing file times of files stored in archives (ZIP, RAR, ...) with files on NTFS or FAT media can be really a nightmare.


6. Comparing file time with current time

For this task comparing last modification time of a file with current time, it should be enough to just take date and time format settings into account.

The batch code below uses code explained in detail in my answer on Batch file to delete files older than N days. This explanation should be read before using the code below.

Depending on date/time format of the variables DATE and TIME and date/time string returned by %~tI for the file on local Windows PC, it might be necessary to make small modifications to code. Appropriate hints are given in the comment lines of batch code.

@echo off
setlocal EnableExtensions

rem Get seconds since 1970-01-01 for current date and time.
rem From date string only the last 10 characters are passed to GetSeconds
rem which results in passing dd/mm/yyyy or dd.mm.yyyy in expected format
rem to this subroutine independent on date string of environment variable
rem DATE is with or without abbreviated weekday at beginning.
call :GetSeconds "%DATE:~-10% %TIME%"

rem Subtract seconds for 4 hours (4 * 3600 seconds) from seconds value.
set /A "CompareTime=Seconds-4*3600"

rem Define batch file itself as the file to compare time by default.
set "FileToCompareTime=%~f0"

rem If batch file is started with a parameter and the parameter
rem specifies an existing file (or directory), compare with last
rem modification date of this file.
if not "%~1" == "" (
    if exist "%~1" set "FileToCompareTime=%~1"
)

rem Get seconds value for the specified file.
for %%F in ("%FileToCompareTime%") do call :GetSeconds "%%~tF:0"

rem Compare the two seconds values.
if %Seconds% LSS %CompareTime% (
    echo File %FileToCompareTime% was last modified for more than 4 hours.
) else (
    echo File %FileToCompareTime% was last modified within the last 4 hours.
)
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 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

rojo added information on how date and time format settings, file system and Windows version can be ignored by using wmic - the Windows Management Instrumentation command-line utility.

Using wmic has the advantage of fixed format for the date/time strings and therefore the batch code works on all Windows computers without adaptations to local date/time format.

Also version of Windows does not matter on using wmic because of internal usage of GetFileTime function (most likely).

The file system becomes only important on evaluating also minutes offset of local time / file time to UTC. Command wmic returns on FAT drives just +*** for the minutes offset to UTC while the minutes offset is correct for the last modification file time of a file on an NTFS drive.

Example for same file on NTFS and FAT32 drive:

NTFS:  20071011192622.000000+120
FAT32: 20071011192622.000000+***

+120 is sum of +60 minutes for CET (Central European Time) and +60 minutes for active daylight saving time, or in other words +120 minutes for CEST (Central European Summer Time).

Therefore the batch code below does not evaluate the minutes offset to UTC which is not needed on comparing last modification file time of a file with current local time.

Further the file name must be specified always with full path in wmic command line. Just specifying the file name for a file in current directory or a file name with relative path does not work. And each backslash must be escaped with one more backslash in file name with path.

The main disadvantage on using wmic is the speed. The batch code above needs on my computer about 50 ms to finish according to Process Monitor log (average of several runs). The batch code below calling wmic twice needs for the same task about 1500 ms to finish (also an average value). Therefore using wmic on a large number of files is definitely not a good idea if it can be avoided because local date/time format is known.

@echo off
setlocal EnableExtensions

rem Get seconds since 1970-01-01 for current date and time.
for /F "tokens=2 delims==." %%T in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do call :GetSeconds %%T

rem Subtract seconds for 4 hours (4 * 3600 seconds) from seconds value.
set /A "CompareTime=Seconds-4*3600"

rem Define batch file itself as the file to compare time by default.
set "FileToCompareTime=%~f0"

rem If batch file is started with a parameter and the parameter
rem specifies an existing file (or directory), compare with last
rem modification date of this file.
if not "%~1" == "" (
    if exist "%~f1" set "FileToCompareTime=%~f1"
)

rem Get seconds value for the specified file.
set "DataFile=%FileToCompareTime:\=\\%"
for /F "usebackq tokens=2 delims==." %%T in (`%SystemRoot%\System32\wbem\wmic.exe DATAFILE where "name='%DataFile%'" GET LastModified /VALUE`) do echo call :GetSeconds %%T

rem Compare the two seconds values.
if %Seconds% LSS %CompareTime% (
    echo File %FileToCompareTime% was last modified for more than 4 hours.
) else (
    echo File %FileToCompareTime% was last modified within the last 4 hours.
)
endlocal
goto :EOF


rem Date/time format used by the command line utility of Windows
rem Management Instrumentation is always YYYYMMDDHHmmss with a
rem dot and a 6 digit microsecond value and plus/minus 3 digit
rem time offset to UTC in minutes independent on region and
rem language settings. Microsecond is always 000000 for a file
rem time. The minutes offset including time zone offset and
rem current daylight saving time offset is returned for a file
rem time only for a file on an NTFS drive. Minutes offset is
rem +*** for a file on a FAT drive (at least on Windows XP).

:GetSeconds
rem Get year, month, day, hour, minute and second from first parameter.
set "DateTime=%~1"
set "Year=%DateTime:~0,4%"
set "Month=%DateTime:~4,2%"
set "Day=%DateTime:~6,2%"
set "Hour=%DateTime:~8,2%"
set "Minute=%DateTime:~10,2%"
set "Second=%DateTime:~12,2%"
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 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
查看更多
萌系小妹纸
4楼-- · 2020-01-23 04:40

Alternatively you could create a simple program using C# for example like that:

// compile using c:\Windows\Microsoft.NET\Framework\v3.5\csc FileByAge.cs
using System;
using System.IO;

public static class Program
{
  public static void Main(string[] args)
  {
    double age = double.Parse(args[0]);
    string fileName;
    while ((fileName = Console.ReadLine()) != null)
    {
      if(age >= (DateTime.Now - new FileInfo(fileName).CreationTime).TotalHours)
      {
        continue;
      }
      Console.WriteLine(fileName);    
    }
  }
}

That program could then be used to find all files that are older than 2 hours within a batch file like that:

for /F "delims=" %f in ('dir /B *.txt^|FileByAge.exe 2') do echo %f

An improved and self-compiling version

As mentioned by @Paul one could even build a hacky hybrid batch / c# file that will compile and run itself:

@echo off
setlocal
set CsFile=%TEMP%\%~n0.cs
set ExeFile=%TEMP%\%~n0.exe
pushd %SystemRoot%\Microsoft.NET\Framework
for /F %%f in ('dir /B /O:-N v*') do cd %%f & goto :BreakLoop;  
:BreakLoop
(echo /* & type "%~f0") > "%CsFile%"
csc.exe /nologo /out:"%ExeFile%" "%CsFile%" 
del "%CsFile%"
popd
more | "%ExeFile%" %*
del "%ExeFile%"
endlocal
goto:eof
*/

using System;
using System.IO;

public static class Program
{
    public static void Main(string[] args)
    {
        var olderThen = args[0].Contains("older");
        var targetAge = double.Parse(args[1]);
        string fileName;
        while ((fileName = Console.ReadLine()) != null)
        {
            if (string.Empty == fileName)
            {
                continue;
            }
            var fileAge = (DateTime.Now - new FileInfo(fileName).CreationTime).TotalHours;
            if (olderThen ? targetAge > fileAge : targetAge < fileAge)
            {
                continue;
            }
            Console.WriteLine(fileName);
        }
    }
}

And can be used like this:

for /F "delims=" %f in ('dir /B *.txt^|FileByAge.bat older 2') do echo %f
查看更多
我只想做你的唯一
5楼-- · 2020-01-23 04:44

Pure batch is horrible at date math. What happens if your script runs shortly after midnight? What if the time check spans a Daylight Savings Time change?

I propose invoking Windows Script Host and borrowing from a language that better handles date / time math. Here's a hybrid batch / JScript script that'll compute the age of a file or directory pretty easily. Save it with a .bat extension and give it a shot.

@if (@CodeSection==@Batch) @then

@echo off
setlocal

set "file=C:\Path\To\File.ext"

for /f %%I in ('cscript /nologo /e:JScript "%~f0" "%file%"') do (
    rem // value contained in %%I is in minutes.

    if %%I gtr 240 (

        echo %file% is over 4 hours old.

        rem // do stuff here to take whatever actions you wish
        rem // if the file is over 4 hours old.
    )
)

goto :EOF

@end // end batch portion / begin JScript hybrid chimera

var fso = new ActiveXObject("scripting.filesystemobject"),
    arg = WSH.Arguments(0),
    file = fso.FileExists(arg) ? fso.GetFile(arg) : fso.GetFolder(arg);

// Use either DateCreated, DateLastAccessed, or DateLastModified.
// See http://msdn.microsoft.com/en-us/library/1ft05taf%28v=vs.84%29.aspx
// for more info.

var age = new Date() - file.DateLastModified;
WSH.Echo(Math.floor(age / 1000 / 60));

For more information on batch / JScript hybrid scripts, see this GitHub page. The style used above is similar to Hybrid.bat on that page. This answer is based on another. For what it's worth, PowerShell is also good at handling date math; but WSH executes much more quickly.

查看更多
登录 后发表回答