'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.



Solution 1:[1]

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.

Solution 2:[2]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2