I often write routines that allow a user to input a date. This is very often very problematic to say the least.
Users often do not care about which format is expected and routines that may call this routine may have
been programmed with a specific date format peculiar to the country or machine in use. This routine
chooses to ignore the 'Tue 12/24/2013' and '24-Dec-13' formats since users never input that way. These
formats are typically generated by accessing %date% in these peculiar formats and thus are correct dates.
This flexible routine determines if a date passed is a valid date (AD) and accepts multiple date formats.
It does not rely on any outside programs such as xcopy, WMIC, etc. Nor does it access the registry.
Leap years are processed appropriately, including years divisible by 100 with no leap year unless
divisible by 400, like the year 2000, which had a leap year. Please report any bugs if found.
Code: Select all
@echo off&goto :start
:Valid_Date Date
echo.:::----------------------------------------------------------:::
echo.::: Syntax: Valid_Date 'Date' :::
echo.::: :::
echo.::: Date may be passed as a value or as a reference variable :::
echo.::: Checks for validity of the Date value, which may be in :::
echo.::: M-D-Y, MM-DD-YYYY, YYYYMMDD or Julian (7 digit) format :::
echo.::: Non English YYYY.MM.DD is valid if year is gtr 2 digits :::
echo.::: Year must be at least year 0 AD to be considered valid :::
echo.::: 1 or 2 digit years in M-D-Y format are considered 2000+ :::
echo.::: For example the year 11 A.D. should be passed as 0011 :::
echo.::: The Valid date parameter separators are: '/' '-' and '.' :::
echo.::: Returns 0 in errorlevel if date is valid, else non zero :::
echo.::: :::
echo.:::----------------------------------------------------------:::
exit /b 1
:start
if "%~1"=="" goto :Valid_Date Syntax
if "%~1"=="/?" goto :Valid_Date Syntax
SetLocal EnableDelayedExpansion
if defined %~1 (set "dte=!%~1!") else (set "dte=%~1")
set "invalid="
set "sep="
set/a err=1
for /f "tokens=1* delims=0123456789-/." %%a in ("!dte!") do set "err=%%a"
if /i !err! neq 1 set/a err=1&goto :end
for /f "tokens=1-2* delims=0123456789" %%a in ("!dte!") do (
set "err=%%a"&set "sep=%%b"&set "invalid=%%c"
)
call :strln dte len
if /i !err! equ 1 ( REM string is numeric only
if /i !len! lss 7 goto :end
if /i !len! gtr 8 goto :end
) else ( REM valid separators found in string
set/a err=2
if defined invalid goto :end
if not defined sep goto :end
if /i !len! lss 5 goto :end
if /i !len! gtr 10 goto :end
)
set "dte=%dte:/=-%"
set "dte=%dte:.=-%"
echo !dte!|Find "--">nul
if !errorlevel! equ 0 goto :end
set "ymd=%dte:-=%"
if /i !err! equ 1 ( REM process numeric only string
if /i !len! equ 7 (call :gdate ymd &set/a err=0)
set "dte=!ymd:~4,2!-!ymd:~6,2!-!ymd:~0,4!"
)
if !err! neq 0 ( REM process separated string
for /f "tokens=1-3 delims=-" %%A in ("!dte!") do (
set mm=%%A&set dd=%%B& set "yy=%%C"
call :strln mm mlen
if /i !mlen! gtr 2 if /i "!sep!" equ "." (
set yy=%%A&set mm=%%B&set "dd=%%C"
if not defined dd goto :end
)
)
if not defined yy goto :end
call :strln yy ylen
if /i !dd! gtr 31 goto :end
if /i !mm! gtr 12 goto :end
set/a yy=10000!yy! %% 10000,mm=100!mm! %% 100,dd=100!dd! %% 100
if !yy! LSS 100 if /i !ylen! lss 3 set /a yy+=2000
call :jdate JD yy mm dd
if /i 1!mm! lss 20 set "mm=0!mm!"
if /i 1!dd! lss 20 set "dd=0!dd!"
set "ymd=!yy!!mm!!dd!"
call :Gdate JD
if /i !JD! equ !ymd! set/a err=0
)
:end
EndLocal&exit /b %err%
:gdate
setlocal enabledelayedexpansion
set "p=!%~1!"
set /a p = p + 68569
set /a q = 4 * %p% / 146097
set /a r = %p% - ( 146097 * %q% +3 ) / 4
set /a s = 4000 * ( %r% + 1 ) / 1461001
set /a t = %r% - 1461 * %s% / 4 + 31
set /a u = 80 * %t% / 2447
set /a v = %u% / 11
set /a GYear = 100 * ( %q% - 49 ) + %s% + %v%
set /a GMonth = %u% + 2 - 12 * %v%
set /a GDay = %t% - 2447 * %u% / 80
if /i 1%GMonth% lss 20 set "GMonth=0%GMonth%"
if /i 1%GDay% lss 20 set "GDay=0%GDay%"
endlocal&(
set "%~1=%GYear%%GMonth%%GDay%"
)&exit/b %GYear%%GMonth%%GDay%
:jdate
setlocal enabledelayedexpansion
set/a yy=!%~2!,mm=!%~3!,dd=!%~4!
set /a JD=dd-32075+1461*(yy+4800+(mm-14)/12)/4+367*(mm-2-(mm-14)/12*12)/12-3*((yy+4900+(mm-14)/12)/100)/4
endlocal&set/a %~1=%JD%&exit/b
:Strln
( SetLocal
set "str=A!%~1!"
set "len=0"
for /L %%A in (12,-1,0) do (
set /a "len|=1<<%%A"
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"
)
)
EndLocal&(
if "%~2" NEQ "" set/a %~2=%len%
)&exit /b %len%