Windows daydate.bat to demonstrate date/day-of-week manipulation
[page last modified 2018-11-13]

This is a Windows command script (.bat) to demonstrate manipulation of dates within batch files by "packing" the date into a single number.  This method allows jumping any number of days ahead or back by addition or subtraction, then decoding the new year month day.

The method also provides for easily discerning day-of-week.  This is a common need for conditional execution within .bat scripts.

The essence is contained in two small subroutines -- :dpack and :dunpk.   They are each a few lines and have no external dependency.   :dpack takes decimal year month day values and encodes them into a single decimal number that represents the count of days since 1 1 1.   So, giving it 1 1 1 produces a zero.  Giving it 2018 10 23 produces 736989.

Why since 1 1 1?  Why not since 0 1 1?  Why not since the start of the Gregorian calendar in 1582?  Answer: no reason, it's arbitrary.   The first version of the packer I wrote 40 years ago worked this way and I've never changed it.

An example of calling the :dpack subroutine:

   call :dpack res 2018 10 23
   echo %res%
The year month day inputs are not checked and must represent a valid date.  On return the environment variable res will hold the packed date... a single large number.

The single number can be incremented or decremented to move ahead or back a day.  When unpacked the result will always reflect the correct year, month, and day, handling month and year crossing (and yes, leap years).

The packed value easily yields the day-of-week -- modulo 7 of it returns   0=Mon, 1=Tue, ..., 6=Sun.  This follows ISO 8601 standards.

The :dunpk routine is the inverse of :dpack -- it's given three variable names to store the unpacked year month day into, and the value to unpack.   For example:

   call :dunpk y m d res
   echo %y% %m% %d%
   2018 10 23

An example of jumping ahead 10 days:

   set /a res += 10
   call :dunpk y m d res
   echo %y% %m% %d%
   2018 11 2

The input values to both :dpack and :dunpk are "set /a" expressions, so they may be actual numbers or variable names or contain an operation.  For example:

   call :dunpk y m d res+7

The provided daydate.bat contains and demonstrates the use of these routines.  daydate.bat is displayed at the bottom of this page.   It displays the seven dates centered around the current, with day-of-week prefixed.  Its output should be (understanding your current date will be different):

   Sat 2018 10 20
   Sun 2018 10 21
   Mon 2018 10 22
   Tue 2018 10 23
   Wed 2018 10 24
   Thu 2018 10 25
   Fri 2018 10 26
   Press any key to continue . . . 

Finally a third routine :dgetl is provided.  It's not related to packing/unpacking dates; it gets the current year month day in a locale independent fashion, and is here to demonstrate the other routines.   An example of its use:

   call :dgetl y m d
   echo %y% %m% %d%
   2018 10 23

Change Log

11/13/2018  Properly handle set /a expressions as input to both :dpack and :dunpk.  Speed operation by using continuation lines in :dpack and :dunpk.

Click here to download

11/11/2018  Allow :dunpk packed date argument to be a set /a expression.

Click here to download

10/24/2018  Original release.

Click here to download

Widget is loading comments...

You are visitor 7189       Go to Home Page

@echo off& setlocal enabledelayedexpansion rem 11/13/2018 daydate.bat: Most recent version at rem Example of date manipulation within a .BAT file. rem This is accomplished by first packing the date into a single number. rem This demo .bat displays dates surrounding the current date, prefixed rem with the day-of-week. call :dgetl y m d call :dpack p y m d set days=MonTueWedThuFriSatSun for /l %%o in (-3,1,3) do ( set /a o=p+%%o, o3=o%%7*3 call :dunpk y m d o for %%d in (!o3!) do echo !days:~%%d,3! !y! !m! !d! ) pause& exit /b rem gets local date returning year month day as separate variables rem in: %1 %2 %3=var names for returned year month day :dgetl setlocal& set "z=" for /f "skip=1" %%a in ('wmic os get localdatetime') do set z=!z!%%a set /a y=%z:~0,4%, m=1%z:~4,2% %%100, d=1%z:~6,2% %%100 endlocal& set /a %1=%y%, %2=%m%, %3=%d%& exit /b rem packs date (y,m,d) into count of days since 1/1/1 (0..n) rem in: %1=return var name, %2 %3 %4= year month day (set/a expressions) rem out: set %1= days since 1/1/1 (modulo 7 is weekday, Mon= 0) :dpack setlocal enabledelayedexpansion&^ set /a y=(%2)*512+(%3)*32+(%4), d=y%%32, m=y/32%%16, m3=m*3, y/=512&^ set t=xxx 0 31 59 90120151181212243273304334 set /a r=y-(12-m)/10, r=365*(y-1)+d+!t:~%m3%,3!+r/4-(r/100-r/400)-1 endlocal& set %1=%r%& exit /b rem inverse of date packer rem in: %1 %2 %3=var names for returned year month day rem %4= packed date (set/a expression) :dunpk setlocal& set /a y=(%4)+366, y+=y/146097*3+(y%%146097-60)/36524,^ y+=y/1461*3+(y%%1461-60)/365, d=y%%366+1, y/=366 set m=1& for %%x in (31 60 91 121 152 182 213 244 274 305 335 ) do if %d% gtr %%x set /a m+=1, d=%d%-%%x endlocal& set /a %1=%y%, %2=%m%, %3=%d%& exit /b