Generating Day of Week in Pure Perl

R

rev

I have a date formatted like so MM/DD/YYYY (e.g. 12/01/2003) and I need
to get the day of the week from this (Monday). But, this is occurring
on my hosting vendor so whatever does it needs to be pure Perl.

I cannot use the POSIX package.
I cannot install any package that requires compilation.
Anything I use needs to work through the year 2100.

Any suggestions on this?

-rev
 
G

Gregory Toomey

It was a dark and stormy night, and rev managed to scribble:
I have a date formatted like so MM/DD/YYYY (e.g. 12/01/2003) and I need
to get the day of the week from this (Monday). But, this is occurring
on my hosting vendor so whatever does it needs to be pure Perl.

I cannot use the POSIX package.
I cannot install any package that requires compilation.
Anything I use needs to work through the year 2100.

Any suggestions on this?

-rev


Here's a quick solution (works for years 1900-2100) :

The two routines below work out days between dates and days of the week
Q.How may days between 15-jan-1999 and 5-jul-2003
A. date_ymd("20030715") - date_ymd("19990105");
Q . What day is 21-mar-2003.
A. weekday(date_ymd("20030321"));

sub date_ymd { my($year)=substr($_[0],0,4); my($month)=substr($_[0],4,2);
my($day)=substr($_[0],6,2); my(@months) = (0,31,59,90,120,151,181,212,243,273,304,334);
return $year*365+int(($year-1)/4)+$months[$month-1]+$day($year%4==0&&$month>=3?1:0) +5; }

sub weekday { my ($offset)=@_; my(@wday) = ( "Sun", "Mon", "Tue", "Wed", "Thu",
"Fri", "Sat" );

return $wday[$offset%7]; }


gtoomey
 
G

Gunnar Hjalmarsson

rev said:
I have a date formatted like so MM/DD/YYYY (e.g. 12/01/2003) and I
need to get the day of the week from this (Monday). But, this is
occurring on my hosting vendor so whatever does it needs to be pure
Perl.

I cannot use the POSIX package.
I cannot install any package that requires compilation.
Anything I use needs to work through the year 2100.

This solution makes use of the pure Perl (and standard) module
Time::Local and the localtime() function:

sub day {
use Time::Local;
my @t = split /\//, shift;
my $time = timelocal(0,0,0,$t[1],$t[0]-1,$t[2]-1900);
my @days = qw/Sunday Monday Tuesday
Wednesday Thursday Friday Saturday/;
return $days[ (localtime $time)[6] ];
}

my $date = '12/01/2003';
print day($date);
 
J

J. Gleixner

rev said:
I have a date formatted like so MM/DD/YYYY (e.g. 12/01/2003) and I need
to get the day of the week from this (Monday). But, this is occurring
on my hosting vendor so whatever does it needs to be pure Perl.

I cannot use the POSIX package.
I cannot install any package that requires compilation.
Anything I use needs to work through the year 2100.

Any suggestions on this?

Find Epoch seconds, using Time::Local

perldoc Time::Local

Then use localtime() to find the day of the week

perldoc -f localtime

Then you should easily be able to come up with Monday-Sunday by using
that as an index on a simple array.
 
T

Tom

rev said:
I have a date formatted like so MM/DD/YYYY (e.g. 12/01/2003) and I need
to get the day of the week from this (Monday). But, this is occurring
on my hosting vendor so whatever does it needs to be pure Perl.

OS?

Windows XP:

$date = `date /t`;
chomp($date);
($weekday,$tmp1) = split (/\s+/, $date);

UNIX(Linux):

$date = `date`;
# $date = "Sun Aug 31 5:12:57 PDT 2003"; # < Sample
chomp($date);
($weekday,$month,$day,$ctime,$timezone,$year) = split (/\s+/, $date);
 
T

Tore Aursand


The OP was talking about "pure Perl". Why drag the OS into this case?
Windows XP:

$date = `date /t`;
chomp($date);
($weekday,$tmp1) = split (/\s+/, $date);

UNIX(Linux):

$date = `date`;
# $date = "Sun Aug 31 5:12:57 PDT 2003"; # < Sample
chomp($date);
($weekday,$month,$day,$ctime,$timezone,$year) = split (/\s+/, $date);

This is hardly "pure Perl".
 
T

Tintin

rev said:
I have a date formatted like so MM/DD/YYYY (e.g. 12/01/2003) and I need
to get the day of the week from this (Monday). But, this is occurring
on my hosting vendor so whatever does it needs to be pure Perl.

I cannot use the POSIX package.

Why not? It provides a very simple and portable solution.
 
T

Tore Aursand

For the same reason he can't use any other module. The ISP probably
deleted it and isn't willing to install it again.

Why would an ISP delete a module that comes with Perl?
 
B

Ben Morrow

Tore Aursand said:
Why would an ISP delete a module that comes with Perl?

A perhaps better question is 'Why would anyone *use* an ISP that
deleted a module that comes with Perl?' :).

Ben
 
D

Deborah Pickett

rev said:
I have a date formatted like so MM/DD/YYYY (e.g. 12/01/2003) and I need
to get the day of the week from this (Monday). But, this is occurring
on my hosting vendor so whatever does it needs to be pure Perl.

This works for any Gregorian Calendar day. Apologies for the formatting; my
news client likes to wrap text and I can't be bothered turning off the
feature for one lousy post. I'll leave it as an exercise for you to
extract the day, month and year from the string to pass to this function.
If you know that the date is a valid one, you can omit the function body
from the first comment until "don't try this at home".

# weekday
# input: three scalar parameters: year, month (1-origin), day (1-origin).
# output: scalar from 0 to 6 representing Sunday to Saturday respectively.
# returns undef if month or day is out of bounds or any input is
# not integral.
sub weekday ($$$)
{
my ($y,$m,$d) = @_;

# Year must be made positive for modulus to work correctly.
$y += 400 * (1 + abs int $y/400) if $y <= 0;

# Return undef if anything is out of bounds.
return undef if
$y != int $y or
$m != int $m or
$d != int $d or
$m < 1 || $m > 12 or
$d < 1 ||
$d > [[31,28,31,30,31,30,31,31,30,31,30,31]
[31,29,31,30,31,30,31,31,30,31,30,31]] ->
[ ((! ($y % 400) || ($y % 100)) && (! ($y % 4))) || 0 ][ $m-1 ];

# The calculation. Don't try this at home, kids.
(
[[6,2,2,5,0,3,5,1,4,6,2,4],[6,2,3,6,1,4,6,2,5,0,3,5]] ->
[ ((! ($y % 400) || ($y % 100)) && (! ($y % 4))) || 0 ][ $m-1 ]
+ $d
+ $y
+ int(($y-1)/4)
- int(($y-1)/100)
+ int(($y-1)/400)
) % 7;
}
 
J

John W. Krahn

Deborah said:

A student of the demented Dr. Conway perhaps? ;-)

BTW - in reference to your web page at: http://www.csse.monash.edu.au/courseware/cse2395/exercise/topic09.html
3. Perl provides two Unix commands for creating processes: fork
clones the current process, so that two identical copies of the
process are running (the only difference is in the value the fork
function returns); exec completely replaces the current process
with the specified program. Suggest how to implement the system
function using fork and exec.

AFAIK fork is the only way to _create_ a process. exec doesn't _create_ a process, it
replaces the currently running program with a different one but it is still the same process.

From _Advanced Programming in the UNIX(R) Environment_ by W. Richard Stevens, page 188

8.3 fork Function

The <i>only</i> way a new process is created by the Unix kernel is when an existing process
calls the fork function.



John
 
A

Anno Siegel

Deborah Pickett said:
This works for any Gregorian Calendar day. Apologies for the formatting; my
news client likes to wrap text and I can't be bothered turning off the
feature for one lousy post. I'll leave it as an exercise for you to
extract the day, month and year from the string to pass to this function.
If you know that the date is a valid one, you can omit the function body
from the first comment until "don't try this at home".

Well, that's some compact bit of code. A few remarks:
# weekday
# input: three scalar parameters: year, month (1-origin), day (1-origin).

Add

# which are evaluated in scalar context.
# output: scalar from 0 to 6 representing Sunday to Saturday respectively.
# returns undef if month or day is out of bounds or any input is
# not integral.
sub weekday ($$$)

If you use the prototype ($$$) the user must be told. Actually, I'd leave
it out. It gives the function non-standard behavior for no good reason.
{
my ($y,$m,$d) = @_;

# Year must be made positive for modulus to work correctly.

The problem seems to be the missing year 0. The modulo operator
works fine for negative numerators.
$y += 400 * (1 + abs int $y/400) if $y <= 0;

# Return undef if anything is out of bounds.
return undef if

Don't return undef to signal an error, simply return with no argument.
Undef may do the wrong thing when the function is called in list context.
In scalar context, the effect is the same.
$y != int $y or
$m != int $m or
$d != int $d or
$m < 1 || $m > 12 or
$d < 1 ||
$d > [[31,28,31,30,31,30,31,31,30,31,30,31]

There's a comma missing at the end of this line. Did you *retype* the
code?
[31,29,31,30,31,30,31,31,30,31,30,31]] ->
[ ((! ($y % 400) || ($y % 100)) && (! ($y % 4))) || 0 ][ $m-1 ];

The leap year calculation is duplicated below. I'd make it a subroutine.
# The calculation. Don't try this at home, kids.
(
[[6,2,2,5,0,3,5,1,4,6,2,4],[6,2,3,6,1,4,6,2,5,0,3,5]] ->
[ ((! ($y % 400) || ($y % 100)) && (! ($y % 4))) || 0 ][ $m-1 ]
+ $d
+ $y
+ int(($y-1)/4)
- int(($y-1)/100)
+ int(($y-1)/400)

Come to think of it, in the last three lines you're calculating the
number of leap years before a given year. Make *that* a subroutine,
and find if a given year is a leap year in terms of that.

Appending a revised version below. Indentation could use improvement
too, but I left that alone.

I have also used constants for the four auxiliary arrays. That actually
adds some code, but it makes the relation between the arrays explicit.

Anno


# weekday
# input: three scalar parameters: year, month (1-origin), day (1-origin).
# output: scalar from 0 to 6 representing Sunday to Saturday respectively.
# returns undef if month or day is out of bounds or any input is
# not integral.

use constant DAYS_IN_MONTH => do {
my @dim_l = my @dim_n = (31,28,31,30,31,30,31,31,30,31,30,31);
$dim_l[ 1]++; # Feb one day longer in leap years
[\ @dim_n, \ @dim_l];
};

use constant WDAY_OF_FIRST => do {
my @wof_n = ( 6); # Jan 1st is Saturday in reference year
push @wof_n, ( $wof_n[-1] + $_) % 7 for @{ DAYS_IN_MONTH->[0]};
pop @wof_n; # we generated a thirteenth
my @wof_l = ( 6);
push @wof_l, ( $wof_l[-1] + $_) % 7 for @{ DAYS_IN_MONTH->[1]};
pop @wof_l; # we have a thirteenth
[\ @wof_n, \ @wof_l]
};

sub weekday
{
my ($y,$m,$d) = @_;

# Year must be made positive (missing year 0)
$y += 400 * (1 + abs int $y/400) if $y <= 0;

# Return undef if anything is out of bounds.
return if
$y != int $y or
$m != int $m or
$d != int $d or
$m < 1 || $m > 12 or
$d < 1 || $d > DAYS_IN_MONTH -> [ is_leap_year( $y)][ $m-1 ];

# The calculation. Don't try this at home, kids.
(
WDAY_OF_FIRST -> [ is_leap_year( $y) ][ $m-1 ]
+ $d
+ $y
+ n_leap_years( $y - 1)
) % 7;
}

sub n_leap_years {
my $y = shift;
int($y/4) - int($y/100) + int($y/400);
}

sub is_leap_year {
my $y = shift;
n_leap_years( $y) - n_leap_years( $y - 1);
}
 
I

Iain Chalmers

Bart Lateur said:
For the same reason he can't use any other module. The ISP probably
deleted it and isn't willing to install it again.

Or because the homework assignment specifically said he can't...

;-)

big
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top