Batch file gives different values based on machine? [duplicate] - windows

I am compressing files using WinZip on the command line. Since we archive on a daily basis, I am trying to add date and time to these files so that a new one is auto generated every time.
I use the following to generate a file name. Copy paste it to your command line and you should see a filename with a Date and Time component.
echo Archive_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2%%time:~6,2%.zip
Output
Archive_20111011_ 93609.zip
However, my issue is AM vs PM. The AM time stamp gives me time 9 (with a leading blank space) vs. 10 naturally taking up the two spaces.
I guess my issue will extend to the first nine days, first 9 months, etc. as well.
How do I fix this so that leading zeroes are included instead of leading blank spaces so I get Archive_20111011_093609.zip?

Another solution:
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetime=%%I
It will give you (independent of locale settings!):
20130802203023.304000+120
( YYYYMMDDhhmmss.<milliseconds><always 000>+/-<minutes difference to UTC> )
From here, it is easy:
set datetime=%datetime:~0,8%-%datetime:~8,6%
20130802-203023
For Logan's request for the same outputformat for the "date-time modified" of a file:
for %%F in (test.txt) do set file=%%~fF
for /f "tokens=2 delims==" %%I in ('wmic datafile where name^="%file:\=\\%" get lastmodified /format:list') do set datetime=%%I
echo %datetime%
It is a bit more complicated, because it works only with full paths, wmic expects the backslashes to be doubled and the = has to be escaped (the first one. The second one is protected by surrounding quotes).

Extract the hour, look for a leading space, if found replace with a zero;
set hr=%time:~0,2%
if "%hr:~0,1%" equ " " set hr=0%hr:~1,1%
echo Archive_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%hr%%time:~3,2%%time:~6,2%.zip

You should search; you can simply replace all spaces with zero set hr=%hr: =0% – jeb Oct 11 '11 at 14:16
So I did:
set hr=%time:~0,2%
set hr=%hr: =0%
Then use %hr% inside whatever string you are formatting to always get a two-digit hour.
(Jeb's comment under the most popular answer worked the best for me and is the simplest. I repost it here to make it more obvious for future users.)

As Vicky already pointed out, %DATE% and %TIME% return the current date and time using the short date and time formats that are fully (endlessly) customizable.
One user may configure its system to return Fri040811 08.03PM while another user may choose 08/04/2011 20:30.
It's a complete nightmare for a BAT programmer.
Changing the format to a firm format may fix the problem, provided you restore back the previous format before leaving the BAT file. But it may be subject to nasty race conditions and complicate recovery in cancelled BAT files.
Fortunately, there is an alternative.
You may use WMIC, instead. WMIC Path Win32_LocalTime Get Day,Hour,Minute,Month,Second,Year /Format:table returns the date and time in a invariable way. Very convenient to directly parse it with a FOR /F command.
So, putting the pieces together, try this as a starting point...
SETLOCAL enabledelayedexpansion
FOR /F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') DO (
SET /A FD=%%F*1000000+%%D*100+%%A
SET /A FT=10000+%%B*100+%%C
SET FT=!FT:~-4!
ECHO Archive_!FD!_!FT!.zip
)

I found the best solution for me, after reading all your answers:
set t=%date%_%time%
set d=%t:~10,4%%t:~7,2%%t:~4,2%_%t:~15,2%%t:~18,2%%t:~21,2%
echo hello>"Archive_%d%"
If AM I get 20160915_ 150101 (with a leading space and time).
If PM I get 20160915_2150101.

#For /F "tokens=1,2,3,4 delims=/ " %%A in ('Date /t') do #(
Set DayW=%%A
Set Day=%%B
Set Month=%%C
Set Year=%%D
Set All=%%D%%B%%C
)
"C:\Windows\CWBZIP.EXE" "c:\transfer\ziptest%All%.zip" "C:\transfer\MB5L.txt"
This takes MB5L.txt and compresses it to ziptest20120204.zip if run on 4 Feb 2012

You can add leading zeroes to a variable (value up to 99) like this in batch:
IF 1%Var% LSS 100 SET Var=0%Var%
So you'd need to parse your date and time components out into separate variables, treat them all like this, then concatenate them back together to create the file name.
However, your underlying method for parsing date and time is dependent on system locale settings. If you're happy for your code not to be portable to other machines, that's probably fine, but if you expect it to work in different international contexts then you'll need a different approach, for example by reading out the registry settings:
HKEY_CURRENT_USER\Control Panel\International\iDate
HKEY_CURRENT_USER\Control Panel\International\iTime
HKEY_CURRENT_USER\Control Panel\International\iTLZero
(That last one controls whether there is a leading zero on times, but not dates as far as I know).

From the answer above, I have made a ready-to-use function.
Validated with french local settings.
:::::::: PROGRAM ::::::::::
call:genname "my file 1.txt"
echo "%newname%"
call:genname "my file 2.doc"
echo "%newname%"
echo.&pause&goto:eof
:::::::: FUNCTIONS :::::::::
:genname
set d1=%date:~-4,4%
set d2=%date:~-10,2%
set d3=%date:~-7,2%
set t1=%time:~0,2%
::if "%t1:~0,1%" equ " " set t1=0%t1:~1,1%
set t1=%t1: =0%
set t2=%time:~3,2%
set t3=%time:~6,2%
set filename=%~1
set newname=%d1%%d2%%d3%_%t1%%t2%%t3%-%filename%
goto:eof

As others have already pointed out, the date and time formats of %DATE% and %TIME% (as well as date /T and time /T) are locale-dependent, so extracting the current date and time is always a nightmare, and it is impossible to get a solution that works with all possible formats since there are hardly any format limitations.
But there is another problem with a code like the following one (let us assume a date format like MM/DD/YYYY and a 12 h time format like h:mm:ss.ff ap where ap is either AM or PM and ff are fractional seconds):
rem // Resolve AM/PM time:
set "HOUR=%TIME:~,2%"
if "%TIME:~-2%" == "PM" if %HOUR% lss 12 set /A "HOUR+=12"
if "%TIME:~-2%" == "AM" if %HOUR% equ 12 set /A "HOUR-=12"
rem // Left-zero-pad hour:
set "HOUR=0%HOUR%"
rem // Build and display date/time string:
echo %DATE:~-4,4%%DATE:~0,2%%DATE:~3,2%_%HOUR:~-2%%TIME:~3,2%%TIME:~6,2%
Each instance of %DATE% and %TIME% returns the date or time value present at the time of its expansion, therefore the first %DATE% or %TIME% expression might return a different value than the following ones (you can prove that when echoing a long string containing a huge amount of such, preferrably %TIME%, expressions).
You could improve the aforementioned code to hold a single instance of %DATE% and %TIME% like this:
rem // Store current date and time once in the same line:
set "CURRDATE=%DATE%" & set "CURRTIME=%TIME%"
rem // Resolve AM/PM time:
set "HOUR=%CURRTIME:~,2%"
if "%CURRTIME:~-2%" == "PM" if %HOUR% lss 12 set /A "HOUR+=12"
if "%CURRTIME:~-2%" == "AM" if %HOUR% equ 12 set /A "HOUR-=12"
rem // Left-zero-pad hour:
set "HOUR=0%HOUR%"
rem // Build and display date/time string:
echo %CURRDATE:~-4,4%%CURRDATE:~0,2%%CURRDATE:~3,2%_%HOUR:~-2%%CURRTIME:~3,2%%CURRTIME:~6,2%
But still, the returned values in %DATE% and %TIME% could reflect different days when executed at midnight.
The only way to have the same day in %CURRDATE% and %CURRTIME% is this:
rem // Store current date and time once in the same line:
set "CURRDATE=%DATE%" & set "CURRTIME=%TIME%"
rem // Resolve AM/PM time:
set "HOUR=%CURRTIME:~,2%"
if "%CURRTIME:~-2%" == "PM" if %HOUR% lss 12 set /A "HOUR+=12"
if "%CURRTIME:~-2%" == "AM" if %HOUR% equ 12 set /A "HOUR-=12"
rem // Fix date/time midnight discrepancy:
if not "%CURRDATE%" == "%DATE%" if %CURRTIME:~0,2% equ 0 set "CURRDATE=%DATE%"
rem // Left-zero-pad hour:
set "HOUR=0%HOUR%"
rem // Build and display date/time string:
echo %CURRDATE:~-4,4%%CURRDATE:~0,2%%CURRDATE:~3,2%_%HOUR:~-2%%CURRTIME:~3,2%%CURRTIME:~6,2%
Of course the occurrence of the described problem is quite improbable, but at one point it will happen and cause strange unexplainable failures.
The described problem cannot occur with the approaches based on the wmic command as described in the answer by user Stephan and in the answer by user PA., so I strongly recommend to go for one of them. The only disadvantage of wmic is that it is way slower.

Your question seems to be solved, but ...
I'm not sure if you take the right solution for your problem.
I suppose you try to compress each day the actual project code.
It's possible with ZIP and 1980 this was a good solution, but today you should use a repository system, like subversion or git or ..., but not a zip-file.
Ok, perhaps it could be that I'm wrong.

I realise this is a moot question to the OP, but I just brewed this, and I'm a tad proud of myself for thinking outside the box.
Download gawk for Windows at http://gnuwin32.sourceforge.net/packages/gawk.htm .... Then it's a one liner, without all that clunky DOS batch syntax, where it takes six FOR loops to split the strings (WTF? That's really really BAD MAD AND SAD! ... IMHO of course)
If you already know C, C++, Perl, or Ruby then picking-up AWK (which inherits from the former two, and contributes significantly to the latter two) is a piece of the proverbial CAKE!!!
The DOS Batch command:
echo %DATE% %TIME% && echo %DATE% %TIME% | gawk -F"[ /:.]" "{printf(""""%s%02d%02d-%02d%02d%02d\n"""", $4, $3, $2, $5, $6, $7);}"
Prints:
Tue 04/09/2012 10:40:38.25
20120904-104038
Now that's not quite the full story... I'm just going to be lazy and hard-code the rest of my log-file-name in the printf statement, because it's simple... But if anybody knows how to set a %NOW% variable to AWK's output (yeilding the guts of a "generic" now function) then I'm all ears.
EDIT:
A quick search on Stack Overflow filled in that last piece of the puzzle, Batch equivalent of Bash backticks.
So, these three lines of DOS batch:
echo %DATE% %TIME% | awk -F"[ /:.]" "{printf(""""%s%02d%02d-%02d%02d%02d\n"""", $4, $3, $2, $5, $6, $7);}" >%temp%\now.txt
set /p now=<%temp%\now.txt
echo %now%
Produce:
20120904-114434
So now I can include a datetime in the name of the log-file produced by my SQL Server installation (2005+) script thus:
sqlcmd -S .\SQLEXPRESS -d MyDb -e -i MyTSqlCommands.sql >MyTSqlCommands.sql.%now%.log
And I'm a happy camper again (except life was still SOOOOO much easier on Unix).

I prever to use this over the current accepted answer from Stephan as it makes it possible to configure the timestamp using named parameters after that:
for /f %%x in ('wmic path win32_utctime get /format:list ^| findstr "="') do set %%x
It will provide the following parameters:
Day
DayOfWeek
Hour
Milliseconds
Minute
Month
Quarter
Second
WeekInMonth
Year
You can then configure your format like so:
SET DATE=%Year%%Month%%Day%

So you want to generate date in format YYYYMMDD_hhmmss.
As %date% and %time% formats are locale dependant you might need more robust ways to get a formatted date.
Here's one option:
#if (#X)==(#Y) #end /*
#cscript //E:JScript //nologo "%~f0"
#exit /b %errorlevel%
#end*/
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) +
("0" + todayDate.getSeconds()).slice(-2) ;
WScript.Echo(todayDate);
and if you save the script as jsdate.bat you can assign it as a value :
for /f %%a in ('jsdate.bat') do #set "fdate=%%a"
echo %fdate%
or directly from command prompt:
for /f %a in ('jsdate.bat') do #set "fdate=%a"
Or you can use powershell which probably is the way that requires the less code:
for /f %%# in ('powershell Get-Date -Format "yyyyMMdd_HHmmss"') do set "fdate=%%#"

Adding other options to this list of answers.
you could have replaced empty space with a 0 something like echo %time: =0%
but that is still dependent, move that code to a buddy's PC in some other random place and you'll get funny outputs. So you can incorporate powershell's Get-Date:
for /f "tokens=*" %%i in ('PowerShell -Command "Get-Date -format 'yyyymmdd_HHmmss'"') do echo %%i.zip"

A space is legal in file names. If you put your path and file name in quotes, it may just fly. Here's what I'm using in a batch file:
svnadmin hotcopy "C:\SourcePath\Folder" "f:\DestPath\Folder%filename%"
It doesn't matter if there are spaces in %filename%.

Related

How perform arithmetic (add,subtract) between two Date&Time timestamps?

I am trying to create an arbitrary time using batch files. I am trying to subtract set values (such as 1 day, 1 month, and 2000 years) from my code to display the system time subtracted by the set values stated previously. For the hours and minutes, I am subtracting 10 hours and 10 minutes (shown in code as different values). I am also aware that date and time are variables related to my system. ~ Please ignore
Important -
My problem now is with this part:
if %minb% EQU 60 set /a minb=%minb%-60 & set /a hourb=%hourb%+1
if %hourb% EQU 24 set /a hourb=%hourb%-24 & set /a dayb=%dayb%+1
if %dayb% EQU 30 set /a dayb=%dayb%-30 & set /a monthb=%monthb%+1
if %monthb% EQU 12 set /a monthb=%monthb%-12 & set /a yearb=%yearb%+1
I can't seem to be able to make the minutes reset to 0 and have the hours add 1 when the minute value hits 60, and same with the hour-day, day-month, month-year relationships, other than that, the problems with my code are mostly fixed. When I run the code and wait for the minutes (or add to the time difference) to reach 60, they just pass 60 and don't reset nor add to the hoursb value.
.
My date and time formats are:
Short date: M/d/yyyy
Long date: dddd, MMMM d, yyyy
Short time: h:mm
Long time: h:mm:ss
I am using Windows 10.
Please note that the values in the set /a do not match my description, but of course they can be changed around. They are set to the shown numbers for test purposes only.
EDIT:
Code:
#echo off
:start
set day=%date:~-7,2%
set month=%date:~-10,2%
set year=%date:~-4,4%
set hour=%time:~0,2%
set min=%time:~3,2%
set /a "hourb=%hour%-10"
set /a "minb=%min%+30"
set /a "dayb=%day%-1"
set /a "monthb=%month%-1"
set /a "yearb=%year%-2000"
if %minb% lss 10 set minb=0%minb%
if %hourb% lss 10 set hourb=0%hourb%
if %dayb% lss 10 set dayb=0%dayb%
if %monthb% lss 10 set monthb=0%monthb%
if %minb% EQU 60 set /a minb=%minb%-60 & set /a hourb=%hourb%+1
if %hourb% EQU 24 set /a hourb=%hourb%-24 & set /a dayb=%dayb%+1
if %dayb% EQU 30 set /a dayb=%dayb%-30 & set /a monthb=%monthb%+1
if %monthb% EQU 12 set /a monthb=%monthb%-12 & set /a yearb=%yearb%+1
cls
:: ———————————————————————–
#mode con cols=20 lines=6
title Arbitrary Clock
:time
echo.
echo. %hourb%:%minb%
echo.
echo. %dayb%-%monthb%-%yearb%
echo.
ping -n 2 0.0.0.0 >nul
cls
goto :start
Alright, so as you can see in the new edit to the code, I set the values to the stated at the beginning of this question. Subtracting 10 from the hours, subtracting 1 from the days and months, and subtracting 2000 from the years. I added 30 to the minutes to show the problem right now.
So my system time is 4:40, 1/5/2019. Alright?
The displayed time on the clock is 06:70, 04-00-19.
The desired outcome is 07:10, 04-12-18.
This was edited heavily. Thanks in advance.
You have a fundamental error in your description. When you subtract a number of days/months/years from a date, you are subtracting a date, not a number! For example, if today date is 05/01/2019 (obviously in DD/MM/YYYY format) and you want to subtract 1 month, the result should be 05/12/2018 because you are subtracting a date (1 month). If you subtract a number 1 from the month part, then the result will be 05/00/2019. For this reason, this problem should be solved via the well-known methods to add and subtract dates.
The problem with these methods is that they are valid just for a certain range of dates, and that ranges usually start in 1583. If you want to subtract 2000 from year 2018 and get 18, then such "year 18" is not a valid year in the usual date management methods. For this reason, besides to use the usual methods to manage dates, you also need to complete a couple small adjustments.
I taken the code segments linked in my comment and modified they slightly in order to solve this problem. I completed some tests and it seems to work correctly, but perhaps the method may fail in certain specific range of values.
#echo off
setlocal EnableDelayedExpansion
rem Subtract a number of Days/Months/Years # Hours:Minutes:Seconds from a datetime stamp
rem Antonio Perez Ayala aka Aacini
rem Define the "Date in DDMMYYYY format" To "Julian Day Number" conversion "function"
set "DateToJDN(Date)=( a=1Date, y=a%%10000, a/=10000, m=a%%100, d=a/100-100, a=(m-14)/12, (1461*(y+4800+a))/4+(367*(m-2-12*a))/12-(3*((y+4900+a)/100))/4+d-32075 )"
set /P "stamp1=Enter base timestamp as DD/MM/YYYY HH:MM:SS "
set /P "stamp2=Enter timestamp to subtract in same format: "
set "adjust=0"
for /F "tokens=1-4" %%a in ("%stamp1% %stamp2%") do set "date1=%%a" & set "time1=%%b" & set "date2=%%c" & set "time2=%%d"
if 1%date2:~-4% lss 11600 (
set /A "adjust=1%date2:~-4%+1600"
set "date2=%date2:~0,-4%!adjust:~1!"
set "adjust=1600"
)
set /A "days=!DateToJDN(Date):Date=%date1:/=%! - !DateToJDN(Date):Date=%date2:/=%!, days1600=%DateToJDN(Date):Date=00001600%"
set /A "ss=(((1%time1::=-100)*60+1%-100) - (((1%time2::=-100)*60+1%-100)"
if %ss% lss 0 set /A "ss+=60*60*24, days-=1"
set /A "hh=ss/3600+100, ss%%=3600, mm=ss/60+100, ss=ss%%60+100"
if %days% lss %days1600% set /A days+=days1600, days1600=0
set /A "l=days+68569,n=(4*l)/146097,l=l-(146097*n+3)/4,i=(4000*(l+1))/1461001,l=l-(1461*i)/4+31,j=(80*l)/2447,D=l-(2447*j)/80+100,l=j/11,M=j+2-(12*l)+100,Y=100*(n-49)+i+l+10000+adjust"
if %days1600% equ 0 set /A Y-=1600
echo -----------------------
echo Result: %D:~1%/%M:~1%/%Y:~1% %hh:~1%:%mm:~1%:%ss:~1%
A small example:
Enter base timestamp as DD/MM/YYYY HH:MM:SS 05/01/2019 10:53:45
Enter timestamp to subtract in same format: 01/01/2000 10:10:00
-----------------------
Result: 04/12/0018 00:43:45
PS - I really would like to see a PowerShell solution for this problem. It seems that it would take just a simple and short line of code...
A simpler windows batch option is to call a PowerShell command from within your batch script to manipulate past and future dates.
Below, lines 8 and 9 create and format your arbitrary date.
:: Set the below 3 variables to generate an Arbitrary Clock
:: Use a negative number to subtract (-7), positive number to add (7)
set Altered_Days=-7
set Altered_Months=-10
set Altered_Years=-4
:: The script will merge days and years to determine the arbitrary date and print the Arbitrary date to the screen
set /a Altered_Days=(%Altered_Years%*356)+%Altered_Days%
powershell -command "(((Get-date).AddDays(%Altered_Days%)).AddMonths(%Altered_Months%)).ToString('HH:mm, MM-dd-yyyy')"
:: If you want to capture the value in a variable, use the below line
powershell -command "(((Get-date).AddDays(%Altered_Days%)).AddMonths(%Altered_Months%)).ToString('HH:mm, MM-dd-yyyy')">captureVar && set /p Arbitrary_Clock=<captureVar
echo Arbitrary Clock Prediction && echo %Arbitrary_Clock%
This will enable your batch script to create the end result you are looking for on your Windows 10 OS.
One of the great things about PowerShell is that it can easily be called from the cmd line and a batch script. To do so, enter
powershell -command "the powershell command(s)"
If the PowerShell command calls for quotes, you must use a single quote to avoid ending the PowerShell session. To escape characters, follow batch scripting rules. Batch variables can be read in the PowerShell command, but values created in PowerShell must be redirected for later use in your batch script. This can be done as in line 12 above, or by using PowerShell's Set-Content command to write to a file. I frequently use PowerShell in my batch scripts to manipulate file data.
powershell -command "(Get-Content 'file.txt') -replace 'string','' | Select-String -Pattern 'keep lines with string' -SimpleMatch) -replace 'another string','to somthing different' | Set-Content 'file.txt'"
In the case of adding or subtracting time, PowerShell makes the task easy. And it doesn't require you to create a different script. You can write everything else as batch, use PowerShell commands you find helpful, and save the windows' script as batch.
For detailed explanations on how Get-Date and addDays work, reference:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-date?view=powershell-6
and
https://devblogs.microsoft.com/scripting/adding-and-subtracting-dates-with-powershell/

Get local time from different Timezone using batchfile

I do not want to use any scripting language, but just plain command line commands/scripting.
I can solve this using a (prepared) TZ table (e.g. format: UTC+06:00 + 'TZ name'), then use TZUTIL to collect their resp UTC values and calculate the Timezone's local time (preferably taking DST into account).
I am just hoping that somebody already did this in a more "proper" way. Searched this/other sites, but found nothing so far.
ps: no 'external' tools/etc, since the final batch-file needs to be distributed EMEA-wise.
get the local date and time (wmic)
get the local offset (wmic)
convert the local time to UTC with the help of #2
get the offset of a TZ. (tzutil /l)
convert the result of #3 and #4 not to mention the change of date (this is where it gets complicated).
I think I have not forgotten something? :)
I had a need to obtain time zone information in a specific format from MS Windows systems via a batch file. I could not find what I needed so I developed my own. Thought I would share for others to use. Use the code and adjust as necessary.
#echo off
REM Obtain the ActiveBias value and convert to decimal
for /f "tokens=3" %%a in ('reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation /v ActiveTimeBias ^| grep -i "ActiveTimeBias"') do set /a abias=%%a
for /f "tokens=3-8" %%n in ('reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation /v TimeZoneKeyName') do set tzn=%%n %%o %%p %%q %%r 2>NUL
REM Set the + or - sign variable to reflect the timezone offset
IF "%abias:~0,1%"=="-" (set si=+) ELSE (set si=-)
for /f "tokens=1 delims=-" %%t in ('echo %abias%') do set tzc=%%t
REM Calculate to obtain floating points (decimal values)
set /a tzd=100*%tzc%/60
REM Calculate the active bias to obtain the hour
set /a tze=%tzc%/60
REM Set the minutes based on the result of the floating point calculation
IF "%tzd%"=="0" (set en=00 && set si=)
IF "%tzd:~1%"=="00" (set en=00) ELSE IF "%tzd:~2%"=="00" (set en=00 && set tz=%tzd:~0,2%)
IF "%tzd:~1%"=="50" (set en=30) ELSE IF "%tzd:~2%"=="50" (set en=30 && set tz=%tzd:~0,2%)
IF "%tzd:~1%"=="75" (set en=45) ELSE IF "%tzd:~2%"=="75" (set en=45 && set tz=%tzd:~0,2%)
REM Adding a 0 to the beginning of a single digit hour value
IF %tze% LSS 10 (set tz=0%tze%)
REM Display timezone name and offset
echo %tzn% %si%%tz%%en%

Howto batch-convert pdf's using ImageMagick and benchmark the process?

After fiddling around for the first time with the command-line and ImageMagic, I have been able to do the following:
c:\test\paper.pdf contains a pdf file
c:\test>convert paper.pdf output-%d.tiff
The pdf file contains five pages, and the output is as expected 5 tiff files :-) Now I want to put multiple files in c:\test and loop through them, creating pages based on the original filename. So assume the following files in c:\test:
paper.pdf (5 pages)
example.pdf (2 pages)
new.pdf (1 page)
The output of a batch script should be 8 tiff files in the c:\test\tiffs\ folder:
paper-0.tiff paper-1.tiff paper-2.tiff paper-3.tiff paper-4.tiff
example-0.tiff example-1.tiff
new-0.tiff
and I would very much like the command line return the time it took converting the pdf's to tiffs:
c:\test>convert.bat
c:\test>This action took 120 seconds
I'm trying to write this batch file but since it's my first try I'm having trouble. I am first trying to loop through the test folder and create all the tiffs (leaving timing the process for now):
for %%f in (C:\test\) do convert %%f.pdf %%f-%%d.tiff
I would have expected this to work but got an error:
C:\Users\Me>convert c:\test\c:\test\.pdf c:\test\c:\test\-%d.tiff convert.exe:
unable to open image `c:\test\c:\test\.pdf': Invalid argument #
error/blob.c/OpenBlob/2646. convert.exe: no decode delegate for this image
format `c:\test\c:\test\.pdf' # error/constitute.c/ReadImage/552. convert.exe:
no images defined `c:\test\c:\test\-%d.tiff' #
error/convert.c/ConvertImageCommand/3106.
What am I doing wrong?
Timing
This is one of those jobs that it is just wrong to do (for real—it's OK to do it to show off) with batch/CMD. Nearly every programming language I've ever encountered would be better at doing this than CMD.
This particular (hackish) implementation will not work if the elapsed time goes past midnight of any given day, and depending on how you have dates configured in your locale, it might break if the run starts before 1 P.M. and finishes after 1 P.M.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "tokens=5 delims= " %%t IN ('^<NUL time') DO SET START_TIME=%%t
:: Stuff that you're timing.
FOR /F "tokens=5 delims= " %%t IN ('^<NUL time') DO SET END_TIME=%%t
SET ELAPSED=
FOR /F "delims=:. tokens=1,2,3,4" %%a IN ("%START_TIME%") DO (
FOR /F "delims=:. tokens=1,2,3,4" %%n IN ("%END_TIME%") DO (
SET /A HOURS=%%n-%%a
SET /A MINUTES=%%o-%%b
SET /A SECONDS=%%p-%%c
SET /A HUND=%%q-%%d
SET /A TOTAL=!HOURS!*3600 + !MINUTES!*60 + !SECONDS!
IF !HUND! LSS 0 (
SET /A TOTAL=!TOTAL!-1
SET /A HUND=!HUND!+100
)
IF !HUND! LSS 10 SET HUND=0!HUND!
SET ELAPSED=!TOTAL!.!HUND!
)
)
#ECHO.
#ECHO.
#ECHO This action took %ELAPSED% seconds.
ImageMagick
The problem I see with your convert command is that you've not told CMD what pattern to search for. It sounds like you want *.pdf. Doing this will mean that you don't need to append ".pdf" to the end of the input file name and need to remove the extension when specifying the output file name. The %%~dpnf produces the entire path to the file without the extension.
FOR %%f IN (C:\temp\*.pdf) do convert "%%~f" "%%~dpnf-%%d.tiff"
EDIT: To put the PDF's in a different location, change the %%f substitution:
FOR %%f IN (C:\temp\*.pdf) do convert "%%~f" "\path\to\tiffs\%%~nf-%%d.tiff"
Better Method?
I guess I should at least put in a plug for doing it the Right™ way. If you didn't want to invoke your command from a script in a language that supported timing (like Python), you could just retrieve the system time as an integer. This strategy will work until roughly 2038. I used Perl in this example.
#ECHO OFF
FOR /F %%t IN ('perl -e "print(time());"') DO SET START_TIME=%%t
:: stuff to time
FOR /F %%t IN ('perl -e "print(time());"') DO SET END_TIME=%%t
SET /A ELAPSED=%END_TIME%-%START_TIME%
#ECHO This step took %ELAPSED% seconds.

Batch file setting variable in if/else statement in For Loop

I'm trying to create a batch file that generates a custom AVIsynth script per each file. Right now the batch file is set to execute from within the folder where the video files exist. What I need to do is get the creation time of the file to generate a timecode burn in. I have no problem getting the info I need. However, if the file was created in the afternoon I need it to be in 24hr time. For example, 2pm needs to display as 14.
I have a working if statement that creates a newth variable that adds 12 if need be. However, if it doesn't need it the variable persists. On each subsequent iteration of the loop the variable doesn't change.
My example. I have two files the first was created at 2pm the other at 12pm. The 2pm file is read first and the newth variable becomes 14. So far so good. On the next file the newth variable should become 12 but instead remains 14. How do I fix this?
#Echo Off & CLS
SetLocal EnableDelayedExpansion
For /F %%a In ('dir *.mpg /b') Do (
ECHO Processing "%%a"
echo %%~ta
set time=%%~ta
set th=!time:~11,2!
set tm=!time:~14,2!
set era=!time:~17,2!
echo !era!
if "!era!"=="PM" (
if !th! LSS 12 ( set /a newth=!th!+12 )
) else ( set /a newth=!th!)
echo !newth!
echo //AviSynth Test Script >scripts/%%a.avs
echo DirectshowSource^("%%~fa"^)>>scripts/%%a.avs
echo LanczosResize^(720,400^) >>scripts/%%a.avs
echo ShowSMPTE^(^) >>scripts/%%a.avs
ECHO Back to Console
Pause
)
It's a little messy because I've been using echo for debugging. But hopefully the problem is clear.
Here is a method with Wmic - Wmic is in XP pro and above.
#Echo Off & CLS
SetLocal EnableDelayedExpansion
For /F "delims=" %%a In ('dir *.mpg /b') Do (
ECHO Processing "%%a"
set "file=%cd%\%%a"
set "file=!file:\=\\!"
WMIC DATAFILE WHERE name="!file!" get creationdate|find ".">file.tmp
for /f %%a in (file.tmp) do set dt=%%a
set tm=!dt:~8,2!:!dt:~10,2!:!dt:~12,2!
del file.tmp
echo !tm!
echo //AviSynth Test Script >scripts/%%a.avs
echo DirectshowSource^("%%~fa"^)>>scripts/%%a.avs
echo LanczosResize^(720,400^) >>scripts/%%a.avs
echo ShowSMPTE^(^) >>scripts/%%a.avs
ECHO Back to Console
Pause
)
There are a few problems with your code. The major one is this sequence
if "!era!"=="PM" (
if !th! LSS 12 ( set /a newth=!th!+12 )
) else ( set /a newth=!th!)
With your first filetime "02:xx PM"
th=02, era=PM, so set /a newth=02+12 sets newth=14
With your second filetime "12:xx PM"
th=12, era=PM, so - do nothing, since there's no else action for !th! LSS 12
Hence, newth remains at 14.
So - what's the fix? Since you don't use newth further, we can't say for certain, but it appears you want 24-hour format - 4 digit hhmm.
DANGER, Will Robinson moment number 1:
You are dealing with numbers starring LEADING ZEROES. All well and good except where the value is 08 or 09, which batch bizarrely interprets as OCTAL since it begins 0.
DANGER, Will Robinson moment number 2:
set /a will suppress leading zeroes, so set /a newth=!th! will set newth to 7 for time 07:36 AM - not 07...
So - how to overcome all this?
IF !th!==12 SET th=00
SET th=!th: =0!
if "!era!"=="PM" (set /a newth=1!th!+12
SET newth=!newth:~-2!
) else ( set newth=!th!)
This forces 12 AM to 00 AM and 12 PM to 00 PM
Then replace any spaces with 0 (in case you have leading spaces, not zeroes)
Then, if era is PM, add 100 by stringing 1 before the 2-digit hour number, add 12 and grab the last 2 characters
Otherwise, just use the number in th
Unfortunately, made a little more complicated since you haven't told us whether you use or don't use leading zeroes in your time format. Nevertheless, the incomplete original calculation method is at fault.
DANGER, Will Robinson moment number 3:
time is a MAGIC VARIABLE - and you know what happened to Mickey when he got involved in things better left alone.
If you set time in a batch, then %time% or !time! will return the value you set. If you don't set it, then the value returned will be the system time. Same goes for DATE and a number of similar values (see set /? from the prompt - there's a list at the end)
here's how you can get the time stamp with seconds:
C:\>powershell -command "& {(gwmi -query """select * from cim_datafile where name = 'C:\\file.txt' """).lastModified;}"
C:\>powershell -command "& {(gwmi -query """select * from cim_datafile where name = 'D:\\other.txt' """).creationdate;}"
I've tried with WMIC but still cannot get the time stamp.As you are using Win7 you should have powershell installed by default.

Batch command date and time in file name

I am compressing files using WinZip on the command line. Since we archive on a daily basis, I am trying to add date and time to these files so that a new one is auto generated every time.
I use the following to generate a file name. Copy paste it to your command line and you should see a filename with a Date and Time component.
echo Archive_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2%%time:~6,2%.zip
Output
Archive_20111011_ 93609.zip
However, my issue is AM vs PM. The AM time stamp gives me time 9 (with a leading blank space) vs. 10 naturally taking up the two spaces.
I guess my issue will extend to the first nine days, first 9 months, etc. as well.
How do I fix this so that leading zeroes are included instead of leading blank spaces so I get Archive_20111011_093609.zip?
Another solution:
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetime=%%I
It will give you (independent of locale settings!):
20130802203023.304000+120
( YYYYMMDDhhmmss.<milliseconds><always 000>+/-<minutes difference to UTC> )
From here, it is easy:
set datetime=%datetime:~0,8%-%datetime:~8,6%
20130802-203023
For Logan's request for the same outputformat for the "date-time modified" of a file:
for %%F in (test.txt) do set file=%%~fF
for /f "tokens=2 delims==" %%I in ('wmic datafile where name^="%file:\=\\%" get lastmodified /format:list') do set datetime=%%I
echo %datetime%
It is a bit more complicated, because it works only with full paths, wmic expects the backslashes to be doubled and the = has to be escaped (the first one. The second one is protected by surrounding quotes).
Extract the hour, look for a leading space, if found replace with a zero;
set hr=%time:~0,2%
if "%hr:~0,1%" equ " " set hr=0%hr:~1,1%
echo Archive_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%hr%%time:~3,2%%time:~6,2%.zip
You should search; you can simply replace all spaces with zero set hr=%hr: =0% – jeb Oct 11 '11 at 14:16
So I did:
set hr=%time:~0,2%
set hr=%hr: =0%
Then use %hr% inside whatever string you are formatting to always get a two-digit hour.
(Jeb's comment under the most popular answer worked the best for me and is the simplest. I repost it here to make it more obvious for future users.)
As Vicky already pointed out, %DATE% and %TIME% return the current date and time using the short date and time formats that are fully (endlessly) customizable.
One user may configure its system to return Fri040811 08.03PM while another user may choose 08/04/2011 20:30.
It's a complete nightmare for a BAT programmer.
Changing the format to a firm format may fix the problem, provided you restore back the previous format before leaving the BAT file. But it may be subject to nasty race conditions and complicate recovery in cancelled BAT files.
Fortunately, there is an alternative.
You may use WMIC, instead. WMIC Path Win32_LocalTime Get Day,Hour,Minute,Month,Second,Year /Format:table returns the date and time in a invariable way. Very convenient to directly parse it with a FOR /F command.
So, putting the pieces together, try this as a starting point...
SETLOCAL enabledelayedexpansion
FOR /F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') DO (
SET /A FD=%%F*1000000+%%D*100+%%A
SET /A FT=10000+%%B*100+%%C
SET FT=!FT:~-4!
ECHO Archive_!FD!_!FT!.zip
)
I found the best solution for me, after reading all your answers:
set t=%date%_%time%
set d=%t:~10,4%%t:~7,2%%t:~4,2%_%t:~15,2%%t:~18,2%%t:~21,2%
echo hello>"Archive_%d%"
If AM I get 20160915_ 150101 (with a leading space and time).
If PM I get 20160915_2150101.
#For /F "tokens=1,2,3,4 delims=/ " %%A in ('Date /t') do #(
Set DayW=%%A
Set Day=%%B
Set Month=%%C
Set Year=%%D
Set All=%%D%%B%%C
)
"C:\Windows\CWBZIP.EXE" "c:\transfer\ziptest%All%.zip" "C:\transfer\MB5L.txt"
This takes MB5L.txt and compresses it to ziptest20120204.zip if run on 4 Feb 2012
You can add leading zeroes to a variable (value up to 99) like this in batch:
IF 1%Var% LSS 100 SET Var=0%Var%
So you'd need to parse your date and time components out into separate variables, treat them all like this, then concatenate them back together to create the file name.
However, your underlying method for parsing date and time is dependent on system locale settings. If you're happy for your code not to be portable to other machines, that's probably fine, but if you expect it to work in different international contexts then you'll need a different approach, for example by reading out the registry settings:
HKEY_CURRENT_USER\Control Panel\International\iDate
HKEY_CURRENT_USER\Control Panel\International\iTime
HKEY_CURRENT_USER\Control Panel\International\iTLZero
(That last one controls whether there is a leading zero on times, but not dates as far as I know).
From the answer above, I have made a ready-to-use function.
Validated with french local settings.
:::::::: PROGRAM ::::::::::
call:genname "my file 1.txt"
echo "%newname%"
call:genname "my file 2.doc"
echo "%newname%"
echo.&pause&goto:eof
:::::::: FUNCTIONS :::::::::
:genname
set d1=%date:~-4,4%
set d2=%date:~-10,2%
set d3=%date:~-7,2%
set t1=%time:~0,2%
::if "%t1:~0,1%" equ " " set t1=0%t1:~1,1%
set t1=%t1: =0%
set t2=%time:~3,2%
set t3=%time:~6,2%
set filename=%~1
set newname=%d1%%d2%%d3%_%t1%%t2%%t3%-%filename%
goto:eof
As others have already pointed out, the date and time formats of %DATE% and %TIME% (as well as date /T and time /T) are locale-dependent, so extracting the current date and time is always a nightmare, and it is impossible to get a solution that works with all possible formats since there are hardly any format limitations.
But there is another problem with a code like the following one (let us assume a date format like MM/DD/YYYY and a 12 h time format like h:mm:ss.ff ap where ap is either AM or PM and ff are fractional seconds):
rem // Resolve AM/PM time:
set "HOUR=%TIME:~,2%"
if "%TIME:~-2%" == "PM" if %HOUR% lss 12 set /A "HOUR+=12"
if "%TIME:~-2%" == "AM" if %HOUR% equ 12 set /A "HOUR-=12"
rem // Left-zero-pad hour:
set "HOUR=0%HOUR%"
rem // Build and display date/time string:
echo %DATE:~-4,4%%DATE:~0,2%%DATE:~3,2%_%HOUR:~-2%%TIME:~3,2%%TIME:~6,2%
Each instance of %DATE% and %TIME% returns the date or time value present at the time of its expansion, therefore the first %DATE% or %TIME% expression might return a different value than the following ones (you can prove that when echoing a long string containing a huge amount of such, preferrably %TIME%, expressions).
You could improve the aforementioned code to hold a single instance of %DATE% and %TIME% like this:
rem // Store current date and time once in the same line:
set "CURRDATE=%DATE%" & set "CURRTIME=%TIME%"
rem // Resolve AM/PM time:
set "HOUR=%CURRTIME:~,2%"
if "%CURRTIME:~-2%" == "PM" if %HOUR% lss 12 set /A "HOUR+=12"
if "%CURRTIME:~-2%" == "AM" if %HOUR% equ 12 set /A "HOUR-=12"
rem // Left-zero-pad hour:
set "HOUR=0%HOUR%"
rem // Build and display date/time string:
echo %CURRDATE:~-4,4%%CURRDATE:~0,2%%CURRDATE:~3,2%_%HOUR:~-2%%CURRTIME:~3,2%%CURRTIME:~6,2%
But still, the returned values in %DATE% and %TIME% could reflect different days when executed at midnight.
The only way to have the same day in %CURRDATE% and %CURRTIME% is this:
rem // Store current date and time once in the same line:
set "CURRDATE=%DATE%" & set "CURRTIME=%TIME%"
rem // Resolve AM/PM time:
set "HOUR=%CURRTIME:~,2%"
if "%CURRTIME:~-2%" == "PM" if %HOUR% lss 12 set /A "HOUR+=12"
if "%CURRTIME:~-2%" == "AM" if %HOUR% equ 12 set /A "HOUR-=12"
rem // Fix date/time midnight discrepancy:
if not "%CURRDATE%" == "%DATE%" if %CURRTIME:~0,2% equ 0 set "CURRDATE=%DATE%"
rem // Left-zero-pad hour:
set "HOUR=0%HOUR%"
rem // Build and display date/time string:
echo %CURRDATE:~-4,4%%CURRDATE:~0,2%%CURRDATE:~3,2%_%HOUR:~-2%%CURRTIME:~3,2%%CURRTIME:~6,2%
Of course the occurrence of the described problem is quite improbable, but at one point it will happen and cause strange unexplainable failures.
The described problem cannot occur with the approaches based on the wmic command as described in the answer by user Stephan and in the answer by user PA., so I strongly recommend to go for one of them. The only disadvantage of wmic is that it is way slower.
Your question seems to be solved, but ...
I'm not sure if you take the right solution for your problem.
I suppose you try to compress each day the actual project code.
It's possible with ZIP and 1980 this was a good solution, but today you should use a repository system, like subversion or git or ..., but not a zip-file.
Ok, perhaps it could be that I'm wrong.
I realise this is a moot question to the OP, but I just brewed this, and I'm a tad proud of myself for thinking outside the box.
Download gawk for Windows at http://gnuwin32.sourceforge.net/packages/gawk.htm .... Then it's a one liner, without all that clunky DOS batch syntax, where it takes six FOR loops to split the strings (WTF? That's really really BAD MAD AND SAD! ... IMHO of course)
If you already know C, C++, Perl, or Ruby then picking-up AWK (which inherits from the former two, and contributes significantly to the latter two) is a piece of the proverbial CAKE!!!
The DOS Batch command:
echo %DATE% %TIME% && echo %DATE% %TIME% | gawk -F"[ /:.]" "{printf(""""%s%02d%02d-%02d%02d%02d\n"""", $4, $3, $2, $5, $6, $7);}"
Prints:
Tue 04/09/2012 10:40:38.25
20120904-104038
Now that's not quite the full story... I'm just going to be lazy and hard-code the rest of my log-file-name in the printf statement, because it's simple... But if anybody knows how to set a %NOW% variable to AWK's output (yeilding the guts of a "generic" now function) then I'm all ears.
EDIT:
A quick search on Stack Overflow filled in that last piece of the puzzle, Batch equivalent of Bash backticks.
So, these three lines of DOS batch:
echo %DATE% %TIME% | awk -F"[ /:.]" "{printf(""""%s%02d%02d-%02d%02d%02d\n"""", $4, $3, $2, $5, $6, $7);}" >%temp%\now.txt
set /p now=<%temp%\now.txt
echo %now%
Produce:
20120904-114434
So now I can include a datetime in the name of the log-file produced by my SQL Server installation (2005+) script thus:
sqlcmd -S .\SQLEXPRESS -d MyDb -e -i MyTSqlCommands.sql >MyTSqlCommands.sql.%now%.log
And I'm a happy camper again (except life was still SOOOOO much easier on Unix).
I prever to use this over the current accepted answer from Stephan as it makes it possible to configure the timestamp using named parameters after that:
for /f %%x in ('wmic path win32_utctime get /format:list ^| findstr "="') do set %%x
It will provide the following parameters:
Day
DayOfWeek
Hour
Milliseconds
Minute
Month
Quarter
Second
WeekInMonth
Year
You can then configure your format like so:
SET DATE=%Year%%Month%%Day%
So you want to generate date in format YYYYMMDD_hhmmss.
As %date% and %time% formats are locale dependant you might need more robust ways to get a formatted date.
Here's one option:
#if (#X)==(#Y) #end /*
#cscript //E:JScript //nologo "%~f0"
#exit /b %errorlevel%
#end*/
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) +
("0" + todayDate.getSeconds()).slice(-2) ;
WScript.Echo(todayDate);
and if you save the script as jsdate.bat you can assign it as a value :
for /f %%a in ('jsdate.bat') do #set "fdate=%%a"
echo %fdate%
or directly from command prompt:
for /f %a in ('jsdate.bat') do #set "fdate=%a"
Or you can use powershell which probably is the way that requires the less code:
for /f %%# in ('powershell Get-Date -Format "yyyyMMdd_HHmmss"') do set "fdate=%%#"
Adding other options to this list of answers.
you could have replaced empty space with a 0 something like echo %time: =0%
but that is still dependent, move that code to a buddy's PC in some other random place and you'll get funny outputs. So you can incorporate powershell's Get-Date:
for /f "tokens=*" %%i in ('PowerShell -Command "Get-Date -format 'yyyymmdd_HHmmss'"') do echo %%i.zip"
A space is legal in file names. If you put your path and file name in quotes, it may just fly. Here's what I'm using in a batch file:
svnadmin hotcopy "C:\SourcePath\Folder" "f:\DestPath\Folder%filename%"
It doesn't matter if there are spaces in %filename%.

Resources