3

I have a batch file that I use to create new project folders for clients that walks a user through the creation process and adds the appropriate files and folders to a central location. I need to add an input section so they can put a date (not always current date) in and it is included in the naming of the files.

The issue I have, and I have hunted high and low and can't find my answer, is that I need to dummy proof the date input. I want the user to input the date in the MM-DD-YYYY format including dashes. It needs to then format it into YYYY-MM-DD. It needs to be smart enough that it forces the user to use the required format of MM-DD-YYYY; has to be numbers and dashes, no slashes, the right amount of characters, and so forth.

I haven't been able to find anything close to even remotely get me where I need to be so I am asking the awesome geniuses out there for help in this regard as it is driving me up a wall. Below is my script code. I need this input to go right after the job type is selected. "Please insert date (MM-DD-YYYY format): "

@echo off
setlocal EnableDelayedExpansion
set version=7.95
set projectpath="P:"
set workbookpath="\\server2\Documents\Blanks (DO NOT EDIT)\dryingworkbook_v3r75.xls"
set questions="\\server2\Documents\Blanks (DO NOT EDIT)\Abatement and Mold Questions.txt"
set notes="\\server2\Documents\Blanks (DO NOT EDIT)\Job Notes.docx"
set info="\\server2\Documents\Blanks (DO NOT EDIT)\Job Information.docx"
set bizname=1

ECHO =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ECHO =  Welcome to SERVPRO Project Creation Wizard v%version%  =
ECHO =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ECHO.

:sof
ECHO.
ECHO Is this new project for a Residential or Commercial job?
:loopJobType
SET /P jobtype=Enter [r] for Residential or [c] for Commercial:  
ECHO.
IF "%jobtype%" == "r" GOTO :loopResidential
IF "%jobtype%" == "R" GOTO :loopResidential
IF "%jobtype%" == "c" GOTO :loopCommercial
IF "%jobtype%" == "C" GOTO :loopCommercial
GOTO :loopJobType

:loopResidential
ECHO You have chosen to create a new Residential job project.
ECHO.
set type=1
GOTO :loopFirstName

:loopCommercial
ECHO You have chosen to create a new Commercial job project.
ECHO.
set type=2
SET /p bizname=Please enter the business name:  
ECHO.
IF "%bizname%"=="" GOTO :loopCommercial

:loopFirstName
SET /P FirstName=Please enter the insured's first name:  
IF "%FirstName%"=="" GOTO :loopFirstName
call :format FirstName

:loopLastName
ECHO.
SET /P LastName= Please enter the insured's last name:  
IF "%LastName%"=="" GOTO :loopLastName
call :format LastName
SET FullName=%LastName%, %FirstName%
SET FullBizName=%bizname% (%FullName%)
goto :ConfirmProject

:format
set Name=!%1!
set Head=%Name:~0,1%
set Tail=%Name:~1%
for %%a in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) do set Head=!Head:%%a=%%a!
for %%a in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do set Tail=!Tail:%%a=%%a!
set %1=%Head%%Tail%
GOTO :eof

:ConfirmProject
ECHO.
IF "%type%" == "1" SET /P yesno=Are you sure you want to add "%FullName%" to the Project directory? [y/n]  
IF "%type%" == "2" SET /P yesno=Are you sure you want to add "%FullBizName%" to the Project directory? [y/n]  
IF "%yesno%" == "y" GOTO :CreateProject
IF "%yesno%" == "Y" GOTO :CreateProject
IF "%yesno%" == "n" GOTO :sof
IF "%yesno%" == "N" GOTO :sof
GOTO :ConfirmProject

:CreateProject
IF "%type%" == "1" SET ProjectName=%FullName%
IF "%type%" == "2" SET ProjectName=%FullBizName%

:: Create a folder containing a new project.
mkdir "%projectpath%\%ProjectName%"
ECHO.
ECHO.
ECHO Creating a Project directory for "%ProjectName%" ...

:: Create a folder within said project that will contain job documents.
ECHO Creating a Documents directory for "%ProjectName%" ...
mkdir "%projectpath%\%ProjectName%\Documents"
:: (Taken out of use 7-15-13) ECHO Adding a Job Information file for "%ProjectName%" ...
:: (Taken out of use 7-15-13) copy /-Y %info% "%projectpath%\%ProjectName%\Documents\Job Information - %ProjectName%.docx"
ECHO Documents directory creation for "%ProjectName%" finished ...

:: Create a folder within said project that will contain drying workbook(s).
ECHO Creating a Drying Workbook directory for "%ProjectName%" ...
mkdir "%projectpath%\%ProjectName%\Drying Workbooks"

:: Copy a new blank workbook to the project workbook directory and give it the proper name.
ECHO Adding a Drying Workbook for "%ProjectName%" ...
copy /-Y %workbookpath% "%projectpath%\%ProjectName%\Drying Workbooks\DRY 1_%ProjectName%.xls"
ECHO Adding an Abatement and Mold Questions file for "%ProjectName%" ...
copy /-Y %questions% "%projectpath%\%ProjectName%\Drying Workbooks\Abatement and Mold Questions.txt"
ECHO Drying Workbook directory creation for "%ProjectName%" finished ...

:: Create a folder within said project that will contain original photos.
ECHO Creating a Photos directory for "%ProjectName%" ...
mkdir "%projectpath%\%ProjectName%\"Photos

:: Create a folder within said project photo folder that will contain resized photos.
mkdir "%projectpath%\%ProjectName%\Photos\Resized"
mkdir "%projectpath%\%ProjectName%\Photos\Upload"
ECHO Photos directory creation for "%ProjectName%" finished ...

:: Add in Job Notes file.
ECHO Adding a Job Notes files for "%ProjectName%" ...
copy /-Y %notes% "%projectpath%\%ProjectName%\Job Notes - %ProjectName%.docx"

::  Log the creation of the project.
FOR /F "TOKENS=1* DELIMS= " %%A IN ('DATE/T') DO SET CDATE=%%B
For /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set date=%%a%%b%%c)
echo off >  "%projectpath%\Logs\%ProjectName% - [Project Created %date% by %computername%].txt"
ECHO Logging "%ProjectName%" creation date and time...
ECHO Project directory creation for "%ProjectName%" finished ...
GOTO :OpenProject

:OpenProject
:: Ask if the project should be opened now.  If so open and close script, else close script.
set /p reply=Do you want to open the "%ProjectName%" project now? [y/n]
if "%reply%" == "y" %SystemRoot%\explorer.exe "%projectpath%\%ProjectName%"
IF "%yesno%" == "Y" %SystemRoot%\explorer.exe "%projectpath%\%ProjectName%"
GOTO :eof
IF "%yesno%" == "n" GOTO :No
IF "%yesno%" == "N" GOTO :No
exit

:No
ECHO.
ECHO.
ECHO You have successfully created a new project for %ProjectName%.
ECHO.
ECHO Press any key to exit . . .
PAUSE>NUL

:eof
Arian Faurtosh
  • 17,987
  • 21
  • 77
  • 115
JBacon9484
  • 43
  • 1
  • 4

6 Answers6

5

The Batch file below check that the inserted date have the right format and that it represent a valid date, that is, that have the right number of days in each month, even for February on leap years!

@echo off
setlocal EnableDelayedExpansion

set i=0
for %%a in (31 28 31 30 31 30 31 31 30 31 30 31) do (
   set /A i+=1
   set dpm[!i!]=%%a
)

set /P "inDate=Please insert date (MM-DD-YYYY format): "
if "%inDate:~2,1%%inDate:~5,1%" neq "--" goto invalidDate
for /F "tokens=1-3 delims=-" %%a in ("%inDate%") do set "MM=%%a" & set "DD=%%b" & set "YYYY=%%c"
ver > NUL
set /A month=1%MM%-100, day=1%DD%-100, year=1%YYYY%-10000, leap=year%%4  2>NUL
if errorlevel 1 goto invalidDate
if not defined dpm[%month%] goto invalidDate
if %leap% equ 0 set dpm[2]=29
if %day% gtr !dpm[%month%]! goto invalidDate
if %day% lss 1 goto invalidDate
echo Date correct: %YYYY%-%MM%-%DD%
goto :EOF

:invalidDate
echo Bad date
Aacini
  • 65,180
  • 12
  • 72
  • 108
  • @MattWilliamson: The `set /A` command set errorlevel to non-zero value if there is an error, otherwise it does _NOT_ change the errorlevel. The `ver` command set the errorlevel to zero before the `set /A` and it may be removed if the code assure that the errorlevel is zero at that point. – Aacini Dec 06 '13 at 16:52
  • Ahh. that's pretty clever ;) – Matt Williamson Dec 06 '13 at 16:54
  • Why doesn't SO have colored syntax for batch script? – Arian Faurtosh Dec 06 '13 at 17:14
4

You can check whether your string is valid easily with the findstr command.

set /p date= Please insert date (MM-DD-YYYY format):
echo %date%| findstr /r "^[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]$">nul
if errorlevel 1 (
    echo invalid date
)
pause

(^ means beginning of line, while $ stands for end of line.)

Now for the reformatting MM-DD-YYYY into YYYY-MM-DD, you can split your string and than reassemble it. Since it's a fixed format, this isn't too hard either:

set yyyy=%date:~6,4%
set mm=%date:~0,2%
set dd=%date:~3,2%
set newDate=%yyyy%-%mm%-%dd%
echo %newDate%

The first number in each command resembles the position where the string will be cut. The second number resembles the length of the substring.

Wouter
  • 192
  • 12
  • How does this validate that the month is a valid number (1-12) and that the day they chose is valid for that month? – Squashman Sep 07 '16 at 18:14
  • The person asked: " I want the user to input the date in the MM-DD-YYYY format including dashes." I did my best to give the code that enforced this format, while keeping the code simple and readable. If it had to be a valid date, extra logic is obviously needed. But this solution seemed clean and really general purpose for other people wanting to format input. – Wouter Sep 08 '16 at 00:42
1

I made a function :getdate who test the date try it; It will test if the separators are correct, the value range for thr month and the day and if the values are NUM.

@ECHO OFF
setlocal enabledelayedexpansion

:GetDate
set /p $D=Enter a date (MM-DD-YYYY) :

set $separate=%$d:~2,1% %$d:~5,1%

for %%a in (%$separate%) do (if "%%a" neq "-" (echo Wrong Separator : %%a
                                               pause
                                               goto:Getdate))


set $D=%$D:-= %
set $c=1

for %%a in (%$d%) do (call:test !$c! %%a
                      set /a $c+=1)

if !$c!==4 set $DateOK=%$month%-%$day%-%$Year%
echo This DATE IS OK %$dateOK%
exit /b

:test
if %1 equ 1 (echo %2 | findstr [0-9][0-9]
             if errorlevel 1 (echo Unvalid value for Month [NOT NUM]: %2
                              pause
                              goto:getdate)

             if %2 GTR 12 (echo Unvalid value for Month [VALUR RANGE +]: %2
                                pause
                                goto:getdate)
              if %2 LSS 1 (echo Unvalid value for Month [VALUR RANGE -]: %2
                                pause
                                goto:getdate)
              set $month=%2) 



if %1==2  (echo %2 | findstr [0-9][0-9]
           if errorlevel 1 (echo Unvalid value for Day [NOT NUM]: %2
                            pause
                            goto:getdate)
           if %2 GTR 31 (echo Unvalid value for Day [VALUR RANGE +] : %2
                              pause
                              goto:getdate)
           if %2 LSS 01 (echo Unvalid value for Day [VALUE RANGE -]: %2
                              pause
                              goto:getdate)
           set $day=%2)


if %1==3  (echo %2 | findstr [0-9][0-9][0-9][0-9]
           if errorlevel 1 (echo Unvalid value for Year [NOT NUM] : %2
                            pause
                            goto:getdate)
         set $Year=%2)
SachaDee
  • 9,245
  • 3
  • 23
  • 33
1
@ECHO OFF
SETLOCAL enabledelayedexpansion
CALL :getverdate
ECHO DATE %indate% is OK.
GOTO :EOF
::
:: Get and verify date in format mm-dd-yyyy; reformat as yyyy-mmm-dd
::
:regetdate
ECHO "%indate%" is not in format "MM-DD-YYYY" or is invalid
:getverdate
SET /p indate="Please insert date (MM-DD-YYYY format): "
IF NOT "%indate:~2,1%%indate:~5,1%"=="--" GOTO regetdate
SET checkdate=9%indate:-=%
IF NOT "%checkdate:~8%"=="%checkdate:~8,1%" GOTO regetdate
FOR %%a IN (0 1 2 3 4 5 6 7 8 9) DO SET checkdate=!checkdate:%%a=!
IF DEFINED checkdate GOTO regetdate
IF %indate:~3,2%==00 GOTO regetdate
FOR %%i IN (01:31 02:29 03:31 04:30 05:31 06:30 07:31 08:31 09:30 10:31 11:30 12:31) DO (
 FOR /f "tokens=1,2delims=:" %%j IN ("%%i") DO IF %%j==%indate:~0,2% if "%%k" geq "%indate:~3,2%" GOTO goodday 
)
GOTO regetdate
:goodday
IF "%indate:~-4%" geq "1980" IF "%indate:~-4%" leq "2099" GOTO goodyear
GOTO regetdate
:goodyear
SET /a checkdate=%indate:~-4% %% 4
IF "%indate:~0,2%%indate:~3,2%"=="0229" IF %checkdate% neq 0 GOTO regetdate
SET indate=%indate:~-4%-%indate:~0,2%-%indate:~3,2%
GOTO :eof

Here's another 'get and validate date` routine.

Note that in your code you should never set a variable called date. %date% will return the current date - it's a "magic variable" controlled by CMD. Other such variables include %time%, %random% and %errorlevel%. Setting any of these overrides the system-established value.

Magoo
  • 77,302
  • 8
  • 62
  • 84
0

You could present the user with three prompts - year, month, day.

set /p y="Please enter year (YYYY): "
set /p m="Please enter month (MM): "
set /p d="Please enter day (DD): "
set date=%y%-%m%-%d%

If you would like to verify the length of the input something like:

if [%y:~4%] NEQ [] echo year entered incorrectly & goto :getDate

You can assume if %y% is greater than four characters - i.e. if %y:~4% is not null - that it has been entered incorrectly (see Dos Tips on string manipulation). The same principal applies for day and month, except they should be two characters.
Obviously for that example you would need to add the label :getDate before the user input.

unclemeat
  • 5,029
  • 5
  • 28
  • 52
0

You may use ReadFormattedLine subroutine for all kind of formatted input. For example, the command below read 3 numbers in a date format; the routine just accept digits, insert the hyphens and continue automatically after read the last digit. If the user delete characters, the hyphens are also deleted automatically.

call :ReadFormattedLine myDate="##-##-####" /M "Please insert date (MM-DD-YYYY format): "

This subroutine is written in pure Batch so it does not require any additional program, and it allows several formatted input operations, like read passwords, convert letters to uppercase, etc. You may download ReadFormattedLine subroutine from Read a line with specific format.

Aacini
  • 65,180
  • 12
  • 72
  • 108