Windows batch: formatted date into variable - windows

How do I save the current date in YYYY-MM-DD format into some variable in a Windows .bat file?
Unix shell analogue:
today=`date +%F`
echo $today

You can get the current date in a locale-agnostic way using
for /f "skip=1" %%x in ('wmic os get localdatetime') do if not defined MyDate set MyDate=%%x
Then you can extract the individual parts using substrings:
set today=%MyDate:~0,4%-%MyDate:~4,2%-%MyDate:~6,2%
Another way, where you get variables that contain the individual parts, would be:
for /f %%x in ('wmic path win32_localtime get /format:list ^| findstr "="') do set %%x
set today=%Year%-%Month%-%Day%
Much nicer than fiddling with substrings, at the expense of polluting your variable namespace.
If you need UTC instead of local time, the command is more or less the same:
for /f %%x in ('wmic path win32_utctime get /format:list ^| findstr "="') do set %%x
set today=%Year%-%Month%-%Day%

Use date /T to find the format on command prompt.
If the date format is Thu 17/03/2016 use like this:
set datestr=%date:~10,4%-%date:~7,2%-%date:~4,2%
echo %datestr%

If you wish to achieve this using standard MS-DOS commands in a batch file then you could use:
FOR /F "TOKENS=1 eol=/ DELIMS=/ " %%A IN ('DATE/T') DO SET dd=%%A
FOR /F "TOKENS=1,2 eol=/ DELIMS=/ " %%A IN ('DATE/T') DO SET mm=%%B
FOR /F "TOKENS=1,2,3 eol=/ DELIMS=/ " %%A IN ('DATE/T') DO SET yyyy=%%C
I'm sure this can be improved upon further but this gives the date into 3 variables for Day (dd), Month (mm) and Year (yyyy). You can then use these later in your batch script as required.
SET todaysdate=%yyyy%%mm%%dd%
echo %dd%
echo %mm%
echo %yyyy%
echo %todaysdate%
While I understand an answer has been accepted for this question this alternative method may be appreciated by many looking to achieve this without using the WMI console, so I hope it adds some value to this question.

Two more ways that do not depend on the time settings (both taken from How get data/time independent from localization). And both also get the day of the week and none of them requires admin permissions!:
MAKECAB - will work on EVERY Windows system (fast, but creates a small temporary file) (the foxidrive script):
#echo off
pushd "%temp%"
makecab /D RptFileName=~.rpt /D InfFileName=~.inf /f nul >nul
for /f "tokens=3-7" %%a in ('find /i "makecab"^<~.rpt') do (
set "current-date=%%e-%%b-%%c"
set "current-time=%%d"
set "weekday=%%a"
)
del ~.*
popd
echo %weekday% %current-date% %current-time%
pause
ROBOCOPY - it's not a native command for Windows XP and Windows Server 2003, but it can be downloaded from the Microsoft site. But it is built-in in everything from Windows Vista and above:
#echo off
setlocal
for /f "skip=8 tokens=2,3,4,5,6,7,8 delims=: " %%D in ('robocopy /l * \ \ /ns /nc /ndl /nfl /np /njh /XF * /XD *') do (
set "dow=%%D"
set "month=%%E"
set "day=%%F"
set "HH=%%G"
set "MM=%%H"
set "SS=%%I"
set "year=%%J"
)
echo Day of the week: %dow%
echo Day of the month : %day%
echo Month : %month%
echo hour : %HH%
echo minutes : %MM%
echo seconds : %SS%
echo year : %year%
endlocal
And three more ways that uses other Windows script languages. They will give you more flexibility e.g. you can get week of the year, time in milliseconds and so on.
JScript/BATCH hybrid (need to be saved as .bat). JScript is available on every system from Windows NT and above, as a part of Windows Script Host (though can be disabled through the registry it's a rare case):
#if (#X)==(#Y) #end /* ---Harmless hybrid line that begins a JScript comment
#echo off
cscript //E:JScript //nologo "%~f0"
exit /b 0
*------------------------------------------------------------------------------*/
function GetCurrentDate() {
// Today date time which will used to set as default date.
var todayDate = new Date();
todayDate = todayDate.getFullYear() + "-" +
("0" + (todayDate.getMonth() + 1)).slice(-2) + "-" +
("0" + todayDate.getDate()).slice(-2) + " " + ("0" + todayDate.getHours()).slice(-2) + ":" +
("0" + todayDate.getMinutes()).slice(-2);
return todayDate;
}
WScript.Echo(GetCurrentDate());
VBScript/BATCH hybrid (Is it possible to embed and execute VBScript within a batch file without using a temporary file?) same case as jscript , but hybridization is not so perfect:
:sub echo(str) :end sub
echo off
'>nul 2>&1|| copy /Y %windir%\System32\doskey.exe %windir%\System32\'.exe >nul
'& echo current date:
'& cscript /nologo /E:vbscript "%~f0"
'& exit /b
'0 = vbGeneralDate - Default. Returns date: mm/dd/yy and time if specified: hh:mm:ss PM/AM.
'1 = vbLongDate - Returns date: weekday, monthname, year
'2 = vbShortDate - Returns date: mm/dd/yy
'3 = vbLongTime - Returns time: hh:mm:ss PM/AM
'4 = vbShortTime - Return time: hh:mm
WScript.echo Replace(FormatDateTime(Date, 1), ", ", "-")
PowerShell - can be installed on every machine that has .NET - download from Microsoft (v1, v2, and v3 (only for Windows 7 and above)). Installed by default on everything form Windows 7/Win2008 and above:
C:\> powershell get-date -format "{dd-MMM-yyyy HH:mm}"
Self-compiled jscript.net/batch (I have never seen a Windows machine without .NET so I think this is a pretty portable):
#if (#X)==(#Y) #end /****** silent line that start jscript comment ******
#echo off
::::::::::::::::::::::::::::::::::::
::: Compile the script ::::
::::::::::::::::::::::::::::::::::::
setlocal
if exist "%~n0.exe" goto :skip_compilation
set "frm=%SystemRoot%\Microsoft.NET\Framework\"
:: searching the latest installed .net framework
for /f "tokens=* delims=" %%v in ('dir /b /s /a:d /o:-n "%SystemRoot%\Microsoft.NET\Framework\v*"') do (
if exist "%%v\jsc.exe" (
rem :: the javascript.net compiler
set "jsc=%%~dpsnfxv\jsc.exe"
goto :break_loop
)
)
echo jsc.exe not found && exit /b 0
:break_loop
call %jsc% /nologo /out:"%~n0.exe" "%~dpsfnx0"
::::::::::::::::::::::::::::::::::::
::: End of compilation ::::
::::::::::::::::::::::::::::::::::::
:skip_compilation
"%~n0.exe"
exit /b 0
****** End of JScript comment ******/
import System;
import System.IO;
var dt=DateTime.Now;
Console.WriteLine(dt.ToString("yyyy-MM-dd hh:mm:ss"));
Logman This cannot get the year and day of the week. It's comparatively slow, also creates a temp file and is based on the time stamps that logman puts on its log files.Will work everything from Windows XP and above. It probably will be never used by anybody - including me - but it is one more way...
#echo off
setlocal
del /q /f %temp%\timestampfile_*
Logman.exe stop ts-CPU 1>nul 2>&1
Logman.exe delete ts-CPU 1>nul 2>&1
Logman.exe create counter ts-CPU -sc 2 -v mmddhhmm -max 250 -c "\Processor(_Total)\%% Processor Time" -o %temp%\timestampfile_ >nul
Logman.exe start ts-CPU 1>nul 2>&1
Logman.exe stop ts-CPU >nul 2>&1
Logman.exe delete ts-CPU >nul 2>&1
for /f "tokens=2 delims=_." %%t in ('dir /b %temp%\timestampfile_*^&del /q/f %temp%\timestampfile_*') do set timestamp=%%t
echo %timestamp%
echo MM: %timestamp:~0,2%
echo dd: %timestamp:~2,2%
echo hh: %timestamp:~4,2%
echo mm: %timestamp:~6,2%
endlocal
exit /b 0
More information about the Get-Date function.

As per answer by #ProVi just change to suit the formatting you require
echo %DATE:~10,4%-%DATE:~7,2%-%DATE:~4,2% %TIME:~0,2%:%TIME:~3,2%:%TIME:~6,2%
will return
yyyy-MM-dd hh:mm:ss
2015-09-15 18:36:11
EDIT
As per #Jeb comment, whom is correct the above time format will only work if your DATE /T command returns
ddd dd/mm/yyyy
Thu 17/09/2015
It is easy to edit to suit your locale however, by using the indexing of each character in the string returned by the relevant %DATE% environment variable you can extract the parts of the string you need.
eg. Using %DATE~10,4% would expand the DATE environment variable, and then use only the 4 characters that begin at the 11th (offset 10) character of the expanded result
For example if using US styled dates then the following applies
ddd mm/dd/yyyy
Thu 09/17/2015
echo %DATE:~10,4%-%DATE:~4,2%-%DATE:~7,2% %TIME:~0,2%:%TIME:~3,2%:%TIME:~6,2%
2015-09-17 18:36:11

I really liked Joey's method, but I thought I'd expand upon it a bit.
In this approach, you can run the code multiple times and not worry about the old date value "sticking around" because it's already defined.
Each time you run this batch file, it will output an ISO 8601 compatible combined date and time representation.
FOR /F "skip=1" %%D IN ('WMIC OS GET LocalDateTime') DO (SET LIDATE=%%D & GOTO :GOT_LIDATE)
:GOT_LIDATE
SET DATETIME=%LIDATE:~0,4%-%LIDATE:~4,2%-%LIDATE:~6,2%T%LIDATE:~8,2%:%LIDATE:~10,2%:%LIDATE:~12,2%
ECHO %DATETIME%
In this version, you'll have to be careful not to copy/paste the same code to multiple places in the file because that would cause duplicate labels. You could either have a separate label for each copy, or just put this code into its own batch file and call it from your source file wherever necessary.

Just use the %date% variable:
echo %date%

It is possible to use PowerShell and redirect its output to an environment variable by using a loop.
From the command line (cmd):
for /f "tokens=*" %a in ('powershell get-date -format "{yyyy-MM-dd+HH:mm}"') do set td=%a
echo %td%
2016-25-02+17:25
In a batch file you might escape %a as %%a:
for /f "tokens=*" %%a in ('powershell get-date -format "{yyyy-MM-dd+HH:mm}"') do set td=%%a

This is an extension of Joey's answer to include the time and pad the parts with 0's.
For example, the result will be 2019-06-01_17-25-36. Also note that this is the UTC time.
for /f %%x in ('wmic path win32_utctime get /format:list ^| findstr "="') do set %%x
set Month=0%Month%
set Month=%Month:~-2%
set Day=0%Day%
set Day=%Day:~-2%
set Hour=0%Hour%
set Hour=%Hour:~-2%
set Minute=0%Minute%
set Minute=%Minute:~-2%
set Second=0%Second%
set Second=%Second:~-2%
set TimeStamp=%Year%-%Month%-%Day%_%Hour%-%Minute%-%Second%

Check this one..
for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a"
set "YY=%dt:~2,2%" & set "YYYY=%dt:~0,4%" & set "MM=%dt:~4,2%" & set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%" & set "Min=%dt:~10,2%" & set "Sec=%dt:~12,2%" & set "MS=%dt:~15,3%"
set "datestamp=%YYYY%%MM%%DD%" & set "timestamp=%HH%%Min%%Sec%" & set "fullstamp=%YYYY%-%MM%-%DD%_%HH%-%Min%-%Sec%-%MS%"
echo datestamp: "%datestamp%"
echo timestamp: "%timestamp%"
echo fullstamp: "%fullstamp%"
pause

I set an environment variable to the value in the numeric format desired by doing this:
FOR /F "tokens=1,2,3,4 delims=/ " %a IN ('echo %date%') DO set DateRun=%d-%b-%c

Due to date and time format is location specific info, retrieving them from %date% and %time% variables will need extra effort to parse the string with format transform into consideration. A good idea is to use some API to retrieve the data structure and parse as you wish. WMIC is a good choice. Below example use Win32_LocalTime. You can also use Win32_CurrentTime or Win32_UTCTime.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
for /f %%x in ('wmic path Win32_LocalTime get /format:list ^| findstr "="') do set %%x
set yyyy=0000%Year%
set mmmm=0000%Month%
set dd=00%Day%
set hh=00%Hour%
set mm=00%Minute%
set ss=00%Second%
set ts=!yyyy:~-4!-!mmmm:~-2!-!dd:~-2!_!hh:~-2!:!mm:~-2!:!ss:~-2!
echo %ts%
ENDLOCAL
Result:
2018-04-25_10:03:11

I am using the following:
set iso_date=%date:~6,4%-%date:~3,2%-%date:~0,2%
Or in combination with a logfile name 'MyLogFileName':
set log_file=%date:~6,4%-%date:~3,2%-%date:~0,2%-MyLogFileName

echo %DATE:~10,4%%DATE:~7,2%%DATE:~4,2%

If you have Python installed, you can do
python -c "import datetime;print(datetime.date.today().strftime('%Y-%m-%d'))"
You can easily adapt the format string to your needs.

If powershell is available, you can use codes below:
# get date
$BuildDate=(get-date -format "yyMMdd")
echo BuildDate=$BuildDate
# get time
$BuildTime=(get-date -format "hhmmss")
echo BuildTime=$BuildTime
Here is the result:
BuildDate=200518
BuildTime=115919

If you don't mind an one-time investment of 10 to 30 minutes to get a reliable solution (that doesn't depend on Windows' region settings), please read on.
Let's free our minds. Do you want to simplify the scripts to just look like this? (Assume you wants to set the LOG_DATETIME variable)
FOR /F "tokens=* USEBACKQ" %%F IN (`FormatNow "yyyy-MM-dd"`) DO (
Set LOG_DATETIME=%%F
)
echo I am going to write log to Testing_%LOG_DATETIME%.log
You can. Simply build a FormatNow.exe with C# .NET and add it to your PATH.
Notes:
You can use any Visual Studio edition, such as Visual Studio Express, to build the FormatNow.exe.
In Visual Studio, choose the "Console Application" C# project, not "Windows Forms Application" project.
Common sense: the built FormatNow.exe will need .NET Framework to run.
Common sense: after adding FormatNow.exe to PATH variable, you need to restart CMD to take effect. It also applies to any change in environment variables.
Benefits:
It's not slow (finishes within 0.2 seconds).
Many formats are supported https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx
e.g. FormatNow "ddd" to get only the day of week, FormatNow "yyyy" to get only the year
It doesn't depend on Windows' region settings, so its output is much more reliable. On the other hand, %date% doesn't give a consistent format over different computers, and is not reliable.
You don't need to create so many CMD variables and pollute the variable namespace.
It would require 3 lines in the batch script to invoke the program and get the results. It should be reasonably short enough.
Source code of FormatNow.exe which I built with Visual Studio 2010 (I prefer to build it myself to avoid the risk of downloading an unknown, possibly malicious program). Just copy and paste the codes below, build the program once, and then you have a reliable date formatter for all future uses.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace FormatNow
{
class Program
{
static void Main(string[] args)
{
try
{
if (args.Length < 1)
{
throw new ArgumentException("Missing format");
}
string format = args[0];
Console.Write(DateTime.Now.ToString(format, CultureInfo.InvariantCulture.DateTimeFormat));
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
In general, when dealing with complicated logics, we can make it simpler for by building a very small program and calling the program to capture the output back to a batch script variable instead. We are not students and we're not taking exams requiring us to follow the batch-script-only rule to solve problems. In real working environment, any (legal) method is allowed. Why should we still stick to the poor capabilities of Windows batch script that needs workarounds for many simple tasks? Why should we use the wrong tool for the job?

Related

In BAT file, compare files by modification time: "if file1 is-older-than file2 do"? [duplicate]

In DOS batch files, the method of achieving certain things is somewhat obfuscated. Fortunately, there is a fantastic reference site for batch scripting: Simon Sheppard's SS64. (The same site also has plenty of information about Bash.)
One difficulty is branching execution based on whether a directory is empty.
The obvious if exist "%dir%\*.*" doesn't work. But it can be done with this conditional execution trick:
( dir /b /a "%dir%" | findstr . ) > nul && (
echo %dir% non-empty
) || (
echo %dir% empty
)
Another awkward problem is branching according to file contents.
Again that can be done like this:
( fc /B "%file1%" "%file2%" | find "FC: no differences encountered" ) > nul && (
echo "%file1%" and "%file2%" are the same
) || (
echo "%file1%" and "%file2%" are different
)
So, my question is:
Is there a way to do branch according to the time-stamps of files?
This is the sort of thing I want:
REM *** pseudo-code!
if "%file1%" is_newer_than "%file2%" (
echo "%file1%" is newer
) else if "%file1%" is_older_than "%file2%" (
echo "%file2%" is newer
) else (
echo "%file1%" and "%file2%" are the same age
)
Thanks.
You can find the newer of two files with one line of batch script. Just list the files in date order, oldest first, which means the last file listed must be the newer file. So if you save the file name each time, the last name put in your variable will be the newest file.
For, example:
SET FILE1=foo.txt
SET FILE2=bar.txt
FOR /F %%i IN ('DIR /B /O:D %FILE1% %FILE2%') DO SET NEWEST=%%i
ECHO %NEWEST% is (probably) newer.
This unfortunately doesn't cope with the date stamps being the same. So we just need to check if the files have the same date and time stamp first:
SET FILE1=foo.txt
SET FILE2=bar.txt
FOR %%i IN (%FILE1%) DO SET DATE1=%%~ti
FOR %%i IN (%FILE2%) DO SET DATE2=%%~ti
IF "%DATE1%"=="%DATE2%" ECHO Files have same age && GOTO END
FOR /F %%i IN ('DIR /B /O:D %FILE1% %FILE2%') DO SET NEWEST=%%i
ECHO Newer file is %NEWEST%
:END
Dave Webb's soution while a great one will of course only work on files in the same directory.
Here is a solution that will work on any two files.
First get the file time (see How to get file's last modified date on Windows command line?).
for %%a in (MyFile1.txt) do set File1Date=%%~ta
for %%a in (MyFile2.txt) do set File2Date=%%~ta
However one has then to manually break the date and time into it's components since Cmd.exe will compare them as a sting thus 2 > 10 and 10:00AM > 2:00PM.
Compare first the years, then the months, then the day, then AM/PM, then the hour, and then the minute and second, (actually time consuming, but I don't have on the minute a better idea), see the final code at the end.
However this solution will not work if the files are in the same minute but different by the second.
If you are to this level of precision then get the filetime by using the "forfiles" command (see https://superuser.com/questions/91287/windows-7-file-properties-date-modified-how-do-you-show-seconds).
for /F "tokens=*" %%a in ('forfiles /m MyFile1.txt /c "cmd /c echo #fdate #ftime"')
do set File1Date=%%a
for /F "tokens=*" %%a in ('forfiles /m MyFile2.txt /c "cmd /c echo #fdate #ftime"')
do set File2Date=%%a
Note that "ForFiles" has a limitation that it can't take a path with spaces, so if you have a path with spaces you will have to change to that directory first, see forfiles - spaces in folder path
Comparison Code
:compareFileTime
set "originalFileTime=%1"
set "secondFileTime=%2"
for /F "tokens=1,2,3 delims= " %%a in (%originalFileTime%) do (
set "originalDatePart=%%a"
set "originalTimePart=%%b"
set "originalAmPmPart=%%c"
)
for /F "tokens=1,2,3 delims= " %%a in (%secondFileTime%) do (
set "secondDatePart=%%a"
set "secondTimePart=%%b"
set "secondAmPmPart=%%c"
)
for /F "tokens=1,2,3 delims=/" %%a in ("%originalDatePart%") do (
set "originalMonthPart=%%a"
set "originalMonthDayPart=%%b"
set "originalYearPart=%%c"
rem We need to ensure that the year is in a 4 digit format and if not we add 2000 to it
rem Cmd considers "50" > "100" but 50 < 100, so don't surround it with qoutes
if %%c LSS 100 set "originalYearPart=20%%c
)
for /F "tokens=1,2,3 delims=/" %%a in ("%secondDatePart%") do (
set "secondMonthPart=%%a"
set "secondMonthDayPart=%%b"
set "secondYearPart=%%c"
rem We need to ensure that the year is in a 4 digit format and if not we add 2000 to it
rem Cmd considers "50" > "100" but 50 < 100, so don't surround it with quotes
if %%c LSS 100 set "secondYearPart=20%%c
)
if %originalYearPart% GTR %secondYearPart% goto newer
if %originalYearPart% LSS %secondYearPart% goto older
rem We reach here only if the year is identical
rem Cmd considers "2" > "10" but 2 < 10, so don't surround it with quotes or you will have to set the width explicitly
if %originalMonthPart% GTR %secondMonthPart% goto newer
if %originalMonthPart% LSS %secondMonthPart% goto older
if %originalMonthDayPart% GTR %secondMonthDayPart% goto newer
if %originalMonthDayPart% LSS %secondMonthDayPart% goto older
rem We reach here only if it is the same date
if %originalAmPmPart% GTR %secondAmPmPart% goto newer
if %originalAmPmPart% LSS %secondAmPmPart% goto older
rem we reach here only if i=t is the same date, and also the same AM/PM
for /F "tokens=1 delims=:" %%a in ("%originalTimePart%") do set "originalHourPart=%%a"
for /F "tokens=1 delims=:" %%a in ("%secondTimePart%") do set "secondHourPart=%%a"
rem Cmd considers "2" > "10" but 2 < 10, so don't surround it with qoutes or you will have to set the width explicitly
if %originalHourPart% GTR %secondHourPart% goto newer
if %originalHourPart% LSS %secondHourPart% goto older
rem The minutes and seconds can be compared directly
if %originalTimePart% GTR %secondTimePart% goto newer
if %originalTimePart% LSS %secondTimePart% goto older
if %originalTimePart% EQU %secondTimePart% goto same
goto older
exit /b
:newer
echo "newer"
exit /b
:older
echo "older"
exit /b
:same
echo "same"
exit /b
seriously, you should start to learn something else. Its not a joke. DOS(cmd.exe) seriously lacks date manipulation capabilities and many more deficiencies. Here's the next better alternative natively provided besides DOS batch, vbscript
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objArgs = WScript.Arguments
strFile1 = objArgs(0)
strFile2 = objArgs(1)
Set objFile1 = objFS.GetFile(strFile1)
Set objFile2 = objFS.GetFile(strFile2)
If objFile1.DateLastModified < objFile2.DateLastModified Then
WScript.Echo "File1: "&strFile1&" is older than "&strFile2
Else
WScript.Echo "File1: "&strFile1&" is newer than "&strFile2
End If
run it on command line
C:\test>dir
Volume in drive C has no label.
Volume Serial Number is 08AC-4F03
Directory of C:\test
11/06/2009 07:40 PM <DIR> .
11/06/2009 07:40 PM <DIR> ..
11/06/2009 06:26 PM 135 file
11/02/2009 04:31 PM 4,516 m.txt
C:\test>cscript /nologo test.vbs file m.txt
File1: file is newer than m.txt
Of course, in newer versions of windows, you may want to try out Powershell...
I would use xcopy for this:
xcopy /L /D /Y PATH_TO_FILE1 PATH_TO_FILE2|findstr /B /C:"1 " && echo FILE1 is newer!
Because the xcopy always returns true in this case you need to filter its output with the findstr command.
The /L is for listing only, nothing is copied.
The /D is doing the time comparision. Hint: By swapping File1 and File2 you can decide how to handle identical files.
The /Y in only necessary to avoid an 'Overwrite existing' question.
That's all and it works with different paths.
Here's an easier solution. By concatenating the date parts as a single string from most significant to least significant, we can just do a simple string compare on the result.
In other words, comparing YYYYMMDDAMPMHHMM values will give the desired result without having to individually compare each segment of the date. This value is obtained by concatenating the various parts of the date string as extracted by the second FOR command.
call :getfiledatestr path\file1.txt file1time
call :getfiledatestr path\file2.txt file2time
if %file1time% equ %file2time% (
echo path\file1.txt is the same age as path\file2.txt to within a minute
) else if %file1time% lss %file2time% (
echo path\file1.txt is older than path\file2.txt
) else (
echo path\file1.txt is newer than path\file2.txt
)
goto :eof
#REM usage:
#REM :getfiledatestr file-path envvar
#REM result returned in %envvar%
:getfiledatestr
for %%f in (%1) do set getfiledatestr=%%~tf
#REM for MM/DD/YYYY HH:MM AMPM use call :appendpadded %2 %%c %%b %%a %%f %%d %%e
#REM for DD/MM/YYYY HH:MM AMPM use call :appendpadded %2 %%c %%b %%a %%f %%d %%e
#REM for YYYY/DD/MM HH:MM AMPM use call :appendpadded %2 %%a %%b %%c %%f %%d %%e
set %2=
for /f "tokens=1,2,3,4,5,6 delims=/: " %%a in ("%getfiledatestr%") do (
call :appendpadded %2 %%c %%b %%a %%f %%d %%e
)
#goto :eof
#REM Takes an env var as the first parameter
#REM and values to be appended as the remaining parameters,
#REM right-padding all values with leading 0's to 4 places
:appendpadded
set temp_value=000%2
call :expand set %1=%%%1%%%%temp_value:~-4%%
shift /2
if "%2" neq "" goto appendpadded
set temp_value=
#goto :eof
#REM forces all variables to expand fully
:expand
%*
#goto :eof
For one specific situation wherein you want to do something in the style of a Makefile, overwriting a destination file based on a source file only if the source file is newer, I came up with this hideous but simple method. This is only an option if you don't care in any way about the existing contents of a destination file that's older than the source file.
for /f "delims=" %%r in ('xcopy /D /Y /f /e "%inputdir%\%source_filename%" "%outputdir%\%dest_filename%"') do (
IF "%%r" EQU "1 File(s) copied" %build_command% "%inputdir%\%source_filename%" "%outputdir%\%dest_filename%"
)
What this does is, xcopy only overwrites the destination file if the origin file is newer. If it's not newer, %%r is "0 File(s) copied", so the conditional command doesn't execute, and the destination file is never overwritten. If it is newer, %%r is "1 File(s) copied", so your destination file is briefly a copy of the source file, then the build command gets executed, replacing it with a new version of whatever the destination file is actually supposed to be.
I should probably have just written a perl script.
(Note: you can also have xcopy handle the situation where the destination file doesn't initially exist, by putting an asterisk at the end of the destination filename; if you don't do that then xcopy isn't sure whether the destination is a filename or folder name and there is no flag to default the answer to filename.)
The freeware command line program WASFILE (https://www.horstmuc.de/wbat32.htm#wasfile) will also do a file date/time comparison for you.
Here's the quick program writeup from the web page:
*WasFile compares ..
.. time&date of two files (or directories),
.. the date of two files, time ignored
.. the date of a file with today-n (days)
.. time&date of a file with now-n (minutes)
Examples:
WasFile this.zip created before that.zip
WasFile this.zip modified after today-8
WasFile this.dat created before now-10
Using file stamps for created, modified (default) or accessed;
comparison: [not] before|after|sametime
Option to compare date only, ignore time: /DateLocal or /DateUTC*
Based on Wes answer I created updated script. The updates got too numerous to put into comments.
I added support for one more date format: a format including dots in dates. Added instructions about how to enable more date formats.
The Wes answer actually did not work since cmd is not able to compare integers larger than 1+31 bits, so I fixed that with converting the numeric strings to quoted numeric strings.
The script is able to consider missing files as oldest files.
Added support for second precision.
My version is below.
#REM usage:
#REM :getfiledatestr file-path envvar
#REM result returned in %envvar%
:getfiledatestr
for %%A in (%1) do (
set getfilefolderstr=%%~dpA
set getfilenamestr=%%~nxA
)
#REM Removing trailing backslash
if %getfilefolderstr:~-1%==\ set getfilefolderstr=%getfilefolderstr:~0,-1%
#REM clear it for case that the forfiles command fails
set getfiledatestr=
for /f "delims=" %%i in ('"forfiles /p ""%getfilefolderstr%"" /m ""%getfilenamestr%"" /c "cmd /c echo #fdate #ftime" "') do set getfiledatestr=%%i
#REM old code: for %%f in (%1) do set getfiledatestr=%%~tf
set %2=
#REM consider missing files as oldest files
if "%getfiledatestr%" equ "" set %2=""
#REM Currently supported date part delimiters are /:. and space. You may need to add more delimiters to the following line if your date format contains alternative delimiters too. If you do not do that then the parsed dates will be entirely arbitrarily structured and comparisons misbehave.
for /f "tokens=1,2,3,4,5,6 delims=/:. " %%a in ("%getfiledatestr%") do (
#REM for MM/DD/YYYY HH:MM:SS AMPM use call :appendpadded %2 %%c %%b %%a %%g %%d %%e %%f
#REM for DD/MM/YYYY HH:MM:SS AMPM use call :appendpadded %2 %%b %%c %%a %%g %%d %%e %%f
#REM for YYYY/DD/MM HH:MM:SS AMPM use call :appendpadded %2 %%a %%b %%c %%g %%d %%e %%f
call :appendpadded %2 %%c %%b %%a %%g %%d %%e %%f
)
#goto :eof
#REM Takes an env var as the first parameter
#REM and values to be appended as the remaining parameters,
#REM right-padding all values with leading 0's to 4 places
:appendpadded
set temp_value=000%2
call :expand set %1=%%%1%%%%temp_value:~-4%%
shift /2
if "%2" neq "" goto appendpadded
set temp_value=
#REM cmd is not able to compare integers larger than 1+31 bits, so lets convert them to quoted numeric strings instead. The current implementation generates numeric strings much longer than 31 bit integers worth.
call :expand set %1="%%%1%%%"
#goto :eof
#REM forces all variables to expand fully
:expand
%*
#goto :eof

How do I find the file creation date to the bat?

Tell me how you can find out the date the file was created in BAT.
We have a variable for %%~ti, but it only indicates the file's modification date. And how do you know it is the date the file was created?
#echo off
for /f "skip=5 tokens=1,2 delims= " %%a in ('dir /a-d /tc "%~1"') do (
echo Date: %%a, Time: %%b, File: %~1
exit /b 0
)
At least these three options exist, which are all scriptable.
There can be limitations depending on the OS version, and the language, and characters being used.
Powershell
WMIC
VBS script using WSH

Windows console width in environment variable

How can I get the current width of the windows console in an environment variable within a batch file?
I like the approach using the built-in mode command in Windows.
Try the following batch-file:
#echo off
for /F "usebackq tokens=2* delims=: " %%W in (`mode con ^| findstr Columns`) do set CONSOLE_WIDTH=%%W
echo Console is %CONSOLE_WIDTH% characters wide
Note that this will return the size of the console buffer, and not the size of the window (which is scrollable).
If you wanted the height of the windows console, you can replace Columns in the findstr expression with Lines. Again, it will return the height of the buffer, not the window... I personally like to have a big buffer to allow scrolling back through history, so for me the Lines usually reports about 3000 :)
Just for fun, here's a version that doesn't use findstr to filter the output... in case (for some reason) you have a dislike of findstr:
#echo off
for /F "usebackq tokens=1,2* delims=: " %%V in (`mode con`) do (
if .%%V==.Columns (
set CONSOLE_WIDTH=%%W
goto done
)
)
:done
echo Console is %CONSOLE_WIDTH% characters wide
Note, this was all tried in Windows XP SP3, in a number of different windows (including one executing FAR manager).
try this (language/locale/.net independent):
#ECHO OFF
SET "ConsoleWidth="
SET /A LINECOUNT=0
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "tokens=1,2,*" %%A IN ('mode con') DO (SET /A LINECOUNT=!LINECOUNT!+1&IF !LINECOUNT! EQU 4 SET ConsoleWidth=%%B)
SETLOCAL DISABLEDELAYEDEXPANSION
SET "LINECOUNT="
ECHO ConsoleWidth: %ConsoleWidth% characters
tested on Windows XP and Windows 7, both in Czech language
Powershell's (Get-Host).UI.RawUI.WindowSize property sets or returns the dimensions of the current console window. You can capture it with a for loop thusly:
for /f %%I in ('powershell ^(Get-Host^).UI.RawUI.WindowSize.width') do set width=%%I
Alright, here's one that doesn't require powershell to be installed. It composes, runs and deletes a .Net application to set a batch script variable. :)
#echo off
setlocal
pushd "%windir%\microsoft.net\"
for /f "delims=" %%I in ('dir /s /b csc.exe') do (
set csc=%%I
goto next
)
:next
popd
echo using System;>width.cs
echo class Width {>>width.cs
echo public static void Main() {>>width.cs
echo string m1 = "{0}";>>width.cs
echo Console.WriteLine^(m1, Console.WindowWidth^); } }>>width.cs
"%csc%" /out:width.exe width.cs >NUL 2>NUL
for /f %%I in ('width.exe') do set width=%%I
del width.exe width.cs
echo %width%
You can't get it in an environment variable, but it is stored in the registry so you can access it from your batch script.
There are answers here about how to change it:
How to change Screen buffer size in Windows Command Prompt from batch script
In a similar way you can use reg.exe QUERY [key details] rather than reg.exe ADD [details]. See the Technet documentation for HKCU\Console for details.
For a one simple line:
for /f tokens^=2 %%w in ('mode con^|find "Col"')do set _width=%%~w"

Format file date YYYYMMDD in batch

I've been working on some code in a batch file that evaluates two file dates. If one date is greater than the other then it runs another bat file. What I want to do is format the two dates as YYYYMMDD so that I can use the GTR (greater than).
The code is below but and it works if I use == (equal) because it's evaluating the string. I only want to know if one file date is greater than the other file date.
I'm not asking for someone to amend the code below but if you can show me how to format the dates I would be very grateful.
set Fileone=File1.txt
set FileTwo=File2.txt
pushd "D:\Board\Broadcast\FA_Report8_A"
FOR %%f IN (%FileOne%) DO SET filedatetime=%%~tf
FOR %%f IN (%FileTwo%) DO SET filedatetime2=%%~tf
SET filedatetime2=%year%%month%%day%
IF %filedatetime:~0, 10% GTR %filedatetime2:~0, 10% (
echo FileOne Greater - run bat
timeout /t 20 /nobreak
goto Finish
) else (
echo FileOne not Greater - Finish
goto Finish
)
:Finish
echo finished
pause
It's not portable between machines with different date formats but the simplest way is to use a substring: %var:~STARTPOS,LENGTH%
set filedatetime=14/06/2012 12:26
set filedatetime=%filedatetime:~6,4%%filedatetime:~3,2%%filedatetime:~0,2%
echo "%filedatetime%"
"20120614"
You can separate a date in its parts with a FOR /F command:
set filedatetime=14/06/2012 12:26
for /F "tokens=1-3 delims=/ " %%a in ("%filedatetime%") do (
set filedatetime=%%c%%b%%a
)
This form prevents you to make a mistake in the position or size of each substring, and is very easy to change the order of the parts. For example, if your date is MM/DD/YYYY:
set filedatetime=%%c%%a%%b
Type FOR /? for further details.
The layout for date variable strings in the system can be assumed by settings from the user, by regional and/or language, so, date is not 100% predictable layout to work with.
Try using wmic OS Get localdatetime /value because the result is 100% predictable:
LocalDateTime=20190609123708.733000-180
SO, if you use in for loop, adding 2 delimiters like =., (equal and dot), you go getting this string output:
20190609123708.
The layout from this command is predictable and works independent of regional settings, user settings or system language, so, the command will always return:
set _date=20190609123708
rem :: %_date:~0,4%_%_date:~4,2%_%_date:~6,2%
rem :: year:2019 | month:06 | day:09
In bat file:
#echo off & for /f "tokens=2delims==." %%i in ('wmic OS Get localdatetime /value ^|findstr /r [0-9]')do set "_date=%%i" & echo/%_date:~0,4%%_date:~4,2%%_date:~6,2%
In command line:
for /f "tokens=2delims==." %i in ('wmic OS Get localdatetime /value ^|findstr /r [0-9]')do set "_data=%i" & mkdir %_date:~0,4%_%_date:~4,2%_%_date:~6,2%
This commands result ::
20190609
May I can also suggest:
wmic Path Win32_LocalTime Get Day,Month,Year
In looping for:
#echo off & setlocal enabledelayedexpansion & set "_do=wmic Path Win32_LocalTime Get Day^,Month^,Year"
for /f "tokens=1-3delims= " %%a in ('!_do!^|findstr /r [0-9]')do set "y=%%c" & set "d=0%%a" & set "m=0%%b"
echo/!y!!m:~-2!!d:~-2! >nul
Result:
Day Month Year
9 6 2019
%%a %%b %%c
The difference is no zero in number for month/day less equal 9, so, you can use this bat to put leading zero in this case:
#echo off & setlocal enabledelayedexpansion & set "_do=wmic Path Win32_LocalTime Get Day^,Month^,Year"
for /f "tokens=1-3delims= " %%a in ('!_do!^|findstr /r [0-9]')do set "y=%%c" & set "d=0%%a" & set "m=0%%b"
echo/!y!!m:~-2!!d:~-2!
Result in YearMonthDay:
20190609
Obs.: In PowerShell, the layout can be customized simple by:
ToString("yyyyMMdd")
The strings can be set with ToString.
Sample: yyyy-MM-dd, dd-MM-yyyy, MM-dd-yyyy, MM_dd_yyyy, yyyy_MM_dd, etc..
The Powershell command:
$(Get-Date).ToString("yyyyMMdd")
Result:
2010609
See more about date variable layout output in batch here:
Safe way to get current day month and year in batch
Parsing Dates in Batch Files & Regional Settings / Locale
Update - How applying the comments/observations above in your bat, so, if I understood your code in this question:
#echo off & setlocal enabledelayedexpansion
set "Fileone=File1.txt" & set "FileTwo=File2.txt"
set "_both=!FileOne!-!FileTwo!" & rem cd /d "D:\Board\Broadcast\FA_Report8_A"
set "_path_back=%__CD__%" & rem :: you can use this "%__CD__%" or pushd "D:\Board\Broadcast\FA_Report8_A"
for /f "tokens=2delims==." %%i in ('wmic OS Get localdatetime /value ^|findstr /r [0-9]')do set "_date=%%i"
set _now=!_date:~0,4!!_date:~4,2!!_date:~6,2! & for /f "tokens=1*delims=-" %%i in ('echo/!_both!')do (
call :compare "%%~fi" & call :compare "%%~fj" & if !_dt_file_2! gtr !_dt_file_2! (
echo/ FileOne Greater - run bat & timeout /t 20 /nobreak & goto :run_bat
) else (echo/ FileOne not Greater - Finish & goto Finish)
)
:compare
set "_file=" & set "_file=%~1"
for /f "tokens=1delims=. " %%d in ('wmic datafile where name^='!_file:\=\\!' get LastModified ^|findstr /v "LastModified"')do (
if "!_dt_file_1!./" == "./" (set _dt_file_2=%%d) else (set _dt_file_2=%%d)
) & exit /b
:run_bat
call the_bat_to_run.cmd
:Finish
echo/Finished
So sorry by my limited English...

Generate unique file name with timestamp in batch script

In my .bat file I want to generate a unique name for files/directories based on date-time.
e.g.
Build-2009-10-29-10-59-00
The problem is that %TIME% won't do because it contains characters that are illegal in filename (e.g. :).
Is there something like tr in batch files?
Any other ideas how to solve this (that don't require extra command line utilities aside from the batch interpreter)?
EDIT: A better way of doing this is to take a date/time string that has a defined and unchanging format instead of using the locale-defined ones from %date% and %time%. You can use the following to get it:
for /f "skip=1" %%x in ('wmic os get localdatetime') do if not defined mydate set mydate=%%x
It yields something like 20120730203126.530000+120 and you can use that to construct your file names.
(Old answer below)
You can simply replace the offending character with an empty string:
echo %time::=%
The syntax %var:str1=str2% takes the environment variable (or pseudo-variable in case of %TIME% and replaces str1 by str2. If nothing follows after the equals sign then str1 is simply deleted.
In your specific case I think you'd want the following:
rem cut off fractional seconds
set t=%time:~0,8%
rem replace colons with dashes
set t=%t::=-%
set FileName=Build-%date%-%t%
A more brute-force way in case you don't know whether colons are used (but the order of the time would be the same):
set FileName=Build-%date%-%time:~0,2%-%time:~3,2%-%time:~6,2%
All preceding things, however, assume that you use standard ISO 8601 date format, i. e. 2009-10-29. I'd assume this as simply normal, but some people use other formats so be careful. But since you didn't ask about the date I was assuming you didn't have a problem there.
Following up on #Joey's and #Kees' answers to make them instantly usable.
On the command line:
FOR /f %a IN ('WMIC OS GET LocalDateTime ^| FIND "."') DO SET DTS=%a
SET DateTime=%DTS:~0,4%-%DTS:~4,2%-%DTS:~6,2%_%DTS:~8,2%-%DTS:~10,2%-%DTS:~12,2%
echo %DateTime%
In a BAT file:
#echo off
REM See http://stackoverflow.com/q/1642677/1143274
FOR /f %%a IN ('WMIC OS GET LocalDateTime ^| FIND "."') DO SET DTS=%%a
SET DateTime=%DTS:~0,4%-%DTS:~4,2%-%DTS:~6,2%_%DTS:~8,2%-%DTS:~10,2%-%DTS:~12,2%
echo %DateTime%
Example output:
2014-10-21_16-28-52
I use this to create a unique file name for the execution of the batch file.
REM ****Set up Logging ****
For /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%c-%%a-%%b)
For /f "tokens=1-2 delims=/:" %%a in ("%TIME%") do (set mytime=%%a%%b)
set mytime=%mytime: =0%
set Logname="PCCU_%mydate%_%mytime%.log"
Echo. >>%Logname% 2>>&1
Echo.=================== >>%Logname% 2>>&1
I had to add the line
set mytime=%mytime: =0%
because I had the same problem where a blank was being entered before 10 AM, now I get 09 instead of 9. I also reuse the %mydate% and %mytime% variable for other files that I create with this script so that they all have the same date time stamp.
This routine, actually only one line, works on every system set to any date or time format.
Output of line 1 is in format 20140725095527.710000+120
The actual date/time format you need is determined in line 2. You can format it however you want.
Just add the resulting DateTime variable to your filename ie. Filename_%DateTime%.log
::=======================================================================
::== CREATE UNIQUE DateTime STRING IN FORMAT YYYYMMDD-HHMMSS
::=======================================================================
FOR /f %%a IN ('WMIC OS GET LocalDateTime ^| FIND "."') DO SET DTS=%%a
SET DateTime=%DTS:~0,8%-%DTS:~8,6% | REM OUTPUT = 20140725-095527
I made this universal, Will work on any environment where date format may be different.
echo off
if not exist "C:\SWLOG\" mkdir C:\SWLOG
cd C:\SWLOG\
cmd /c "powershell get-date -format ^"{yyyyMMdd-HHmmss}^""> result.txt
REM echo %time% > result.txt
type result.txt > result1.txt
set /p filename=<result1.txt
echo %filename%
del C:\SWLOG\result.txt
del C:\SWLOG\result1.txt
To make the code in the previous example work, I needed to adjust slightly. I presume this is because my PC is using a UK date format. To get back "2014-04-19" in mydate I needed:
For /f "tokens=1-3 delims=/ " %%a in ('date /t') do (set mydate=%%c-%%b-%%a)
Below I've pasted my total script, and included an example of how to use the filename
For /f "tokens=1-3 delims=/ " %%a in ('date /t') do (set mydate=%%c-%%b-%%a)
For /f "tokens=1-2 delims=/:" %%a in ("%TIME%") do (set mytime=%%a%%b)
set mytime=%mytime: =0%
set Logname="c:\temp\LogFiles\MyLogFile_%mydate%_%mytime%.log"
Robocopy \\sourceserver\Music H:\MyBackups\Music /MIR /FFT /Z /XA:H /W:5 /np /fp /dcopy:T /unilog:%Logname% /tee
Hope this helps!
Datetime stamp:
#echo off
for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a"
set "YY=%dt:~2,2%" & set "YYYY=%dt:~0,4%" & set "MM=%dt:~4,2%" & set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%" & set "Min=%dt:~10,2%" & set "Sec=%dt:~12,2%"
rem set "datestamp=%YY%%MM%%DD%" & set "timestamp=%HH%%Min%%Sec%"
set "datestamp=%YYYY%%MM%%DD%"
set "timestamp=%HH%%Min%%Sec%"
set unique_number=%datestamp%%timestamp%
echo %unique_number%
If you can guarantee that the machine has a specific version of Python installed on it and accessible in the system's PATH, you can use this solution:
FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "import datetime; print
datetime.datetime.now().replace(microsecond=0).isoformat().replace(':', '-')"`) DO (
SET TIMESTAMP=%%F
)
ECHO %TIMESTAMP%
This will put the current local ISO-8601(ish) date representation into a %TIMESTAMP% variable and echo it out, like so:
2017-05-25T14:54:37
I've put replace(':', '-') after the isoformat() so that the resultant string can be used in a filename or directory, since : is a forbidden character in the Windows filesystem.

Resources