Elif Nur Kahraman
Elif Nur Kahraman

Reputation: 33

Finding the day from a given date

#!/usr/bin/perl

@month = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
@week = ("Sunday", "Monday","Tuesday", "Wednesday","Thursday", "Friday", 
    "Saturday");

print "date:\n";
$date=<STDIN>;
print "mon:\n";
$mon=<STDIN>;
print "year:\n";
$year=<STDIN>;

if ( ($year % 400 == 0) || ($year % 4 == 0) && ($year % 100 != 0) )
{
    $month[1] = 29 ;
    for($i = 0 ; $i < $mon - 1 ; $i++)
    {       
        $s = $s + $month[$i] ;
        $s = $s + ($date + $year + ($year / 4) - 2) ;
        $s = $s % 7 ;
    }
}
print $week[$s+1] ;

i've been trying to learn perl for a few days and i wrote that code to find the day from a given date. actually i converted this from a C code. But its not working true. The output is always Monday. Where am i making the mistake?

Upvotes: 2

Views: 2231

Answers (5)

Davy Liu
Davy Liu

Reputation: 1

My solution was quite straight forward, designate a specific epoch time of the given date, then use strftime to retrieve the weekday.

use Time::Local;

#The time was set to be 10:30:30 as default.

$designated_epoch_time = timelocal(30, 30, 10, $given_day, $given_month-1, $given_year); 

$weekday = strftime("%w", localtime($designated_epoch_time));

Upvotes: 0

Dave Cross
Dave Cross

Reputation: 69274

Don't do this yourself. Use a module. Time::Piece has been a standard part of the Perl distribution for almost ten years.

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use Time::Piece;

print "date:\n";
chomp(my $date = <STDIN>);
print "mon:\n";
chomp(my $mon = <STDIN>);
print "year:\n";
chomp(my $year = <STDIN>);

my $tp = Time::Piece->strptime("$year-$mon-$date", '%Y-%m-%d');

say $tp->fullday;

Some other tweaks I've made:

  • Always use strict and use warnings
  • Declare variables with my
  • Use chomp() to remove newlines from input

Update: I've now looked at your code in greater detail. There's only one error there.

Your logic looks like this:

if (we're in a leap year) {
    Change the @months array to deal with leap years
    Do the maths to calculate the day
}

When it should have looked like this:

if (we're in a leap year) {
    Change the @months array to deal with leap years
}
Do the maths to calculate the day

So, unless your input year was a leap year, you were skipping all of the calculations. This meant that $s was never being given a value. Perl treats an undefined value as 0, therefore your final statement was always printing $week[0 + 1] which is Monday.

If modules like Time::Piece weren't available, a Perl programmer would write your code like this:

#!/usr/bin/perl

# Force us to declare variables.
use strict;
# Get Perl to tell us when we're doing something stupid
use warnings;
# Allow the use of say()
use feature 'say';

# Declare variables with my()
my @month = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
# qw(...) defines a list without all that tedious punctuation
my @week = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday);

print "date:\n";
# Use chomp() to remove newlines from input
chomp(my $date = <STDIN>);
print "mon:\n";
chomp(my $mon = <STDIN>);
print "year:\n";
chomp(my $year = <STDIN>);

# This logic can be cleaned up a lot.
if ( ($year % 400 == 0) || ($year % 4 == 0) && ($year % 100 != 0) ) {
    $month[1] = 29 ;
}

# Initialise $s to avoid warnings later
my $s = 0;
# A foreach look is almost always cleaner than for (;;)
foreach my $i (0 .. $mon - 2) {
    # Haven't checked your calculations (but they seem to work
    # += is  useful shortcut
    $s += $month[$i];
    $s += ($date + $year + ($year / 4) - 2);
    $s %= 7;
}

# say() is like print() but with an extra newline
say $week[$s+1];

Upvotes: 2

zdim
zdim

Reputation: 66881

To also introduce a most widely used module for date and time, here it is with DateTime

use warnings;
use strict;
use feature qw(say);

use DateTime;

# ... acquire input ($year, $mon, $date)

my $dt = DateTime->new(year => $year, month => $mon, day => $date);

say $dt->day_name;

This is a very large and "heavy" module, with a lot of functionality. There are others.

While I support doing this by hand as a part of learning, once it comes to working with dates and time you will want to use a module.

Upvotes: 2

ikegami
ikegami

Reputation: 385897

use Time::Local qw( timegm );

my @dow_names = qw( Sunday Monday Tuesday Wednesday Thursday Friday Saturday );

my $Y = ...;
my $m = ...;
my $d = ...;

my $epoch = timegm(0, 0, 0, $d, $m-1, $Y-1900);
my $dow = ( gmtime($epoch) )[6];
my $dow_name = $dow_names[$dow];

Better yet,

use POSIX       qw( strftime );
use Time::Local qw( timegm );

my $Y = ...;
my $m = ...;
my $d = ...;

my $epoch = timegm(0, 0, 0, $d, $m-1, $Y-1900);
my $dow_name = strftime("%A", gmtime($epoch));

You could also use DateTime; it's simpler to use, but it's not nearly as light a module as the ones used above.

Upvotes: 1

mob
mob

Reputation: 118605

As @Chris Turner points out, there is no path in your code to handle the case that is not a leap year. To fix this and to accomplish your stated goal, there is just one small change to make to your code.

if( ($year % 400 == 0) || ($year % 4 == 0) && ($year % 100 != 0) ) {
   $month[1] = 29 ;
   for($i = 0 ; $i < $mon - 1 ; $i++) {
      ...
   }
}
print $week[$s+1] ;

should be re-written and re-indented as

if( ($year % 400 == 0) || ($year % 4 == 0) && ($year % 100 != 0) ) {
   $month[1] = 29 ; 
}
for($i = 0 ; $i < $mon - 1 ; $i++) {
   ...
}
print $week[$s+1] ;

so that $month[1] is only updated for leap years, but the code in your for loop always runs.


Experienced Perl programmers will also always advise you to start every script with

use strict;
use warnings;

because these pragmas promote good programming practices and help catch many errors early in development. They wouldn't have helped with this problem, but if you start using them they will help with the next one.

Upvotes: 5

Related Questions