First and last day of a month

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).

• What OS are you using? It might help others give you the right answer. – Kevdog777 Jul 17 '12 at 10:35

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.

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

• The +"format string" parameter to date will also be helpful here, e.g. date -d "01/07/2012 + 1 month - 1 day" +"%d" – daniel kullmann Jul 17 '12 at 11:32
• @Antoine: The world is a bigger place then you might have realized. There's lots of places that will have school on any given July day. – Christian Sep 30 '13 at 14:11
• i got this need in a real life script, not sure why one would directly assume this is homework, and @apseyy 's answer is just perfect for my need – mazs Apr 29 at 15: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 -d "`date +%Y%m01`" +%Y-%m-%d)
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"
**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

**Currunt 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
date -d "20121101 + 1 month - 1 day" +%Y%m%d
• At least you should specify which of the question's 3 points are answered by your code. – manatwork Nov 1 '12 at 9:06