72

I am using below to get previous, current and the next month under Ubuntu 11.04:

LAST_MONTH=`date +'%m' -d 'last month'`
NEXT_MONTH=`date +'%m' -d 'next month'`
THIS_MONTH=`date +'%m' -d 'now'`

It works well until today, the last day of October, 2012 (2012-10-31)

I get below result as of now:

$ date
Wed Oct 31 15:35:26 PDT 2012
$ date +'%m' -d 'last month'
10
$ date +'%m' -d 'now'
10
$ $ date +'%m' -d 'next month'
12

I suppose the outputs should be 9,10,11 respectively.

Don't understand why date outputs behave like this. What should be a good way to get consistant previous, current and next month instead?

greeness
  • 15,956
  • 5
  • 50
  • 80
  • Can't you just get the month for `now` and then add or subtract 1? – Lev Levitsky Oct 31 '12 at 22:46
  • Thanks. I understand this works provided you take care of `Dec + 1 = Jan` etc. My question is why the above does not work. – greeness Oct 31 '12 at 22:49
  • You can get more insight by printing full date calculated for "last month" and "next month". I don't have a unix terminal right now, but my guess is that it just adds/subtracts 30 days to/from `now`. – Lev Levitsky Oct 31 '12 at 22:53

5 Answers5

132

The problem is that date takes your request quite literally and tries to use a date of 31st September (being 31st October minus one month) and then because that doesn't exist it moves to the next day which does. The date documentation (from info date) has the following advice:

The fuzz in units can cause problems with relative items. For example, `2003-07-31 -1 month' might evaluate to 2003-07-01, because 2003-06-31 is an invalid date. To determine the previous month more reliably, you can ask for the month before the 15th of the current month. For example:

 $ date -R
 Thu, 31 Jul 2003 13:02:39 -0700
 $ date --date='-1 month' +'Last month was %B?'
 Last month was July?
 $ date --date="$(date +%Y-%m-15) -1 month" +'Last month was %B!'
 Last month was June!
TomH
  • 8,900
  • 2
  • 32
  • 30
  • 2
    It doesn't move to the next day, it takes the number of days in the prior month and subtracts it from DoY, based on what I've seen. date --date="03/31/2015 -1 month" +'Date: %m-%d-%Y' returns "Date: 03-03-2015". 3/31/2016 returns 3/2/2016. I was just bit by the -1 month thing myself! I love the solution though. – Iamiuru Dec 31 '14 at 15:28
  • 1
    Note: I wanted to use this approach, but I was stuck with BusyBox date, which is a bit less fully-featured and doesn't have the time arithmetic built in. This slightly ridiculous modification worked instead: `date -d $(date +%Y)-$(( $(date +%m) - 1 ))-15 +%B`. And yes, this works over years: it's quite happy to accept zero (December) and negative (counting backwards) months. – Aesin Dec 02 '17 at 01:11
  • Assuming that every month=31 days is not taking it "literally", it's showing that the `date` utility is not up to the task. – iconoclast May 08 '18 at 00:03
  • BusyBox v1.29.3 fails to implement "-1 month" and "-v-1m" – user1133275 Sep 29 '18 at 12:52
  • An example of when this is needed is running it in the first hour of April, which in the UK is shortly after the loss of an hour in the clocks change by an hour. Without the 15th day adjustment, this returns February. – fooquency Mar 31 '20 at 23:52
25

the following will do:

date -d "$(date +%Y-%m-1) -1 month" +%-m
date -d "$(date +%Y-%m-1) 0 month" +%-m
date -d "$(date +%Y-%m-1) 1 month" +%-m

or as you need:

LAST_MONTH=`date -d "$(date +%Y-%m-1) -1 month" +%-m`
NEXT_MONTH=`date -d "$(date +%Y-%m-1) 1 month" +%-m`
THIS_MONTH=`date -d "$(date +%Y-%m-1) 0 month" +%-m`

you asked for output like 9,10,11, so I used the %-m

%m (without -) will produce output like 09,... (leading zero)

this also works for more/less than 12 months:

date -d "$(date +%Y-%m-1) -13 month" +%-m

just try

date -d "$(date +%Y-%m-1) -13 month"

to see full result

guest
  • 251
  • 3
  • 2
18

If you happen to be using date in a MacOS environment, try this:

ST1:~ ejf$ date
Mon Feb 20 21:55:48 CST 2017
ST1:~ ejf$ date -v-1m +%m
01
ST1:~ ejf$ date -v+1m +%m
03

Also, I'd rather calculate the previous and next month on the first day of each month, this way you won't have issues with months ending the 30/31 or 28/29 (Feb/Feb leap year)

estebanfallasf
  • 197
  • 1
  • 2
3

the main problem occur when you don't have date --date option available and you don't have permission to install it, then try below -

Previous month
#cal -3|awk 'NR==1{print toupper(substr($1,1,3))"-"$2}'
DEC-2016 
Current month
#cal -3|awk 'NR==1{print toupper(substr($3,1,3))"-"$4}'
JAN-2017
Next month
#cal -3|awk 'NR==1{print toupper(substr($5,1,3))"-"$6}'
FEB-2017
VIPIN KUMAR
  • 3,019
  • 1
  • 23
  • 34
2

Previous month:

date -d "1 month ago" +'%m'

Current month:

date +'%m'

Next month:

date -d "-1 month ago" +'%m'
ZygD
  • 22,092
  • 39
  • 79
  • 102