Reputation: 69
All,
I want to find out the date of previous wednesday from the given date.
Eg. I have date as "20150804" and i would need "20150729".
DateTime is not available and i cannot install it as well.
I looked at few examples but they were using DateTime.
Can you please redirect me where i can get some help.? Thanks.
I am planning to code something like below.
Code:
#!/opt/perl-5.8.0/bin/perl
use warnings;
use strict;
my $dt="20150804";
my $prevWednesday=getPrevWednesday($dt);
sub getPrevWednesday()
{
my $givenDt=shift;
...
}
Upvotes: 4
Views: 688
Reputation: 54333
Here is a more elegant solution that does not do bruteforce.
use strict;
use warnings;
use Time::Local 'timelocal';
use POSIX 'strftime';
my $dt = "20150804";
say getPrevWednesday($dt);
# note you do not want () here,
# see http://perldoc.perl.org/perlsub.html#Prototypes
sub getPrevWednesday {
my $givenDt = shift;
# parse the string into a unix timestamp
my ( $year, $month, $day ) = $givenDt =~ /(....)(..)(..)/;
my $timestamp = timelocal( 0, 0, 12, $day, $month - 1, $year );
# get the day of week, ignore the rest
my ( undef, undef, undef, undef, undef, undef, $wday ) =
localtime $timestamp;
# because we start the week with Sunday on day 0
# and to get to the previous Wednesday from Sunday it's
# 4 days (Wednesday is 3) we can add 4 to the
# number of this day, divide by 7, take the leftover (modulo)
# and then subtract that many days
# (86_400 is one day in seconds)
# v- -6 ------
# 6 % 7 = 6
# +4 -----v
# v
# 0 1 2 3 4 5 6 0 1 2 3 4 5 6
# S M T W T F S S M T W T F S
my $prev_wed = $timestamp - ( ( $wday + 4 ) % 7 * 86_400 );
# go one week back if we got the same day
$prev_wed -= ( 7 * 86_400 ) if $prev_wed == $timestamp;
# debug output
warn "in: " . localtime($timestamp) . "\n";
warn "out: " . localtime($prev_wed) . "\n\n";
# put it back into your format
return strftime('%Y%m%d', localtime $timestamp);
}
Output:
# STDOUT
20150804
# STDERR
in: Tue Aug 4 12:00:00 2015
out: Wed Jul 29 12:00:00 2015
Upvotes: 2
Reputation: 69274
There's always the brute force approach.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use POSIX 'strftime';
my $ONE_DAY = 24 * 60 * 60;
# Get now
my $time = time;
# Subtract days until you get to a Wednesday
do {
$time -= $ONE_DAY;
} until (localtime($time))[6] == 3;
# Format
say strftime '%Y%m%d', localtime $time;
But if you're working in a Perl environment where you can't install modules from CPAN, then it is always worth working to get that restriction removed. Modern Perl programming is often a case of plumbing together the right series of CPAN modules. If you don't have access to CPAN then you're just making your life much harder than it needs to be.
If you really can't get the restriction lifted, then look for another job. It's not worth dealing with people who impose such pointless restrictions.
Update: Just noticed that you're also using a prehistoric version of Perl. You'll need to remove the use 5.010
and replace the say
with print
. And brush up your CV :-/
Update 2: choroba's solution is better. It deals with any date in the correct format. Mine just deals with the current date. The advice about fixing your working environment still holds though.
Upvotes: 2
Reputation: 40758
Using Time::Piece
:
use feature qw(say);
use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
my $str = '20150804';
my $fmt = '%Y%m%d';
my $t = Time::Piece->strptime($str, $fmt);
do {
$t = $t - ONE_DAY;
} until ( $t->day eq 'Wed');
say $t->strftime($fmt);
Upvotes: 2
Reputation: 241918
Another brute force approach, this time using another core module Time::Local.
#!/usr/bin/perl
use warnings;
use strict;
use Time::Local;
sub prev_wednesday {
my $date = shift;
my ($year, $month, $day) = $date =~ /(....)(..)(..)/;
my $time = timelocal(0, 0, 12, $day, $month - 1, $year);
do { $time -= 60 * 60 * 24 } until (localtime $time)[6] == 3; # <- Wednesday
my ($y, $m, $d) = (localtime $time)[5, 4, 3];
return sprintf "%4d%02d%02d\n", 1900 + $y, $m + 1, $d;
}
print $_, ' ', prev_wednesday($_), for qw( 20150804 20150805 20150806
20150101 20000301 20010301 );
Upvotes: 5