Search code examples
perl

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?


Solution

  • 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];