Reputation: 33
#!/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
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
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:
use strict
and use warnings
my
chomp()
to remove newlines from inputUpdate: 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
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
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
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