1

Using a batch file, I am taking the date from user. Now, I want to validate if the date in YYYY-MM-DD format.

I have been searching but I am not getting some of the things here: Found this one somewhere:

set /p startDate=Start Date [YYYY-MM-DD]: 
if "%startDate:~2,1%%startDate:~5,1%" neq "--" goto invalidDate
for /f "tokens=2 delims==" %%I in ("%startDate%") do set datetime=%%I
ver > NUL
set /A month=1%MM%-100, day=1%DD%-100, year=1%YYYY%-10000, leap=year%%4
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%

What are these: ~2, 1%%variable:~5, %%, In cmd help I found single % but not double %%

Also,

set /A month=1%MM%-100, day=1%DD%-100, year=1%YYYY%-10000, leap=year%%4

What is this actually doing. Can anyone explain character by character, please?

halfer
  • 19,824
  • 17
  • 99
  • 186
  • I'm assuming, because the code is now wrong, that you changed the first line to suit your intended goal with no idea of how it would effect the rest of it. – Compo Nov 28 '16 at 12:12
  • You are right @Compo I was trying to change it and to understand but it was going above my head :P – Moktik Dhawan Nov 29 '16 at 03:53

3 Answers3

3

Double % are sometimes used (eventhough I do not know the exact purpose; I think it is for a delayed expansion though) but in your case it is just the connection of two variables used:

set foo=foo
set bar=bar
echo %foo%%bar%

-> foobar

Another use of it (at least in batch files) is the declaration and usage of variables in for loops: for /l %%I in(1,1,5) do echo %%i. In the command prompt you would only write %i.
The second line you posted makes no sense to me to be honest... Thanks to Aacinis comment this makes sense to me now! What basically gets done is:

Variables are set with an /Arithmetic term where a 1 is added in front to subtract it back again. Example:

set /a year=1%YYYY%-10000

-> Set the value of the variable year to the value of the variable 1YYYY - 10000. This is done (thanks to Aacini for the explanation again) to prevent the input with a leading zero (e.g. 08) to get interpreted as an octal value which in this case would lead to an invalid date. Notice that there is a difference if you use /a or not:

set foo=1+1
set /a bar=1+1
echo foo %foo%
echo bar %bar%

Next we have another case of %% which in this case is the modulo operator.

The modulo operator will return the rest of a division if you will. Examples:

10 %% 2 = 0 (You can divide 10 by 2 without any rest)
11 %% 4 = 3 (as 8 by 4 is 2 and the rest from 8 to 11 is 3)

I the variable leap is saved whether the year is a leap year (value == 0) or not (value != 0). However this is somewhat taken easy with the other rules you have to think of when checking for leap years but that does not belong here...

%startDate:~2,1%%startDate:~5,1%

Will produce a string that takes the 1 character after the first 2 and the 1 after the first 5 characters. In your case you would have to change that to ~4,1 and ~7,1 as the 5th and 8th character are the dashes the program checkes for.

J.Baoby suggested this link to have a closer look to. Two other great sources for help about are Dostips and SS64.

Feel free to ask questions :)

geisterfurz007
  • 5,292
  • 5
  • 33
  • 54
  • I don't think `%%` is used for delayed expansion. In case of delayed expansion you would just use `!` instead of `%` (`%foo%` must be written as `!foo!` if you want to use [delayed expansion](http://ss64.com/nt/delayedexpansion.html) ). The only place I know where `%%` is used to access variables is in for-loops inside batch-scripts. – J.Baoby Nov 28 '16 at 12:37
  • Yeah thats why I wrote I think... I thought I have seen something like this, but I was not sure. The for-loop thing is a good one though and I will edit it. Thanks for the suggestion! – geisterfurz007 Nov 28 '16 at 12:47
  • Ooh ok. My bad. My appologies and you're welcome. Maybe you could also add that `%var:~5,2%` is used to get a substring from the `%var%` variable (2 characters wide starting from the 6th) as the `~5,1%`. [This](http://www.computerhope.com/sethlp.htm) could be a perfect link – J.Baoby Nov 28 '16 at 12:51
  • No need to apologize! I was wrong indeed! Another thing to add :) – geisterfurz007 Nov 28 '16 at 13:00
  • I like this explanation! +1 **`:)`** I only would add that `set /A month=1%MM%-100` trick is used in order to avoid errors when `%MM%` is 08 or 09 because a number that start in "0" is taken as octal number, but "08" and "09" are _invalid_ octal numbers. I invite you to review [my answer](http://stackoverflow.com/questions/40843216/validate-the-date-given-by-user-using-batch-file/40849081#40849081). – Aacini Nov 28 '16 at 16:47
  • @Aacini much appreciated! Thanks for the explanation! I editted that quickly and it makes sense to me now :) – geisterfurz007 Nov 28 '16 at 18:04
  • Thanks a lot @geisterfurz007 for explaining it so well (by the help of others as well) :) I appreciate your answer. I have now understood what is actually going on there. Thank you everyone else as well :) – Moktik Dhawan Nov 29 '16 at 03:42
2

You definitely have some code issues with your program but I am going to offer you an alternative to checking for valid dates that uses a little XCOPY trickery. It basically uses XCOPY to check two empty folders for the purpose of copying but we use the /L option so that it will not actually copy anything. The other trick is that if the date is invalid it throws and error before even attempting to copy which we can use to redirect back to the question to ask for a valid date.

@echo off

:invalidDate
cls
set /p startDate=Start Date YYYY-MM-DD:

REM xcopy needs date in m-d-y format
for /f "tokens=1,2,3 delims=-" %%G in ("%startDate%") do set checkdate=%%H-%%I-%%G
set rand=%random%
md "dummy%rand%\empty%rand%"

xcopy /d:%checkdate% /t "dummy%rand%\empty%rand%" "dummy%rand%" >nul 2>&1 ||(
    rd /s /q "dummy%rand%" 
    goto invalidDate
    )

rd /s /q "dummy%rand%"
echo %startdate% is a valid date

pause
Squashman
  • 13,649
  • 5
  • 27
  • 36
  • Thank you @Squashman I just wanted an explanation so I could write it on my own or atleast understand what is going on ;) – Moktik Dhawan Nov 29 '16 at 03:48
0

Well, if you take a working code, modify it and then you ask "Why this code not works?", I think the answer is obvious...

This is the original code you "found somewhere":

@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

If you want to change the original MM-DD-YYYY date format to YYYY-MM-DD, then you must change these lines:

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"

... by these ones:

set /P "startDate=Start Date [YYYY-MM-DD]: "
if "%startDate:~4,1%%startDate:~7,1%" neq "--" goto invalidDate
for /F "tokens=1-3 delims=-" %%a in ("%startDate%") do set "YYYY=%%a" & set "MM=%%b" & set "DD=%%c"

A further explanation of !dpm[%month%]! part is given at this answer. I invite you to read such an answer and upvote it, because it just needs a few upvotes to reach 100! ;)

Community
  • 1
  • 1
Aacini
  • 65,180
  • 12
  • 72
  • 108
  • Damn I just had to wait a bit longer... Congrats @Aacini :) – geisterfurz007 Nov 28 '16 at 17:56
  • @Aacini I know that code of mine is not working. I did change the original code and I was not able to understand anything of it. That's why, I have nowhere said that my code is not working :P I just wanted to understand whats going on so I just posted it like that. Sorry for that ;) Thank you very much for your answer :) – Moktik Dhawan Nov 29 '16 at 03:40
  • Your last comment confuses me. I _assumed_ that the purpose of this question was to _"Validate the date given by user using batch file"_, that is, to fix the posted code in order to _solve your problem_ (validate user input). IMHO, if your _real_ purpose was a different one, then you should titled the question like "Explain these Batch commands, please" and post just the two or three lines you didn't understood. I suggest you to be as concise and clear as possible in your future questions and not include any additional code/explanations/data not related to the problem... – Aacini Nov 29 '16 at 20:57
  • Okay.. I suppose you are right.. I just posted the topic I was working on.. Surely, I will take care of topics next time ;) Thanks @Aacini – Moktik Dhawan Nov 30 '16 at 04:27