CMD/BAT - Help determining date 36 days ago - command-line

Each night I need to do work on a folder 36 days old from the current date. I have a system that writes files to a daily structure like below. I need to keep 35days worth on the local disk and so each night I need to archive off the 36th day. Here is the kicker... There are approx 2 million files per day, so I cannot efficiently scan the whole 2009 folder and only move files older than 35 days. What I need to do is though a batch script determine the path of the folder that is 36days old and then apply my archive logic. I have scripts to determine but having trouble doing the determination to 36 days old. In a pinch I can use perl if there is not a batch way to do this. --Shawn
Folder structure is like this:
2009\07\01
2009\07\02
2009\07\03
.
.
.
2009\08\01
2009\08\02
2009\08\03
#EDIT: Helen's great answer has me 99% of the way there. My only problem is that the month and day out of the vbs is not padded with a zero which i have to deal with in the folder structure. Does anyone have an easy way to pad in a leading 0 if the day or month is less than 10?
Here is what I am doing so far:
for /F "tokens=1-3 delims=/" %%x in ('cscript //nologo get36thday.vbs') do (
SET YYYY=%%z
SET MM=%%x
SET DD=%%y)
except %MM% ends up being 7 instead of 07

The batch option is pretty wicked you will need to calculate which month it is then based of of that run a while loop counting down the days. I would high recommend perl as it would be a few lines of code
using the DateTime module from CPAN
http://search.cpan.org/dist/DateTime/lib/DateTime.pm
my $dt = DateTime->now->subtract(days => 36);

The batch way to determine the date would be too compilcated; it's much easier to use a script for that. Sorry, no Perl sample but a VBScript one:
WScript.Echo DateAdd("d", Date, -36)
You can call this script from a batch file and read the calculated date like this:
for /f %%d in ('cscript //nologo datediff.vbs') do set dt=%%d

If you came here with google like me:
To fix the leading zero's in the .vbs I add a zero in front and strip the right 2 characters.
"0" & "7" -> "07" and "0" & "14" -> "14"
OldDateCode.vbs:
OldDate = DateAdd("d", Date, -36)
DateCode = Year(OldDate) & Right("0" & Month(OldDate), 2) & Right("0" & Day(OldDate), 2)
WScript.Echo DateCode
I also wanted to keep the 1st folder of the month so I compare the last 2 digits (day) with "01"
VBS code to check for 1st day:
If Right(DateCode, 2)="01" then
WScript.Echo "The 1st:" & vbCrLf & DateCode
Else
WScript.Echo "Not the 1st:" & vbCrLf & DateCode
End If
CheckDate.bat:
#Echo Off
Set Folder=D:
for /f %%d in ('cscript //nologo OldDateCode.vbs') do set OldDateCode=%%d
If "%OldDateCode:~6,7%"=="01" (
Echo "Old Backup: %OldDateCode% 1st of the month: keeping..."
) ELSE (
Echo "Old Backup: %OldDateCode% not the 1st of the month: removing..."
RD /S /Q "%Folder%\%OldDateCode%"
)
pause
Make a folder in D:\ with the datecode of 36 days ago. Play around with the -36 and the datecodes.

Related

How do you define the first day of the month (without including week-ends) in a batch script?

I have created a scheduled task in Windows that consists of running a batch script.
The problem is that this task has to be scheduled every 1st day of the month except for weekends.
For example, for the month of May, this task should run on May 02nd.
As I can't do it via the task scheduler, my idea is to schedule the execution task every day and add a condition checking that the execution day is the 1st day of the month excluding weekends.
This script would be of the following form :
if today = firstday
C:/MyExec/popo.exe arg1 arg2
Can you help me to write this script please?
Thank you in advance.
There are loads of standard methods already created for this, so just for me to experiment a bit, here is an untested method (untested meaning I simply ensured there are no syntax errors, but I have not tested all dates scenarios etc:
#echo off
if not exist _mlock break>_mlock
for /f "tokens=1-3*delims=_" %%i in ('PowerShell -Command "& {Get-Date -format "dd_ddd_MM"}"') do (
if %%i lss 5 if /i not "%%j" == "Sat" if /i not "%%j" == "Sun" findstr "m%%k">nul _mlock || (
echo m%%k>"_mlock"
start "" "C:\MyExec\popo.exe" "arg1" "arg2"
)
)
The concept: Use powershell to get a non-locale dependent dd ddd mm (04 Wed 05). I check for the day number, if it is less than 5 (overkill in this scenario) and if the ddd is not Sat or Sun and if the month's lock file does not contain the current month number, it will launch the command. If however the lock file contains the month, it will skip it until the month is updated in the file.
You're welcome to test this, if you are not happy with the temp holding file method, you can also let it add the month as a lock to the batch-file itself instead.
Notes:
This does not cater for public holidays, only weekends as per your request.
The ddd day result is language dependent and will require you to amend Sat and Sun accordingly on non English Operating systems.
This version is language-independent, excepted for optional display, and doesn't require lock files.
I've put comments inside the batch itself, feel free to ask for precisions if needed. I've tested it for March and May 2022, it works and doesn't trigger the command more than one time per month.
#echo off
setlocal enableextensions enabledelayedexpansion
REM Get current date. Powershell is used to get values in a fixed order.
for /f "usebackq tokens=1-3 delims=/" %%A in (`PowerShell -Command "& {Get-Date -format "dd/MM/yyyy"}"`) do (
set DD=%%A
set MM=%%B
set YY=%%C
REM Scheduled day.
set EDD=01
)
REM Not during first 3 days after scheduled day? Skip.
set /A maxday=!EDD!+2
if !DD! LEQ !maxday! (
if !DD! GEQ !EDD! (
goto :can_test
)
)
echo !DD!/!MM!/!YY!: Not first days after scheduled day, no need to test.
goto :eof
:can_test
REM Compute day of week: 0=Monday, ... 5=Saturday, 6=Sunday.
set /A c=(14-!MM!)/12
set /A y=!YY!-!c!
set /A m=!MM!+(12*!c!)-2
set /A d=((!EDD!-1+!y!+(!y!/4)-(!y!/100)+(!y!/400)+((31*!m!)/12))) %% 7
REM Get human-readable day. Change according to your own language, if needed. Keep order, of course.
set DAYS=Monday Tuesday Wednesday Thursday Friday Saturday Sunday
set /A i=0
for %%A in (!DAYS!) do (
set DAY=%%A
if !i! EQU !d! goto :found_day
set /A i+=1
)
:found_day
echo Day of week for !EDD!/!MM!/!YY!: !d! ^(!DAY!^)
REM If EDD is during week-end, increase expected day (2 days for Saturday, 1 day for Sunday)..
if !d! GEQ 6 (
set /A EDD+=7-!d!
REM Handle non-significative zero.
if !EDD! LSS 10 (
set EDD=0!EDD!
)
echo On week-end, schedule it to !EDD!/!MM!/!YY! instead.
)
REM Are we on scheduled day EXACTLY?
if !DD! EQU !EDD! (
REM Executing command now.
echo Executing: C:\MyExec\popo.exe arg1 arg2
REM C:\MyExec\popo.exe arg1 arg2
goto :eof
)
REM We're before or after schedule, but still within the first three days in the month.
echo Unscheduled for today ^(!DD!/!MM!/!YY!^).
goto :eof
The "day of week" formula comes from here: Mathematical curiosities / Find the day of the week with a given date (in French).
I took also Gerhard's trick for obtaining a fixed date format quickly through Powershell. It could also have been done with an embedded VBS script, since this language natively have the Weekday function, but it may have been quite unreadable to add a temporary script generation within the batch itself.
Sorry for this answer but I've no reputation to add a comment below the answer I'm referring to, the one by Wisblade:
https://stackoverflow.com/a/72113645/16641207
And I think it is important to make some additions to his beautiful answer.
I'm answering just to point out that in the wonderful piece of batch posted by Wisblade, to avoid sundays and saturdays too, at line 38 instead of this:
if !d! GEQ 6 (
there should be:
if !d! GEQ 5 (
because numbering in the "array" begins with zero.
I would also underline that this conditional expression should be changed if weekend days are different in the country where the code is used: e.g. in Israel one should check if !d! is NOT 0 and NOT 6 (not a monday nor a sunday, but saturdays are workdays there, too).
Apart from that, I gave a big +1 to his question, works so beautifully.
I used in a scheduled task, combined with the ability of Windows Scheduled Tasks to be scheduled only on first mon/tue/wed/thu/fri of every month, and doing this I'm able to execute my instruction just on first working day of every month.
Simple and effective.

Batch: How to export yesterday's date keeping format [mm/dd/yyyy]?

I am trying to create an environment variable for yesterday's date. It MUST be in the format of MM/DD/YYYY.
For code I currently have:
set m=%date:~-7,2%
set /A m -= 1
set DATE_YESTERDAY=%date:~-10,2%/%m%/%date:~-4,4%
echo %DATE_YESTERDAY%
This works great, HOWEVER, when I offset to yesterdays day with -1 instead of "12/03/2019" I get "12/3/2019". Thus, missing the zero/0.
Any ideas to keep format as MM/DD/YYYY?
I have seen many other questions regarding this, however, all come short!
NOTE - I do NOT want powershell or VBS scripts. I know this can be done with simply batch.
What you should do is use a method which is not based upon user/locale/PC settings. Commonly wmic is used for that, but it isn't the fastest method and would still require performing some math.
You could use vbscript embedded directly into your batch-file:
<!-- :
#Echo Off
For /F %%# In ('CScript //NoLogo "%~f0?.wsf"') Do Set "YesterDate=%%#"
Echo(%YesterDate%
Pause
GoTo :EOF
-->
<Job><Script Language="VBScript">
dtmYesterday = DateAdd("d", -1, Now())
strDate = Right("0" & Month(dtmYesterday), 2) _
& "/" & Right("0" & Day(dtmYesterday), 2) _
& "/" & Year(dtmYesterday)
WScript.Echo strDate
</Script></Job>
You could also, if you prefer it, use powershell from your batch-file instead:
#Echo Off
For /F %%# In ('PowerShell -NoP "(Get-Date).AddDays(-1).ToString('MM/dd/yyy')"'
)Do Set "YesterDate=%%#"
Echo(%YesterDate%
Pause
Windows command processor cmd.exe does not have built-in support for date calculations. Other script interpreters installed by default on Windows like VBScript or PowerShell support date calculations and Compo posted solutions using VBScript or PowerShell.
Here is a pure batch file solution to calculate yesterday's date from current date with remarks explaining the code. The lines with remark command rem can be removed for faster processing the batch file by Windows command processor.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
if "%~1" == "" (
rem Get local date and time in a region independent format.
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS get LocalDateTime /VALUE') do set "LocalDateTime=%%I"
) else (
rem This is for fast testing determining the date of yesterday from any
rem date specified as parameter in format yyyyMMdd on calling this batch
rem file from within a command prompt window. The parameter string is
rem not validated at all as this is just for testing the code below.
set "LocalDateTime=%~1"
)
rem Get day, month and year from the local date/time string (or parameter).
set "Day=%LocalDateTime:~6,2%"
set "Month=%LocalDateTime:~4,2%"
set "Year=%LocalDateTime:~0,4%"
rem Define a variable with today's date in format MM/dd/yyyy.
set "Today=%Month%/%Day%/%Year%"
rem Decrease the day in month by 1 in any case.
rem It is necessary to remove leading 0 for the days 08 and 09 as
rem those two days would be otherwise interpreted as invalid octal
rem numbers and decreased result would be -1 instead of 7 and 8.
rem if "%Day:~0,1%" == "0" set "Day=%Day:~1%"
rem set /A Day-=1
rem Faster is concatenating character 1 with the day string to string
rem representing 101 to 131 and subtract 101 to decrease day by one.
set /A Day=1%Day%-101
rem The yesterday's date is already valid if the day of month is greater 0.
if %Day% GTR 0 goto BuildYesterday
rem Yesterday is in previous month if day is equal (or less than) 0.
rem Therefore decrease the current month by one with same method as
rem described above to decrease correct also the months 08 and 09.
set "Day=31"
set /A Month=1%Month%-101
rem Yesterday is in previous year if month is equal (or less than) 0.
if %Month% GTR 0 goto GetLastDay
set /A Year-=1
set "Month=12" & goto BuildYesterday
:GetLastDay
rem Determine last day of month depending on month.
for %%I in (4 6 9 11) do if %Month% == %%I set "Day=30" & goto BuildYesterday
if not %Month% == 2 goto BuildYesterday
rem Determine if this year is a leap year with 29 days in February.
set /A LeapYearRule1=Year %% 400
set /A LeapYearRule2=Year %% 100
set /A LeapYearRule3=Year %% 4
rem The current year is always a leap year if it can be divided by 400
rem with 0 left over (1600, 2000, 2400, ...). Otherwise if the current
rem year can be divided by 100 with 0 left over, the current year is NOT
rem a leap year (1900, 2100, 2200, 2300, 2500, ...). Otherwise the current
rem year is a leap year if the year can be divided by 4 with 0 left over.
rem Well, for the year range 1901 to 2099 just leap year rule 3 would be
rem enough and just last IF condition would be enough for this year range.
set "Day=28"
if LeapYearRule1 == 0 goto BuildYesterday
if NOT %LeapYearRule2% == 0 if %LeapYearRule3% == 0 set "Day=29"
rem The leading 0 on month and day in month could be removed and so both
rem values are defined again as string with a leading 0 added and next just
rem last two characters are kept to get day and month always with two digits.
:BuildYesterday
set "Day=0%Day%"
set "Day=%Day:~-2%"
set "Month=0%Month%"
set "Month=%Month:~-2%"
rem Define a variable with yesterday's date in format MM/dd/yyyy.
set "Yesterday=%Month%/%Day%/%Year%"
echo Today is: %Today%
echo Yesterday is: %Yesterday%
endlocal
Please read my answer on Why does %date% produce a different result in batch file executed as scheduled task? It explains in full details the FOR command line using WMIC to get current date in region independent format.
Here is the code above without comments, empty lines and first IF condition needed only for testing the code. The leap year identification is also optimized for using only third rule which means this code is working only for the years 1901 to 2099 which should be enough.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS get LocalDateTime /VALUE') do set "LocalDateTime=%%I"
set "Day=%LocalDateTime:~6,2%" & set "Month=%LocalDateTime:~4,2%" & set "Year=%LocalDateTime:~0,4%"
echo Today is: %Month%/%Day%/%Year%
set /A Day=1%Day%-101
if %Day% GTR 0 goto BuildYesterday
set "Day=31"
set /A Month=1%Month%-101
if %Month% GTR 0 goto GetLastDay
set /A Year-=1
set "Month=12" & goto BuildYesterday
:GetLastDay
for %%I in (4 6 9 11) do if %Month% == %%I set "Day=30" & goto BuildYesterday
if not %Month% == 2 goto BuildYesterday
set /A LeapYearRule3=Year %% 4
if %LeapYearRule3% == 0 (set "Day=29") else set "Day=28"
:BuildYesterday
set "Day=0%Day%"
set "Day=%Day:~-2%"
set "Month=0%Month%"
set "Month=%Month:~-2%"
echo Yesterday is: %Month%/%Day%/%Year%
endlocal
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
for /?
goto /?
if /?
rem /?
set /?
setlocal /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?
See also single line with multiple commands using Windows batch file for an explanation of operator & used to specify more than one command on a single command line.

Is it possible to specifically compare two dates in batch?

I was wondering if it's possible to compare a specific date (in the MM/DD/YY format) to the current system date, and get the difference in minutes?
In my past questions, I've been able to grab the dates of processes and folders and create the conversion to minutes, but I'm stumped.
My project uses Batch/CMD and Powershell, so any of those formats would be more than acceptable.
Here's the code that I use for grabbing dates to minutes with processes:
if exist "C:\Windows\Prefetch\JAVAW*.*" (
for /f "usebackq" %%A in (`
powershell -NoP -C "[int]([datetime]::Now - (gci 'C:\Windows\Prefetch\JAVAW*.*' -Force).LastWriteTime).TotalMinutes"
`) do set "AgeMinutes=%%A"
)
if exist "C:\Windows\Prefetch\JAVAW*.*" echo JAVAW.pf was modified %AgeMinutes% minutes ago.
if not exist "C:\Windows\Prefetch\JAVAW*.*" echo Error: file not found.
Is a comparison to the current date (and outputting it into minutes) possible?

batch rename files keeping substring and adding mm and yy

I have a series of files that have long filenames. For each filename that contains a hyphen I would like to keep the substring in position 6-8, append the _FM07_FY14.prn to the name and ignore the rest of the original filename. The new extension is now .prn. The two digits 07 stands for the month and 14 is the year. The month and year can be found from the "date created" property. Will appreciate it if you can show me how to automatically capture this mm and yy from the date created. Hardcoding this part is okay too since I can sort files by created dates and put them in separate folders.
For example
aaaaaD07.dfdd-1234.A.b.1233 new filename will be D07_FM01_FY14.prn
bbcbaA30dls-d343.a.123d new filename will be A30_FM01_FY14.prn
cdq0dG12ir3-438d.dfd.txt new filename will be G12_FM01_FY14.prn
This is the .bat file I come up with after reading many posts on here, and I don't know how to extract the mm and yy so I hard code it. I am not familiar with Powershell. I can only handle a .bat or .cmd file and run it at the command prompt. Any and all help will be highly appreciated. Thanks!
#ECHO OFF
SETLOCAL
for %%F in (*.*) do (
SET "name=%%a"
set "var=_FM01_FY14.prn"
ren *-* "%name:~6,8%var%"
)
*endlocal*
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\one"
PUSHD %sourcedir%
FOR /f "delims=" %%a IN (
'dir /b /a-d "*" '
) DO (
SET name=%%a
SET fdate=%%~ta
ECHO(REN "%%a" "!name:~5,3!_FM!fdate:~3,2!_FY!fdate:~8,2!.prn"
)
popd
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
The format that I use for date is dd/mm/yyyy If yours is different, then you'll need to change the offset in the !fdate:~m,2! phrases. The value of m is the offset into the date string from the first character (the second parameter is the number of characters to select.)
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(REN to REN to actually rename the files.

Batch- Associate variable values based on filename extract

I have filename that look like this
20120528_Sales_Store_001.pdf
20120529_Sales_Store_001.pdf
etc
I need to extract the date from the filename and then associate it to 2 variables
Month
Week
So for each date in the calendar I need to be able to associate the week number and the month name.
Those are FISCAL calendar week&month so it has nothing to do with the actual calendar.
Example
20120528 would be Week 4 and Month April
20120529 would be Week 5 and Month May
Since the fiscal calendar moves from year to year, I know that I will have to redo it every year but im looking for a way to minimize the work that needs to be done.
So I would like to maybe setup something like
Week4 is from ThisDate to ThisDate and is also in the Month of April
Fiscal April would be from 20120403 to 20120430
and count Week 1-2-3-4 (weeks starting every sunday)
Week5 is from ThisDate to ThisDate and is also in the Month of May
Fiscal May would be from 20120501 to 20120528
It is always, 2 month that has 4 weeks and then the 3rd month have 5 weeks.
etc
Or else, I will have to do it once for every day.. which is going to be looong to do and loooong to update every year.
I will need to use the Month and Week variable for directory classification afterwards and that is why I need it.
setlocal ENABLEDELAYEDEXPANSION
set the_file=20120528_Sales_Store_001.pdf
for /f "tokens=1 delims=_" %%F in ("!the_file!") do (
set date=%%F
set month=!date:~4,6!
set day=!date:~6!
set /a week_of_the_month=!day! / 7
set /a modolus="!day! %% 7"
if !modolus! GEQ 4 (
set /a week_of_the_month=!week_of_the_month!+1
)
echo !the_file! is in the !week_of_the_month!th week of the month
echo !the_file! >> week_!week_of_the_month!_of_!month!_month.txt
)
endlocal
this should write the file name to a week_XX_of_XX_month.txt fime .This can be wrapped in another FOR /F that will traverse the files.The files also can be copied and with a little bit more SETs the number of the month can be changed to it's name.I couldn't find better way to calculate the week in the month.Do you need more improvements?
UPDATE:
setlocal ENABLEDELAYEDEXPANSION
pushd %traverse_dir%
for /f %%D in ('dir /b *.pdf ^|findstr "[1234567890_]" ') do (
for /f "tokens=1 delims=_" %%F in ("%%D") do (
set date=%%F
set month=!date:~4,6!
set day=!date:~6!
set /a week_of_the_month=!day! / 7
set /a modolus="!day! %% 7"
if !modolus! GEQ 4 (
set /a week_of_the_month=!week_of_the_month!+1
)
echo !the_file! is in the !week_of_the_month!th week of the month
echo !the_file! >> week_!week_of_the_month!_of_!month!_month.txt
)
)
endlocal
popd