4

I am creating a script that collects information from the previous month used for reporting:

So far I have the below variables:

#I used an array to get the previous month. 
set -A months Dec Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov
x=`date +%m`  
y=`expr $x - 1`
year=`date +%Y`
month=`perl -MPOSIX=strftime -le '@t = localtime; $t[3] = 1; $t[4]--;print strftime("%m", @t)'`
month_abv=`echo ${months[$y]}`

Variable output:

year=2017
month=04
month_abv=Apr

All I need now is the last day of the previous month.

5 Answers5

5

You could calculate one day before the first day of this month, using GNU date:

date -d "$(date +"%Y-%m-01") - 1 day" +"%F"

or using dateutils's dateadd:

dateadd "$(date +"%Y-%m-01")" "-1d"

or a bit tricky dateutils usage. Round to the last 31th month day. (Documented behavior which works even for months with less than 31 days.)

$ dateround --next today -31d
2017-04-30
$ dateround --next "2016-03-10" -31d
2016-02-29
rudimeier
  • 10,315
  • These are often not available on HP-UX. – Gilles 'SO- stop being evil' May 09 '17 at 21:24
  • 1
    The reason why I did not use date -d, because it does not work in HPUX unfortunately – Christopher Karsten May 10 '17 at 12:10
  • @ChristopherKarsten I know. My solution is about either installing "dateutils" or "GNU date". There is no single operating systems on the planet where dateutils is installed by default. But nevertheless there are people who use it ... ;) – rudimeier May 10 '17 at 12:25
  • @rudimeier, have anyone installed dateutils on HP-UX? I don't seem to find any matches on Google. – saulius2 Sep 03 '20 at 07:09
  • @saulius2 It should be possible to build dateutils from the the released source tarballs: https://bitbucket.org/hroptatyr/dateutils/downloads/ , if something does not work, the author will be happy about a bug-report/question: https://github.com/hroptatyr/dateutils/issues – rudimeier Sep 07 '20 at 15:46
2

The way I went about getting the last day of the previous month is:

cal $month $year | awk 'NF {DAYS = $NF}; END {print DAYS}'

Using the varaible placeholders I am able to get the previous months calander and printing the last day of the month:

cal 04 2017 | awk 'NF {DAYS = $NF}; END {print DAYS}'

Output:

30

If there is an easier way or shorter way please, I am open to all suggestions.

2

Try:

eval "$(
  LC_ALL=C perl -MPOSIX -le '
    @t = localtime;
    $t[3]=0; # set day to 0 (day before the first)
    print strftime q(year=%Y month=%m day=%d month_abv="%b"), @t'
)"

perl constructs some shell code (like year=2017 month=04 day=30 month_abv="Apr") which we then evaluate into the current shell (assuming a POSIX shell).

LC_ALL=C is to force the month_abv to be in English regardless of the locale. Remove it if you'd rather it be in the user's locale.

That day=0 trick works with the strftime() of GNU, FreeBSD and Solaris, but I don't know how portable it is outside of that.

To get the first day, of the previous month, replace $t[3]=0 with $t[3]=1; $t[4]-- which for me works even if run in January but again, I'm not sure how portable it is.

  • I know this nice strftime, mktime and struct tm behavior. But I always wondered whether this is documented somewhere in perl or POSIX documentation. I could only find it for glibc (man mktime). – rudimeier May 09 '17 at 14:48
  • @rudimeier, I can't say the POSIX specification is very clear on that. values[...] shall not be restricted to the ranges described in <time.h>, but that's not saying how the time should be calculated if you use 1234 as the day of the month number or a negative month. – Stéphane Chazelas May 09 '17 at 15:05
  • Interesting, in POSIX 2003 was "components are not restricted" instead of shall. Sounds somehow weaker now. – rudimeier May 09 '17 at 16:05
2

The below seems to work OK for me on ksh88 (thanks to Stéphane Chazelas for some assistance with this)

#! /bin/ksh
eval "$(date +'y=%Y m=%m')"
echo "Current month = $m"
echo "Current year = $y"

#get previous month (and year of previous month)
if [[ $((m-1)) -gt 0 ]]
then
    p_m=$((m-1))
    p_m_y=$((y))
else
    p_m=12
    p_m_y=$((y-1))
fi
echo "p_m = $p_m"
echo "p_m_y = $p_m_y"

#get first/last day of previous month
p_m_from=01.${p_m}.${p_m_y}
p_m_to=$(cal $p_m $p_m_Y | grep -v ^$ | tail -1 | sed 's/^.* \([0-9]*\)$/\1/').${p_m}.${p_m_y}
echo "Previous month runs from $p_m_from to $p_m_to"
0

To find previous month follow this:

date -d "-1 months" +%Y/%-m/%-d

This will give you: 2018/2/22 while it is 2018/3/22.

date -d "-1 days" +%Y/%-m/%-d

Will give you: 2018/3/21

You can add or subtract as many months or days you like. If you subtract enough months it will go to last year.

Kevdog777
  • 3,224