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

Contents

### Method 1

``````# 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"
``````

### Method 2

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`

### Method 3

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.

### Method 4

`date -d "20121101 + 1 month - 1 day" +%Y%m%d`

### Method 5

#### 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
`````` 