Subtract month in Windows batch

2019-09-06 02:51发布

问题:

I would like to have this run and show the previous month. When I try to subtract the month it makes the last day of the month field not appear.

@echo off

set FirstDay=01

set Month=%date:~4,2% 

set Year=%date:~10,4%



if %Month%==01 set LastDay=31 & goto foundate
if %Month%==02 set LastDay=28 & goto foundate
if %Month%==03 set LastDay=31 & goto foundate
if %Month%==04 set LastDay=30 & goto foundate
if %Month%==05 set LastDay=31 & goto foundate
if %Month%==06 set LastDay=30 & goto foundate
if %Month%==07 set LastDay=31 & goto foundate
if %Month%==08 set LastDay=31 & goto foundate
if %Month%==09 set LastDay=30 & goto foundate
if %Month%==10 set LastDay=31 & goto foundate
if %Month%==11 set LastDay=30 & goto foundate
if %Month%==12 set LastDay=31 & goto foundate



:foundate
echo The year is: %Year%
echo The month is: %Month%
echo First day of this month is: %FirstDay%
echo Last day of this month is: %LastDay%

回答1:

Dates are complicated to work with and easy to get wrong, and if you can avoid rolling your own, do so.

CMD does not come with a native date library, but the .NET System.DateTime library is available via PowerShell. The following PS script shows how to use .NET to do what you're asking.

Get-LastMonthStats.ps1
$lastmonth = (Get-Date).addMonths(-1)

"The year is $($lastmonth.year)"
"The month is $($lastmonth.Month)"
"First day of this month is 01"
"Last day of this month is $([DateTime]::DaysInMonth($lastmonth.year, $lastmonth.month))"

Nearly all of this script is formatting the output. To run from CMD, launch this with the command

powershell.exe -ex bypass -f Get-LastMonthStats.ps1

Alternatively, you can put the whole script on one (very long) command line, if you don't want to create a separate .ps1 file.

powershell -c "$lastmonth = (Get-Date).addMonths(-1); 'The year is ' + $lastmonth.year; 'The month is ' + $lastmonth.Month; 'First day of this month is 01'; 'Last day of this month is ' + [DateTime]::DaysInMonth($lastmonth.year, $lastmonth.month)"


回答2:

I made a function library that has these functions. Doing what you want with this library is easy, check below.

The functions are at the end of the code. Just copy to the end of your file (after a goto:eof) and call them using specified arguments.

The given code inside :main should do what you are asking for.

Remarks:

  • Has inbuilt Leap Year check, so if the year is a Leap Year, Februrary will return 29 days.
  • When copying functions to your file, please keep Author information

Code:

@echo off
:main
  :: Recommended to use GetDateIntl function, to get region-independent results (Check functions at the end of this file)
  call :GetDateIntl Year Month Day /A
  echo/Using GetDateIntl to get current date: %Year%-%Month%-%day%
  echo/

  :: Call function to get last day of this month (Check functions at the end of this file)
  call :GetLastDayOfMonth %Month% %Year% LastDay
  echo This month is: %Month%/%Year%
  echo First day of any month is always 1
  echo Last day of this month is: %LastDay%
  echo/

  :: Get previous month number
  set /A "PMonth= %Month% - 1"
  set "PYear=%Year%"
  :: Correct PMonth and PYear if this month is December
  if %PMonth%==0 ( set "PMonth=12" & set /A "PYear= %PYear% - 1" )

  :: Call function to get last day of previous month (Check function after :main)
  call :GetLastDayOfMonth %PMonth% %PYear% PLastDay
  echo Previous month is: %PMonth%/%PYear%
  echo Last day of previous month is: %PLastDay%

goto:eof


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:GetDateIntl yy mm dd [/A]
:: Returns the current date on any machine with regional-independent settings
:: Arguments:
::   yy = variable name for the year output
::   mm = variable name for the month output
::   dd = variable name for the day output
::   /A = OPTIONAL, removes leading 0 on days/months smaller than 10
::
:: By Cyberponk,    v1.1 - 11/05/2016
::          v1.0 - 2015
::
SETLOCAL ENABLEEXTENSIONS
if "%date%A" LSS "A" (set toks=1-3) else (set toks=2-4)
for /f "tokens=2-4 delims=(-)" %%a in ('echo:^|date') do (
  for /f "tokens=%toks% delims=.-/ " %%i in ('date/t') do (
    set '%%a'=%%i
    set '%%b'=%%j
    set '%%c'=%%k
  )
)
if "%'yy'%"=="" set 'yy'=%'aa'%
if %'yy'% LSS 100 set 'yy'=20%'yy'%
endlocal&set %1=%'yy'%&set %4 %2=%'mm'%&set %4 %3=%'dd'%&goto :EOF
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:IsLeapYear %year% IsLeap
:: Checks if a year is a Leap Year (year that has 366 days)
:: Arguments:
::   %year% = year number input. Can be a number or a variable containing a 4 digit number
::   IsLeap = result output containing 1 or 0 (1= is leap year, 0= not yeap lear)
::
:: By Cyberponk,    v1.1  - 11/05/2016
::
SETLOCAL ENABLEEXTENSIONS
  set /A "IsLeap=(!(%1 %% 4) & !!(%1 %% 100)) | !(%1 %% 400)"
endlocal & set %2=%IsLeap%& goto:EOF
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:GetLastDayOfMonth %month% %year% LastDay
:: Gets the last day of a given month from a given year
:: Arguments:
::   %month% = month number input. Can be a number or a variable containing a number
::   %year%  = year for reference (used to check for leap years)
::   LastDay = result output containing last day of the specified month
::
:: By Cyberponk,    v1.0  - 11/05/2016
::
SETLOCAL ENABLEEXTENSIONS
  :: Mathematical formula to last day of month
  set /A "LastDay= 28 + (%1 + (%1/8)) %% 2 + 2 %% %1 + 2 * (1/%1)

  :: Add 1 day if year is a Leap Year and month is February
  call :IsLeapYear %2 IsLeap  
  if %LastDay%==28 set /A LastDay=%LastDay% + %IsLeap%
endlocal & set %3=%LastDay%& goto:EOF
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


回答3:

here's a piece of code that will get the previous month:

@echo off

for /f "" %%# in ('WMIC Path Win32_LocalTime Get month /format:value') do (
    for /f %%Z in ("%%#") do set /a %%Z-1
)
if %month% equ 0 set month=12
echo %month%

Better use wmic to get the date parts because it's independent from the machine's date format unlike the %date% variable.

EDIT (as requested in the comments) :

@echo off


setlocal disableDelayedExpansion
for /f "" %%# in ('"WMIC Path Win32_LocalTime Get month,year /format:value"') do (
    for /f %%Z in ("%%#") do set "%%Z"
)
set /a month=month-1
if %month% equ 0 set month=12
rem echo %month% -- %year%
set /A "leap=!(year&3) + (!!(year%%100)-!!(year&15))"
if %leap% equ 0 (set "m2=29") else (set "m2=28")

setlocal enableDelayedExpansion
set m1=31
set m3=31
set m4=30
set m5=31
set m6=30
set m7=31
set m8=31
set m9=30
set m10=31
set m11=30
set m12=31

set premonLastDay=!m%month%!




endlocal & set premonLastDay=%premonLastDay%
if %month% equ 12 (

        set /a year=year-1

)

echo %premonLastDay%-%month%-%year%

endlocal