Script to find n.of weekdays and n.of weekends in a given date

Hi All,

Could you please provide the shell script to find number of weekdays and
number of weekends for a given date for that month.

Monday to friday should be considered as weekdays and Saturday and Sunday should be considered as weekends.

Date should be passed as parameter.

For example
if we pass date as 2016-09-10 then
O/P weedays weekends
22 8

if we pass date as 2016-02-12 then
O/P weedays weekends
21 8

if we pass date as 2016-08-05 then
O/P weedays weekends
23 8

Please help me.

Thanks

Help me out - how can a single date be a number of weekdays and/or weekends? And, what is "weekends" in your context - two days per weekend?

And, should you want just the number of respective days for that month, neglecting the day, none of your examples is correct.

So - what are you after?

Hi,
We will be passing date but we have to find number of weekdays and
number of weekends in that month of the year.

Monday to Friday should be considered as weekdays and
Saturday and Sunday should be considered as weekends.

Please help me.

Thanks

---------- Post updated at 07:05 PM ---------- Previous update was at 06:20 PM ----------

Hi Rudic,

Please help me.

Thanks.

Hello ROCK_PLSQL,

Could you please try following and let me know if this helps you.

cat scrip.ksh 
VAL=$1
MONTH=`echo $1 | awk -F"-" '{print $2}'`
YEAR=`echo $1 | awk -F"-" '{print $3}'`
cal $MONTH $YEAR | awk 'NR>2 && NF{if($0 ~ /^[[:space:]]/){A[$NF]++;};if(($0 !~ /^[[:space:]]/) && NF<7){B[$1]++;};if(NF==7){B[$1]++;A[$NF]++};Q=$NF} END{print "Number of weekdays are:" Q-length(A)-length(B) ORS "Number of weekdays are:" length(A)+length(B);}'

Now when I run above script.ksh with an argument(as shown by you in very first post), following will be the output.

./scrip.ksh 12-2-15
Number of weekdays are:20
Number of weekdays are:8

./scrip.ksh 12-10-14
Number of weekdays are:23
Number of weekdays are:8

./scrip.ksh 12-01-22
Number of weekdays are:22
Number of weekdays are:9

So you could do cal month year for above example dates and cross check the output too.

cal 2 15
     February 15    
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 2

cal 10 14
     October 14     
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

cal 1 22
     January 22     
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

EDIT: Adding non-one liner form of solution as follows too.

cat script.ksh
VAL=$1
MONTH=`echo $1 | awk -F"-" '{print $2}'`
YEAR=`echo $1 | awk -F"-" '{print $3}'`
cal $MONTH $YEAR | awk 'NR>2 && NF{
					if($0 ~ /^[[:space:]]/){
								A[$NF]++;
							       };
					if(($0 !~ /^[[:space:]]/) && NF<7){
										B[$1]++;
								          };
					if(NF==7){
							B[$1]++;
							A[$NF]++
						 };
					Q=$NF
			          } 
		        END       {
					print "Number of weekdays are:" Q-length(A)-length(B) ORS "Number of weekdays are:" length(A)+length(B);
				  }
                       '

I hope this helps you.

NOTE: Above code is tested in BASH and GNU awk .

Thanks,
R. Singh

Hi Singh,

Thanks for your response.

The script is giving correct result.

Instead of printing this result, it should be assigned to variables since I have to pass this result to hive script.

Instead of

Number of weekdays are:20
Number of weekdays are:8

to should be assigned to variables

var120
var28

Please help me.

Thanks

Hello ROCK_PLSQL,

Please use code tags in all your posts as per forum rules for Inputs/commands/codes which you are using into your post. Could you please try following and let me know if this helps you.

VAL=$1
MONTH=`echo $1 | awk -F"-" '{print $2}'`
YEAR=`echo $1 | awk -F"-" '{print $3}'`
var120=`cal $MONTH $YEAR | awk 'NR>2 && NF{
                                        if($0 ~ /^[[:space:]]/){
                                                                A[$NF]++;
                                                               };
                                        if(($0 !~ /^[[:space:]]/) && NF<7){
                                                                                B[$1]++;
                                                                          };
                                        if(NF==7){
                                                        B[$1]++;
                                                        A[$NF]++
                                                 };
                                        Q=$NF
                                  } 
                        END       {
                                        print Q-length(A)-length(B);
                                  }
                       '`

var28=`cal $MONTH $YEAR | awk 'NR>2 && NF{
                                        if($0 ~ /^[[:space:]]/){
                                                                A[$NF]++;
                                                               };
                                        if(($0 !~ /^[[:space:]]/) && NF<7){
                                                                                B[$1]++;
                                                                          };
                                        if(NF==7){
                                                        B[$1]++;
                                                        A[$NF]++
                                                 };
                                        Q=$NF
                                  } 
                        END       {
                                        print length(A)+length(B);
                                  }
                       '`
echo $var120
echo $var28

If these variabes needed to be used in any other codes, you could remove echo s at last(which I have used to print their values).

Thanks,
R. Singh

Thanks a lot for your help.

The sample dates that were provided in post #1 seem to be in the format YYYY-MM-DD (although from the examples supplied and the lack of any specification, they could also be in the format YYYY-DD-MM ). RavinderSingh13's code seems to be treating the dates as though they were in the format DD-MM-YY . (Note that the outputs from cal shown in post #4 in this thread are from the years 0015, 0014, and 0022.) The code suggested in post #6 works correctly for inputs in the format DD-MM-YYYY , but not for inputs in the format YYYY-MM-DD . Note also that it invokes cal twice and awk four times for each date processed.

You might want to consider the following script which processes inputs in the format YYYY-MM-DD and only invokes cal once and awk once for each date processed:

#!/bin/ksh
IAm=${0##*/}
if [ $# -eq 0 ]
then	printf 'Usage: %s YYYY-MM-DD...\n' "$IAm" >&2
	exit 1
fi
# Process each operand given...
while [ $# -gt 0 ]
do	# Grab year from start of the operand.
	year=${1%%-*}
	# Throw away the day from the end of the operand.
	month=${1%-*}
	# Throw away the year (leaving the month) from the operand.
	month=${month#*-}
	# Read the desired variable values from the following awk script...
	read var120 var28 <<-EOF
		$(cal $month $year | awk '
			NR > 2 && NF {
				# We have skipped over the leading header lines
				# and any empty lines, so what we have left are
				# the days in a given week.  If we have seven
				# days in this week, we have 2 weekend days and
				# 5 weekdays, otherwise we have one weekend day
				# and NF - 1 weekdays.
				weekenddays += 1 + (NF == 7)
				weekdays += NF - 1 - (NF == 7)
			}
			END {	# Print the results...
				print weekdays, weekenddays
			}
		')
	EOF
	printf '%s %d week-days and %d weekend-days in %s-%s\n' \
	    'Do whatever you want with' "$var120" "$var28" "$year" "$month"
	shift
done

If you save this script in a file named tester and make it executable, invoking it with:

./tester 2016-09-10 2016-02-12 2016-08-05 2015-02-12 2016-01-31

it produces the output:

Do whatever you want with 22 week-days and 8 weekend-days in 2016-09
Do whatever you want with 21 week-days and 8 weekend-days in 2016-02
Do whatever you want with 23 week-days and 8 weekend-days in 2016-08
Do whatever you want with 20 week-days and 8 weekend-days in 2015-02
Do whatever you want with 21 week-days and 10 weekend-days in 2016-01

This was written and tested using the Korn shell on OS X, but will work with any shell that performs basic shell parameter expansions required by the POSIX standards. As always, if you want to try this on a Solaris/SunOS system, change awk to /usr/xpg4/bin/awk or nawk .

1 Like

Thank you Don for nice code. I made little modifications into my previous code and now we need not to run cal and awk 1 time.

Hello ROCK_PLSQL,

You could give a try to following code too and as Don mentioned above too, you could save following values(I given in comments which variable value is which) to further as per your use too.

cat script_cal.ksh 
MONTH=`echo $1 | cut -d- -f2`
YEAR=`echo $1 | cut -d- -f3`
VAL=`cal $MONTH $YEAR | awk 'NR>2 && NF{
                    if($0 ~ /^[[:space:]]/){
                                A[$NF]++;
                                   };
                    if(($0 !~ /^[[:space:]]/) && NF<7){
                                        B[$1]++;
                                          };
                    if(NF==7){
                            B[$1]++;
                            A[$NF]++
                         };
                    Q=$NF
                      } 
                END       {
                    print Q-length(A)-length(B)"-"length(A)+length(B);
                  }
                       '`
echo ${VAL##*-}  ##This is value of weekends in a month var28
echo ${VAL%%-*}  ##This is value of weekdays in a month var120

NOTE: Date should be passed into DD-MM-YYYY format.

Thanks,
R. Singh

#!/bin/bash

GIVEN_DATE=${1}
YEAR=$(echo ${GIVEN_DATE} | cut -d- -f1)
MONTH=$(echo ${GIVEN_DATE} | cut -d- -f2)
cal ${MONTH} ${YEAR} | awk 'NR>2&&NF!=0{if(NF==7){w+=2;}else{w+=1;}E=$NF}END{print w,E-w}' | while read WEEKEND WEEKDAY
do
	echo "Week Day : ${WEEKDAY}"
	echo "Week End : ${WEEKEND}"
done

Hi Ravinder,
Note that cal doesn't treat a 2-digit year operand as a year in the late 20th or early 21st century like the touch -t option-argument does; cal treats it as a literal 2 digit year in the 1st century. For example, look at the difference between February 2004 and February 04:

$ cal 2 2004
   February 2004
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

$ cal 2 04
     February 4
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

$ 

(Note: 8 weekend days in year 2004 and 9 weekends days in year 4.) So, if you're going to use cal , the entire year has to be supplied; not just the last two digits.

Note that using cut instead of awk to split month, day, and year out of the given operand still involves the very expensive fork and exec operations that could be done MUCH quicker just using parameter expansions built into the shell.

1 Like

Do you have the ncal command available? Try

set 2016-09-10
TMP=${1#*-}; ncal ${TMP%-*} ${1%%-*} | awk 'NR>1 && NR<7 {WD+=NF-1} NR>=7 {WE+=NF-1} END {print WD, WE}'
22 8

EDIT: Or, just shell:

TMP=${1#*-}; ncal ${TMP%-*} ${1%%-*} | { read; while read A; do set $A; case $1 in S?) (( WE+=$#-1));; *) (( WD+=$#-1)); esac;  done; echo $WD, $WE; }
22, 8

Hi.

I've mentioned the dateutils suite of date utilities before. The reason that I like the codes is that they make sure the dates exist, they diagnose bad forms, etc.

Applied here to the problem, we first get a year-month sequence ( dconv ), then add a month ( dadd ), then generate a sequence of the days in the month ( dseq ), omitting the first day of the next month.

Then it's a matter of counting weekdays and weekENDS, a short grep-wc pipeline:

#!/usr/bin/env bash

# @(#) s1       Demonstrate count weekdays, weekENDs in a given month.

# Utility functions: print-as-echo, print-line-with-visual-space, debug.
# export PATH="/usr/local/bin:/usr/bin:/bin"
LC_ALL=C ; LANG=C ; export LC_ALL LANG
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
em() { pe "$*" >&2 ; }
db() { ( printf " db, ";for _i;do printf "%s" "$_i";done;printf "\n" ) >&2 ; }
db() { : ; }
C=$HOME/bin/context && [ -f $C ] && $C dateutils.dconv dateutils.dadd dateutils.dseq

pe
if [ $# -gt 0 ]
then
  set -- 2016-9.11
else
  set -- 2016-09-10 2016-02-12 2016-08-05 2012-2-15 2012-10-14 2012-01-22 2017-02-29 2016-02-29
fi

for YMD
do
  # YearMonthNow, YearMonthFuture.
  dateutils.dconv -f "%Y-%m" "$YMD" > /dev/null
  ES=$?
  db " Exit status of dconv is :$ES: for date $YMD"
  if [ $ES -ne 0 ]
  then
        pe
    em " Skipping input, date has bad form or is impossible: \"$YMD\""
        continue
  else
        YMN=$( dateutils.dconv -f "%Y-%m" "$YMD" )-01
  fi
  db " YMN is $YMN"
  YMF=$( dateutils.dadd -f "%Y-%m" "$YMD" +1m )-01
  db " YMF is $YMF"
  ymd=$( dateutils.dseq -i "%Y-%m-%d" -f "%F %a" $YMN $YMF | sed '$d' )
  db " ymd = $ymd"
  
  weekdays=$( echo "$ymd" |
    grep -E 'Mon|Tue|Wed|Thu|Fri' |
  wc -l)
  weekends=$( echo "$ymd" |
    grep -E 'Sat|Sun' |
  wc -l )
  
  pl " For date $YMD:"
  pe " Weekdays = $weekdays"
  pe " WeekENDS = $weekends"
done

exit 0

producing:

$ ./s1

Environment: LC_ALL = C, LANG = C
(Versions displayed with local utility "version")
OS, ker|rel, machine: Linux, 3.16.0-4-amd64, x86_64
Distribution        : Debian 8.4 (jessie) 
bash GNU bash 4.3.30
dateutils.dconv dconv 0.3.1
dateutils.dadd dadd 0.3.1
dateutils.dseq dseq 0.3.1


-----
 For date 2016-09-10:
 Weekdays = 22
 WeekENDS = 8

-----
 For date 2016-02-12:
 Weekdays = 21
 WeekENDS = 8

-----
 For date 2016-08-05:
 Weekdays = 23
 WeekENDS = 8

-----
 For date 2012-2-15:
 Weekdays = 21
 WeekENDS = 8

-----
 For date 2012-10-14:
 Weekdays = 23
 WeekENDS = 8

-----
 For date 2012-01-22:
 Weekdays = 22
 WeekENDS = 9

 Skipping input, date has bad form or is impossible: "2017-02-29"

-----
 For date 2016-02-29:
 Weekdays = 21
 WeekENDS = 8

Included is a date in February that would not exist next year (2017), but exists this year (2016 being a leap year).

Best wishes ... cheers, drl

#input date as 20160912
M=$1
Start=`expr  $M - $M % 100`
Start=`expr $Start + 1`
End=`expr $Start + 100`
echo $Start $End
sj=`date --date="$Start" +%j`
ej=`date --date="$End" +%j`
lj=`expr  $ej - $sj`
echo $ej $sj $lj
i=1
weekend=0
weekday=0
while [ $i -le $lj ]
do
test_day=`date --date="$Start" +%u`
if [ $? -eq 0 ]
then
  echo $Start $test_day
  if [ $test_day -gt 5 ]
  then
   weekend=`expr $weekend + 1`
  else
   weekday=`expr $weekday + 1`
  fi
fi
Start=`expr $Start + 1`
i=`expr $i + 1`
done 
echo weekdays $weekday
echo weekends $weekend