Advanced Batch File Programming: TOMORROW.BAT

AID: 4196
  • Status: Published

9770 points

  • Bypaultomasi
  • TypeGeneral
  • Posted on2010-12-06 at 02:32:10
Awards
  • Community Pick
  • Experts Exchange Approved
Let's kick straight off with some advanced batch file code.

TOMORROW

TOMORROW.BAT is inspired by a question I get asked over and over again; that is, "How can I use batch file commands to obtain tomorrow's date?"

The crux of this batch file revolves around the XCOPY command - a technique I discovered while looking for an easy method for validating dates that avoids a series of complex mathematical procedures.  Here's the complete batch file:
::================================================
:: TOMORROW.BAT
::
:: Function to return tomorrow's date
::================================================
@echo off

set /a d=%date:~0,2%
set /a m=%date:~3,2%
set /a y=%date:~6,4%

:loop
   set /a d+=1

   if %d% gtr 31 (
      set d=1
      set /a m+=1
     
      if %m% gtr 12 (
         set m=1
         set /a y+=1
      )
   )

xcopy /d:%m%-%d%-%y% /h /l "%~f0" "%~f0\" >nul 2>&1 || goto loop

echo %d%/%m%/%y%
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:

Select allOpen in new window



INPUT

The input section is pretty straightforward.  The code receives input from the system's %DATE% whose format is DD/MM/YYYY.

set /a d=%date:~0,2%
set /a m=%date:~3,2%
set /a y=%date:~6,4%
                                    
1:
2:
3:

Select allOpen in new window


If your system's date format differs from DD/MM/YYYY then you will need to edit these three lines or provide some other method of delivering the date to the batch file in order for the rest of the code to work correctly.

You are reminded: This is an advanced topic and therefore, I would expect you to know how to do that.

When assigning values to variables, we use using SET /A (rather than just SET) to overcome the problem of attempting to perform octal arithmetic on '08' and '09' later on. This is because leading zeros are trimmed off during arithmetic assignment.


INCREMENTING DAY

Incrementing the day itself is a doddle.  Basically, '1' is added to the current day value. If the resultant date is valid, it is returned to the user.  

The trick is in the end-of-month handling.  The majority of times, the loop will execute just the once.  If today is the last day in the month, then:  If there are 30 days in the current month, the loop will execute twice.  In the worst-case scenario, where February 28 falls on a non-leap year, the loop will execute just four times; to wit: 28+1 is invalid, 29+1 is invalid, 30+1 is invalid, 31+1 becomes 1, which is valid).

:loop
   set /a d+=1

   if %d% gtr 31 (
      set d=1
      set /a m+=1

      if %m% gtr 12 (
         set m=1
         set /a y+=1
      )
   )
rem if the date (d/m/y) is invalid repeat the process
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:

Select allOpen in new window


In all cases, should the day value exceed the maximum range a day may reach - which is 31, it is reset to '1' and the month value is incremented. The same principle is then applied to the month value should it exceed 12.

VALIDATING THE DATE

XCOPY is the tool of choice here.  Other options, such as attempting to use DATE could lead to all manner of problems in a multitasking environment where files are constantly being written to the hard disk drive, not to mention the possibility of a midnight roll-over in between temporarily changing the system date and restoring it.

xcopy /d:%m%-%d%-%y% /h /l "%~f0" "%~f0\" >nul 2>&1 || goto loop
                                    
1:

Select allOpen in new window


For XCOPY to do its job properly, all of its command line parameters and switches must be in good order because the batch file relies on its error condition to determine whether the date in /d:%m%-%d%-%y% is valid or not. This error must therefore come from /d: alone and no other parameter.  Moreover, all output is redirected to the NUL device hence the >nul 2&>1 so logical errors will be difficult to spot at this stage.

Firstly, let's deal with the source and destination. For this technique, we need a file to exist.  The only files we know for certain to exist are the batch file itself and the filespec '.' (the period).

"%~f0" expands to the batch file's full location so we will use this instead of '.' - which refers to all files in the current folder. The ramifications of using '.' is there could be a large number of files to pre-process and that could be time consuming. Also, '.' is a wildcard and this would require further consideration.  So we reject (dot) and go with "%~f0" -- the batch file itself.

We need to be mindful of the possibility, although improbable, the batch file might have its R, H and S attributes set so this is taken care of by the /h switch which ensures this is not going to be a problem should this be the case.

Next is the destination. It should come as no surprise that I have decided to use "%~f0\". This is because we know if the file "%~f0" exists (and obviously it does) then a folder of the same name cannot possibly exist in the same directory as well.  Because "%~f0" is a valid filename "%~f0\" must, therefore, be a valid foldername.

It would have been nice to have been able to use NUL for both source and destination as is the case with the COPY command.

Lastly, we don't actually want XCOPY to really copy anything.  We just need it to validate its own command line parameters -- more specifically, the date.  The /l (letter 'L') switch tells XCOPY to display a list of files that would be copied rather than actually copying any files and as all output is redirected to the NUL device, no output is actually displayed.

If the date in /d: is valid and resolvable, then XCOPY continues (to do 'nothing') and control is passed to the next line of code in the batch file.  If, on the other hand, the date is invalid, the logical OR operator || passes control to the GOTO statement and the process is repeated.


OUTPUT

Output is straight forward. No real need to assign it to a variable such as tomorrow although you can if you wish to.

echo %d%/%m%/%y%
                                    
1:

Select allOpen in new window


Notice the order in which D, M and Y are assembled - they match the input.

Also note that either D or M may consist of one or two digits. If you want to output this as DD/MM/YYYY then you will need to add the leading zeros yourself. For example, the following should do the job quite nicely:

set d=0%d%
set m=0%m%

echo %d:~-2%/%m~-2%/%y%
                                    
1:
2:
3:
4:

Select allOpen in new window


And that's about all you need to know.


FURTHER CONSIDERATIONS

As it stands, the batch file's only output is a date in D/M/YY format (or DD/MM/YY if you have added the leading zeros). This will work as a standalone batch file or as a function tacked onto the end of your own batch file and executed with a CALL statement.

As an external function, you might CALL it from within your own batch file like this:

for /f %%a in ('tomorrow') do set tomorrow=%%a
                                    
1:

Select allOpen in new window


As a function added to your own code, you might do something like this:

for /f %%a in ('call :tomorrow') do set tomorrow=%%a
                                    
1:

Select allOpen in new window


%%a is set to the date given by the function TOMORROW.BAT, this is then assigned to your variable tomorrow.

Output can also be redirected or piped into another command as in the following examples:

call tomorrow >file
                                    
1:

Select allOpen in new window


or,
call tomorrow|find . . .
                                    
1:

Select allOpen in new window


There is no reason why you cannot take input from the command line rather than DATE itself by replacing the three SET statements in the input section with something like this:

for /f "tokens=1-3 usebackq delims=/" %%a in ('%1') do (
   set d=%%a
   set m=%%b
   set y=%%c
)
                                    
1:
2:
3:
4:
5:

Select allOpen in new window


Note: That would make the function return "The Day After x" rather than "Tomorrow"

One Assumption:
This code assumes that the batch file will be executed from a local hard disk drive or some other writable media with granted access.  That is because XCOPY will validate the source and destination and we must assume that the only error possible is that the date is invalid.



CONCLUSION

The primary purpose of this article is to demonstrate XCOPY's ability to validate dates and although the batch file serves as a self-contained function, the principles shown here can just as well be applied to validating dates input from a user - either passed as command line parameters or entered at the SET /P command.

Further info may be found at http://www.paultomasi.co.uk/tomorrow.html
Asked On
2010-12-06 at 02:32:10ID4196
Tags

tomorrow's date

,

next day's date

,

increment day

,

add one to day

,

date

,

xcopy to validate date

Topic

MS DOS

Views
4034

Comments

Author Comment

by: paultomasi on 2010-12-06 at 15:21:34ID: 21921

Thank you for your assistance

Expert Comment

by: subhashchy on 2011-02-05 at 17:48:13ID: 23561

Helo Tom,

INfact this article is useful,i did seen many peopls asking for this question on different forums,
I have one question, Is it date format dependent ?
On my windows 7 machine it just say :Missing Oprend".
When i changed Echo off to Echo on, it seems going into infinite loop.
My date format is Sun 02/06/2011.


Also will it work on months with less then 31 days like Feb and Apr, Or needs to be modified to work with all months..May be you can add the code to check the month and get the total number of days automaticly..

Thank you .

Author Comment

by: paultomasi on 2011-08-18 at 01:45:01ID: 30766

subhashchy

There is no need to modify the code to handle different number of days for different months, the validator will accept ANY combination of date numbers as it is.

With regard to your date format, DDD MM/DD/YYYY, change these lines at the start of the program:

   set /a d=%date:~0,2%
   set /a m=%date:~3,2%
   set /a y=%date:~6,4%

to:

   set /a d=%date:~7,2%
   set /a m=%date:~4,2%
   set /a y=%date:~10,4%

Expert Comment

by: dragon-it on 2011-10-31 at 00:37:11ID: 32930

Hadn't seen this one before Paul... could come in useful!

Steve

Add your Comment

Please Sign up or Log in to comment on this article.

Join Experts Exchange Today

Gain Access to all our Tech Resources

Get personalized answers

Ask unlimited questions

Access Proven Solutions

Search 3.2 million solutions

Read In-Depth How-To Guides

1000+ articles, demos, & tips

Watch Step by Step Tutorials

Learn direct from top tech pros

And Much More!

Your complete tech resource

See Plans and Pricing

30-day free trial. Register in 60 seconds.

Loading Advertisement...

Top MS DOS Experts

  1. billprew

    140,280

    Master

    0 points yesterday

    Profile
    Rank: Genius
  2. paultomasi

    49,157

    10 points yesterday

    Profile
    Rank: Master
  3. dragon-it

    35,867

    0 points yesterday

    Profile
    Rank: Genius
  4. oBdA

    30,202

    0 points yesterday

    Profile
    Rank: Savant
  5. gerwinjansen

    25,227

    0 points yesterday

    Profile
    Rank: Sage
  6. ReneGe

    24,598

    0 points yesterday

    Profile
    Rank: Guru
  7. QCubed

    23,732

    0 points yesterday

    Profile
    Rank: Guru
  8. Bartender_1

    11,800

    0 points yesterday

    Profile
    Rank: Sage
  9. RobSampson

    10,800

    0 points yesterday

    Profile
    Rank: Genius
  10. Qlemo

    10,191

    0 points yesterday

    Profile
    Rank: Genius
  11. BillDL

    8,800

    0 points yesterday

    Profile
    Rank: Genius
  12. lionelmm

    7,446

    0 points yesterday

    Profile
    Rank: Master
  13. DaveBaldwin

    7,136

    0 points yesterday

    Profile
    Rank: Genius
  14. l33tf0b

    6,600

    0 points yesterday

    Profile
    Rank: Wizard
  15. sirbounty

    6,400

    0 points yesterday

    Profile
    Rank: Genius
  16. leew

    6,320

    0 points yesterday

    Profile
    Rank: Savant
  17. pony10us

    5,000

    0 points yesterday

    Profile
    Rank: Wizard
  18. ve3ofa

    4,552

    0 points yesterday

    Profile
    Rank: Genius
  19. ltlbearand3

    4,300

    0 points yesterday

    Profile
    Rank: Wizard
  20. TazDevil1674

    4,300

    0 points yesterday

    Profile
    Rank: Master
  21. Run5k

    4,196

    0 points yesterday

    Profile
    Rank: Genius
  22. DTHConsulting

    4,186

    0 points yesterday

    Profile
    Rank: Guru
  23. serialband

    4,000

    0 points yesterday

    Profile
    Rank: Master
  24. Anuroopsundd

    4,000

    0 points yesterday

    Profile
    Rank: Sage
  25. thinkpads_user

    3,750

    0 points yesterday

    Profile
    Rank: Genius

Hall Of Fame