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:
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:
An example of jumping ahead 10 days:
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:
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):
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:
Windows daydate.bat to demonstrate date/day-of-week manipulation
[page last modified 2018-11-13]
call :dpack res 2018 10 23
echo %res%
736989
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.
call :dunpk y m d res
echo %y% %m% %d%
2018 10 23
set /a res += 10
call :dunpk y m d res
echo %y% %m% %d%
2018 11 2
call :dunpk y m d res+7
C>daydate
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 . . .
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 daydate181113.zip
11/11/2018 Allow :dunpk packed date argument to be a set /a expression.
Click here to download daydate181111.zip
10/24/2018 Original release.
Click here to download daydate181024.zip
You are visitor 7638 Go to Home Page
@echo off& setlocal enabledelayedexpansion rem 11/13/2018 daydate.bat: Most recent version at paulhoule.com/daydate 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