12

Given two numbers, month and year, how can I compute the first and the last day of that month ? My goal is to output these three lines:

  1. month / year (month in textual form but that is trivial)

  2. for each day of the month: name of the day of the week for the current day: Fri. & Sat. & Sun. [...]

  3. day number within the month: 1 & 2 & 3 [...] & 28 & .. ?

I'm looking for a solution using GNU date or BSD date (on OS X).

0

5 Answers 5

22
# Last month:
l_first_date=$(date -d "`date +%Y%m01` -1 month" +%Y-%m-%d)
l_last_date=$(date -d "`date +%Y%m01` -1 day" +%Y-%m-%d)

# This month:
t_first_date=$(date +%Y-%m-01)
t_last_date=$(date -d "`date +%Y%m01` +1 month -1 day" +%Y-%m-%d)

# Next month:
n_first_date=$(date -d "`date +%Y%m01` +1 month" +%Y-%m-%d)
n_last_date=$(date -d "`date +%Y%m01` +2  month -1 day" +%Y-%m-%d)

# Print everything
echo "Last month: $l_first_date to $l_last_date"
echo "This month: $t_first_date to $t_last_date"
echo "Next month: $n_first_date to $n_last_date"
1
  • Pure gold, the nested date is perfect in cron one-liners. thanks! Please note also the nested $() will work e.g. d=$(date -d "$(date +%Y%m01) -3 month" +%Y-%m-%d
    – neurino
    Jun 22, 2021 at 9:55
8

I'll be honest; from the way you're asking the question I get the sense you've been assigned some homework, so I'll leave a few steps out of the answer as an exercise for the reader:

You'll want to take a good look at the date manual page; especially the -d flag, which allows you to examine any given day. The first day of month M in year Y would be "M/01/Y"

Getting the last day of the month, your best bet is to add 1 to the number of the month you were given, then deduct one day in the date.

Hint: date can actually accept some extensive arithmetic; I can, for instance, say date -d "01/07/2012 + 1 month - 1 day" and it will give me the correct answer.

You can find out how to display the output you want in 2) and 3) by studying the "format" section of the date manpage.

Hints: Look at %a and %d

1
  • The +"format string" parameter to date will also be helpful here, e.g. date -d "01/07/2012 + 1 month - 1 day" +"%d" Jul 17, 2012 at 11:32
6

Some time ago I had similar issue. There is my solution:

 $ ./get_dates.sh 2012 07
The first day is 01.2012.07, Sunday
The last day is 31.2012.07, Tuesday
 $ cal
 July 2012
Su Mo Tu We Th Fr Sa
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

Script itself:

#!/bin/bash
# last day for month
lastday()  {
#                ja   fe   ma   ap   ma   jn   jl   ag   se   oc   no   de
  mlength=('xx' '31' '28' '31' '30' '31' '30' '31' '31' '30' '31' '30' '31')

  year=$1
  month=$2

  if [ $month -ne 2 ] ; then
    echo ${mlength[$month]}
    return 0
  fi

  leap=0
  ((!(year%100))) && { ((!(year%400))) && leap=1 ; } || { ((!(year%4))) && leap=1 ; }

  feblength=28
  ((leap)) && feblength=29
  echo $feblength
}

# date to Julian date
date2jd() {

  year=$1
  month=$2
  day=$3
  lday=$(lastday $year $month) || exit $?

  if ((day<1 || day> lday)) ; then
    echo day out of range
    exit 1
  fi

  echo $(( jd = day - 32075
                + 1461 * (year + 4800 - (14 - month)/12)/4
                        + 367 * (month - 2 + (14 - month)/12*12)/12
                    - 3 * ((year + 4900 - (14 - month)/12)/100)/4
                - 2400001 ))
}

jd2dow()
{
  days=('Sunday' 'Monday' 'Tuesday' 'Wednesday' 'Thursday' 'Friday' 'Saturday')

  jd=$1
  if ((jd<1 || jd>782028)) ; then
    echo julian day out of range
    return 1
  fi

  ((dow=(jd+3)%7))

  echo ${days[dow]}
}


echo -n "The first day is 01.$1.$2, "
jd2dow $(date2jd $1 $2 01)
echo -n "The last day is $(lastday $1 $2).$1.$2, "
jd2dow $(date2jd $1 $2 $(lastday $1 $2))

I didn't have GNU date on machines I need it, therefore I didn't solve it with date. May be there is more beautiful solution.

3
date -d "20121101 + 1 month - 1 day" +%Y%m%d
1
  • 7
    At least you should specify which of the question's 3 points are answered by your code.
    – manatwork
    Nov 1, 2012 at 9:06
3

Previous Month Start and End date

month_year=$(date +'%m %Y' | awk '!--$1{$1=12;$2--}1')
m=${month_year% *}
y=${month_year##* }
d=$(cal $m $y | paste -s - | awk '{print $NF}')
first_date=$(printf '01-%02s-%s' $m $y)
last_date=$(printf '%s-%02s-%s' $d $m $y)
echo $first_date $last_date

Current Month Start and End date

month_year=$(date +'%m %Y' | awk '!$1{$1=12;$2--}1')
m=${month_year% *}
y=${month_year##* }
d=$(cal $m $y | paste -s - | awk '{print $NF}')
first_date=$(printf '01-%02s-%s' $m $y)
last_date=$(printf '%s-%02s-%s' $d $m $y)
echo $first_date $last_date

Next Month Start and End date

month_year=$(date +'%m %Y' | awk '!++$1{$1=12;$2--}1')
m=${month_year% *}
y=${month_year##* }
d=$(cal $m $y | paste -s - | awk '{print $NF}')
first_date=$(printf '01-%02s-%s' $m $y)
last_date=$(printf '%s-%02s-%s' $d $m $y)
echo $first_date $last_date

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .