Perl script

Hi All,
I have a question here in perl script
Suppose I have a date 12/22/2011(mm/dd/yyyy) what i have to do is to find
the start date of the week in which that date lies and end date of that week. In this scenario the start date will be 12/19/2011 and the end date will be 12/25/2011:b:

here is your code :slight_smile: (
p.s. i am a novice in shell scripting.. so i dont know what perl script is...
)

datee="12/22/2011"
len=${#datee}
mm=${datee:0:2}
dd=${datee:3:2}
yy=${datee:6:4}
mydate=$(date -d 12/22/2011)
day=$( echo $mydate | awk -F' ' '{print $1}' )
 
startdate=0
 
var1="Sun"
var2="Mon"
var3="Tue"
var4="Wed"
var5="Thu"
var6="Fri"
var7="Sat"
no1=0
no2=1
no3=2
no4=3
no5=4
no6=5
no7=6
 
for i in $(seq 1 7)
do
        val=$( eval eval echo \$var$i )
        num=$( eval eval echo \$no$i )
        if [[ "$val" == "$day" ]]
        then
                number=`expr $dd - $num + 1`
                echo "weekstart: $mm/$number/$yy"
                date -d $mm/$number/$yy
                number2=`expr $dd + 7 - $num`
                echo "weekend: $mm/$number2/$yy"
                date -d $mm/$number2/$yy
        fi
done
 

output:

weekstart: 12/19/2011
Mon Dec 19 00:00:00 EST 2011
weekend: 12/25/2011
Sun Dec 25 00:00:00 EST 2011

The +%u format specifier to the date command returns a numeric value for the day of the week ...

Alternatively the Perl localtime function returns in an array context has the day of the week at index 6.

date +"%u"

gives current days numeric value of the week... if i want to find that of 12/19/2011 what would be the syntax?

If u have -d option, u can use it

date -d '12/22/11' +'%u'

oh okay thanks :slight_smile:

@vivek_d_r: What would your program print if input date is 12/01/2011 in mm/dd/yyyy????

@parthmittal2007: Here's the program you asked for:
[highlight=perl]#! /usr/bin/perl -w
use strict;
use Time::Local;

my ($mt, $dt, $yr, $sec, $wk_st, $wk_nd, $st_date, $nd_date);

(@ARGV != 1) && die "Invalid parameters. Enter date in mm/dd/yyyy format. Exiting";
($ARGV[0] !~ /^(0[1-9]|1[012])\/(0[1-9]|[12][0-9]|3[01])\/[\d]{4}$/)
&& die "Invalid date format. Enter date in mm/dd/yyyy. Exiting";

$mt = substr $ARGV[0], 0, 2;
$dt = substr $ARGV[0], 3, 2;
$yr = substr $ARGV[0], 6, 4;

$sec = timelocal (0, 0, 0, $dt, $mt - 1, $yr);
$wk_st = $sec - (((localtime ($sec))[6] - 1) * 86400);
$wk_nd = $sec + ((6 - ((localtime ($sec))[6] - 1)) * 86400);

$st_date = localtime ($wk_st);
$nd_date = localtime ($wk_nd);

print "Week Start date: $st_date\n";
print "Week End date: $nd_date\n";[/highlight]

[root@hostname test]# ./test.pl 12/01/2011
Week Start date: Mon Nov 28 00:00:00 2011
Week End date: Sun Dec  4 00:00:00 2011

oh yeah i dint think of that... well if it goes negative i need to include both month reduced by 1 but then i need to consider all the months in an year since the number of days varies.. :slight_smile: thanks for finding the bug willl rectify it shortly

---------- Post updated at 06:41 AM ---------- Previous update was at 06:41 AM ----------

awesome...

---------- Post updated at 07:06 AM ---------- Previous update was at 06:41 AM ----------

hi. thanks for your answer.
can you please explain the line 15 to 20.
i didn't clear it very much. can you make me clear

here is your code..(I fixed the bug :slight_smile: )

datee="01/01/2011"
len=${#datee}
mm=${datee:0:2}
dd=${datee:3:2}
yy=${datee:6:4}
mydate=$(date -d 01/01/2011)
day=$( echo $mydate | awk -F' ' '{print $1}' )
month=$( date -d 12/22/2011 | awk -F' ' '{print $2}' )

startdate=0
mon1="Jan"
mon2="Feb"
mon3="Mar"
mon4="Apr"
mon5="May"
mon6="Jun"
mon7="Jul"
mon8="Aug"
mon9="Sep"
mon10="Oct"
mon11="Nov"
mon12="Dec"
days1=31
days2=28
days3=31
days4=30
days5=31
days6=30
days7=31
days8=31
days9=30
days10=31
days11=30
days12=31

var1="Sun"
var2="Mon"
var3="Tue"
var4="Wed"
var5="Thu"
var6="Fri"
var7="Sat"
no1=0
no2=1
no3=2
no4=3
no5=4
no6=5
no7=6

for i in $(seq 1 7)
do
          val=$( eval eval echo \$var$i )
          num=$( eval eval echo \$no$i )
          if [[ "$val" == "$day" ]]
          then
                      number2=`expr $dd + 7 - $num`
                      echo "weekend: $mm/$number2/$yy"
                      date -d $mm/$number2/$yy
                      number=`expr $dd - $num + 1`
                       if [ $number -lt 1 ];then
                                     for i in $(seq 1 12)
                                     do
                                                   mont=$( eval eval echo \$mon$i )
                                                   dayno=$( eval eval echo \$days$i )
                                                    if [ "$month" == "$mont" ]; then
                                                                 number=`expr $dayno + $number`
                                                                  mm=`expr $mm - 1`
 
                                                                  if [ $mm -lt 1 ]; then
                                                                              mm=`expr 12 - $mm` 
                                                                               yy=`expr $yy - 1`
                                                                  fi
                                                   fi
 
 
                                        done
 
 
                            fi
                            echo "weekstart: $mm/$number/$yy"
                            date -d $mm/$number/$yy
             fi
done

output

weekend: 01/2/2011
Sun Jan  2 00:00:00 EST 2011
weekstart: 12/27/2010
Mon Dec 27 00:00:00 EST 2010

it works for both month and year... :slight_smile:

Line 15: Input date is converted into time in seconds from epoch (epoch on most unix systems is 01-Jan-1970 00:00:00).
Line 16: Localtime returns an array and the 7th element specifies the count of current day starting from sunday. So, if localtime were to see 12/22/2011, 7th element returned would contain 4, i.e. 4th day from Sunday starting from 0. According to your specification, week starts from Monday, so I subtracted 1 from the result. This multiplied by 86400 seconds subtracted from $sec would give prev monday's stamp in seconds from epoch.
Line 17: Similarly, the next sunday is determined.
Line 19: The time in seconds from epoch is converted to human readable format.
Line 20: Ditto.

---------- Post updated at 18:39 ---------- Previous update was at 18:28 ----------

@vivek_d_r: What would your revised program print if the input date is 02/29/2012 in mm/dd/yyyy? :wink:

finally..... oooof.. :slight_smile: fixed the bug again

datee="02/29/2012"
len=${#datee}
mm=${datee:0:2}
dd=${datee:3:2}
yy=${datee:6:4}
mydate=$(date -d $datee)
day=$( echo $mydate | awk -F' ' '{print $1}' )
month=$( date -d 12/22/2011 | awk -F' ' '{print $2}' )
 
startdate=0
mon1="Jan"
mon2="Feb"
mon3="Mar"
mon4="Apr"
mon5="May"
mon6="Jun"
mon7="Jul"
mon8="Aug"
mon9="Sep"
mon10="Oct"
mon11="Nov"
mon12="Dec"
days1=31
days2=28
days3=31
days4=30
days5=31
days6=30
days7=31
days8=31
days9=30
days10=31
days11=30
days12=31
 
var1="Sun"
var2="Mon"
var3="Tue"
var4="Wed"
var5="Thu"
var6="Fri"
var7="Sat"
no1=0
no2=1
no3=2
no4=3
no5=4
no6=5
no7=6
mmm="$mm"
for i in $(seq 1 7)
do
        val=$( eval eval echo \$var$i )
        num=$( eval eval echo \$no$i )
        if [[ "$val" == "$day" ]]
        then
                mm="$mmm"
                number2=`expr $dd + 7 - $num`
                for i in $(seq 1 12)
                do
                        mont=$( eval eval echo \$mon$i )
                        dayno=$( eval eval echo \$days$i )
                        if [ "$month" == "$mont" ]; then
                                if [ $number2 -gt $dayno ]; then
                                        mm=`expr $mm + 1`
                                        number2=`expr $number2 - $dayno + 2`
                                        if [ $mm -gt 12 ]; then
                                                mm=`expr $mm - 12`
                                                yy=`expr $yy + 1`
                                        fi
                                fi
                        fi
                done
                echo "weekend: $mm/$number2/$yy"
                date -d $mm/$number2/$yy
                mm="$mmm"
                number=`expr $dd - $num + 1`
                if [ $number -lt 1 ];then
                for i in $(seq 1 12)
                do
                        mont=$( eval eval echo \$mon$i )
                        dayno=$( eval eval echo \$days$i )
                        if [ "$month" == "$mont" ]; then
                                number=`expr $dayno + $number`
                                mm=`expr $mm - 1`
                                if [ $mm -lt 1 ]; then
                                mm=`expr 12 - $mm`
                                yy=`expr $yy - 1`
                                fi
                        fi
 
                done
 
                fi
                echo "weekstart: $mm/$number/$yy"
                date -d $mm/$number/$yy
        fi
done

output

weekend: 3/4/2012
Sun Mar  4 00:00:00 EST 2012
weekstart: 02/27/2012
Mon Feb 27 00:00:00 EST 2012

A small script for the same,

echo "Enter date[MM/DD/YYYY]:"
read datee
day=$(date -d $datee +%u)
num=`expr 7 - $day`
x="date -d '$datee $num days'"
eval $x
num=`expr $day - 1`
x="date -d '$datee $num days ago'"
eval $x

@balajesuri:
hi i didn't understand the logic you have used in line 15,16,17,18 please help me

Epoch is a reference point which is set as 01-Jan-1970 00:00:00 on Unix systems. Time in seconds from epoch refers to the number of seconds that has passed since epoch. So, 1324636284 refers to Fri Dec 23 10:31:24 2011. Work your math and see if time in seconds from 01-Jan-2011 00:00:00 to 23-Dec-2011 10:31:24 is 1324636284.

'timelocal' is a routine available in the Time::Local module of Perl. This routine takes the following parameters: sec, min, hour, mday, mon, year. 'mday' refers to date since beginning of month. 'mon' refers to month (0 for Jan through to 11 for Dec). That's why I've put '$mt - 1'. This routine with all these parameters will return time in seconds since epoch.

If input date is 12/22/2011 (mm/dd/yyyy), then timelocal(0, 0, 0, 22, 11, 2011) would return 1324512000.

'localtime' is a built-in function in Perl adapted from C's library time.h. Read man pages of localtime to see the values this function returns. The 7th element (referred by index #6) is a number, which is the count of number of days that has passed since beginning of current week. So, if you were to use localtime on a Thursday, 7th element from returned list would be 4. Thursday is the 5th day from Sunday (with Sun as 0, Thu is 4).

Since your requirement was to refer Monday as start of week, I subtracted one from this number 4 in line 16, to mean that Mon was 4 days before Thu. Now (4 * 86400) refers to number of seconds in 4 days. This value subtracted from 1324512000 would give number of seconds passed from epoch till Mon i.e. from 01-Jan-1970 to 19-Dec-2011.

End of week is Sunday. We need to find how many days to go from Thu to following Sunday. This is taken care in line 17 by (6 - ((localtime ($sec))[6] - 1) . (6 - (4 - 1)) = 3. So, 3 days to go from Thu to Sun. We're subtracting from 6 because, localtime considers Sun as start of week referred to by 0. Sun - 0 ... Mon - 1 ... Thu - 4 ... Sat - 6.

1324512000 + ( 3 * 86400 ) gives number of seconds passed from epoch till Sun i.e. from 01-Jan-1970 to 25-Dec-2011

If time in seconds is passed as a parameter to localtime, the return value if captured as a scalar variable would be date in human readable format which is what line 19 and 20 does.

I saw in another thread that you wanted to calculate the previous weeks' start/end date and next weeks' start/end date. This is plain math from here on.

Just out of curiosity, which school/college are you studying in?

@balajesuri:
just tell me one more thing how to find month start date and month end date of current date which is given at run time...

---------- Post updated at 08:09 AM ---------- Previous update was at 08:04 AM ----------

hi have find out the answers of my another thread...Your post help me a lot...Thanks again for that.
Can you give me some idea about to find month start date and month end date of current date which is given at run time...
I study in PEC, Chandigarh.

C'mon kiddo. Give it a try. How would you find that out with pen & paper? What is the algorithm that you would follow to find the start and end date of current month? Wouldn't it depend on what month it is? Wouldn't it depend on whether the year is a leap year or not? So, you've to incorporate all these conditions (and probably more) in your pseudo code. Once you're done with the pseudo-code, Perl would be just another tool (albeit, a good one) to implement your ideas. Let us know the sugar & spice that you've gathered so far :slight_smile:

hurray i have done....

---------- Post updated at 12:40 PM ---------- Previous update was at 12:37 PM ----------

#!/etc/edi/bin/perl -w
 use Date::Manip;
 use Time::Local;
  my ($mt, $dt, $yr, $sec, $wk_st, $wk_nd, $st_date, $nd_date);
   
   (@ARGV != 1) && die "Invalid parameters. Enter date in mm/dd/yyyy format. Exiting";
   ($ARGV[0] !~ /^(0[1-9]|1[012])\/(0[1-9]|[12][0-9]|3[01])\/[\d]{4}$/)
   && die "Invalid date format. Enter date in mm/dd/yyyy. Exiting";
    
    $mt = substr $ARGV[0], 0, 2;
    $dt = substr $ARGV[0], 3, 2;
    $yr = substr $ARGV[0], 6, 4;
     
$s=`cal $mt $yr | head -3 | tail -1`;
$s =~ s/^\s+//;
$sd=substr($s,0,1);
print "current month start date: $mt/$sd/$yr";
print "\n";
$e=`cal $mt $yr | tail -2 | head -1`;
$e =~ s/\s+$//;
$ed=substr($e,-2,2);
print "Current month end date: $mt/$ed/$yr";
print "\n";
$mn=$mt+1;
$yn=$yr;
$mt=$mt-1;
if ( $mt == 0 )
  {
   $mt =12;
   $yr=$yr-1;
}

$ps=`cal $mt $yr | head -3 | tail -1`;
$ps =~ s/^\s+//;
$psd=substr($ps,0,1);
print "previous month start date: $mt/$psd/$yr";
print "\n";
$es=`cal $mt $yr | tail -2 | head -1`;
$es =~ s/\s+$//;
$ees=substr($es,-2,2);
print "prevoius end date: $mt/$ees/$yr";
print "\n";
if ( $mn == 13 )
  {
   $mn =1;
   $yn=$yn+1;
}

$ns=`cal $mn $yn | head -3 | tail -1`;
$ns =~ s/^\s+//;
$nsd=substr($ns,0,1);
print "next month start date: $mn/$nsd/$yn";
print "\n";
$nes=`cal $mn $yn | tail -2 | head -1`;
$nes =~ s/\s+$//;
$nees=substr($nes,-2,2);
print "next month end date: $mn/$nees/$yn";
C:\>
C:\> REM the script
 
C:\> type date_arithmetic.pl
#!perl -w
# Usage: perl date_arithmetic.pl YYYY MM DD
use strict;
use DateTime;
 
# Takes as arguments:
#  - The date
#  - The target day (1 is Monday, 7 Sunday)
#  - The day that we want to call the start of the week (1 is Monday, 7 Sunday)
sub get_day_in_same_week {
  my ($dt, $target, $start_of_week) = @_;
 
  # Work out what day the date is within the (corrected) week
  my $wday = ($dt->day_of_week() - $start_of_week + 7) % 7;
 
  # Correct the argument day to our week
  $target = ($target - $start_of_week + 7) % 7;
 
  # Then adjust the current day
  return $dt->clone()->add(days => $target - $wday);
}
 
my ($yr, $mt, $dt) = @ARGV;
my $dt1 = DateTime->new (
   year   => $yr,
   month  => $mt,
   day    => $dt
);
 
print "Given date         = ", $dt1->ymd,"\n";
print "Start of the week  = ", get_day_in_same_week($dt1, 1, 1)->ymd,"\n";
print "End   of the week  = ", get_day_in_same_week($dt1, 7, 1)->ymd,"\n";
print "First day of month = ", DateTime->new( year => $yr, month => $mt, day => 1 )->ymd,"\n";
print "Last  day of month = ", DateTime->last_day_of_month( year => $yr, month => $mt )->ymd,"\n";
 
C:\>
C:\> REM a few test runs...
C:\> perl date_arithmetic.pl 2011 12 22
Given date         = 2011-12-22
Start of the week  = 2011-12-19
End   of the week  = 2011-12-25
First day of month = 2011-12-01
Last  day of month = 2011-12-31
 
C:\>
C:\> perl date_arithmetic.pl 2011 7 1
Given date         = 2011-07-01
Start of the week  = 2011-06-27
End   of the week  = 2011-07-03
First day of month = 2011-07-01
Last  day of month = 2011-07-31
 
C:\>
C:\> perl date_arithmetic.pl 2011 2 4
Given date         = 2011-02-04
Start of the week  = 2011-01-31
End   of the week  = 2011-02-06
First day of month = 2011-02-01
Last  day of month = 2011-02-28
 
C:\>
C:\> perl date_arithmetic.pl 2012 2 27
Given date         = 2012-02-27
Start of the week  = 2012-02-27
End   of the week  = 2012-03-04
First day of month = 2012-02-01
Last  day of month = 2012-02-29
 
C:\>
C:\> perl date_arithmetic.pl 2011 1 1
Given date         = 2011-01-01
Start of the week  = 2010-12-27
End   of the week  = 2011-01-02
First day of month = 2011-01-01
Last  day of month = 2011-01-31
 
C:\>
C:\>

tyler_durden

[highlight=perl]#! /usr/bin/perl -w
use strict;
use Time::Local;

my ($mt, $dt, $yr, $sec, $wk_st, $wk_nd, $st_date, $nd_date);
my ($mt_st, $mt_nd, $mt_st_date, $mt_nd_date);

(@ARGV != 1) && die "Invalid parameters. Enter date in mm/dd/yyyy format. Exiting";
($ARGV[0] !~ /^(0[1-9]|1[012])\/(0[1-9]|[12][0-9]|3[01])\/[\d]{4}$/)
&& die "Invalid date format. Enter date in mm/dd/yyyy. Exiting";

$mt = substr $ARGV[0], 0, 2;
$dt = substr $ARGV[0], 3, 2;
$yr = substr $ARGV[0], 6, 4;

$sec = timelocal (0, 0, 0, $dt, $mt - 1, $yr);
$wk_st = $sec - (((localtime ($sec))[6] - 1) * 86400);
$wk_nd = $sec + ((6 - ((localtime ($sec))[6] - 1)) * 86400);

$st_date = localtime ($wk_st);
$nd_date = localtime ($wk_nd);

print "Week Start date: $st_date\n";
print "Week End date: $nd_date\n";

$mt_st = $sec - (($dt-1) * 86400);
if ($mt == 12) {
$mt = 1;
$yr= $yr + 1;
}
else {
$mt++;
}
$mt_nd = timelocal (0, 0, 0, 1, $mt - 1, $yr) - 86400;

$mt_st_date = localtime ($mt_st);
$mt_nd_date = localtime ($mt_nd);

print "Month Start Date: $mt_st_date\n";
print "Month End Date: $mt_nd_date\n";[/highlight]

Cheers!