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

824 views Asked by At

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.

3

There are 3 answers

2
Wisblade On

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.

2
Gerhard On

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:

  1. This does not cater for public holidays, only weekends as per your request.
  2. The ddd day result is language dependent and will require you to amend Sat and Sun accordingly on non English Operating systems.
0
TheBlackbird On

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.