On Aug 8, Kevin Old said:
I'm trying to come up with a way to automatically create a hash like
the following:
It's a hash with the start and end date for each week with the start
date being Sunday and the end date being the following Saturday.
You can use localtime() and the Time::Local module (which comes with Perl)
to get this information rather easily.
First, get the value of time() for a given date:
use Time::Local; # provides timelocal(), the inverse of localtime()
# note: $m is month-1, $y is year-1900
# 2005-08-08, today's date
my ($d, $m, $y) = (8, 7, 105);
my $day_at_noon = timelocal(0,0,12, $d, $m, $y);
We use noon because it's a safe time -- should we be going backwards or
forwards over a daylight-savings-time boundary in our following
calculations, we won't accidentally skip or double a day.
Now we find out what day of the week this is:
my $dow = (localtime $day_at_noon)[6];
In today's case, $dow is 1. This means that to get the Y/M/D for Sunday,
we need to subtract 1 * 86400 (seconds in a day) from $day_at_noon. To
get the Y/M/D for Saturday, we must add 5 * 86400 to $day_at_noon:
my $sunday = $day_at_noon - $dow * 86400;
my $saturday = $day_at_noon + (6 - $dow) * 86400;
Finally, to get the YYYY-MM-DD representations of these, we call
localtime() two more times:
my @sun_dmy = (localtime $sunday)[3,4,5];
my @sat_dmy = (localtime $saturday)[3,4,5];
Of course, you must add 1 to $xxx_dmy[1] and 1900 to $xxx_dmy[2], but
otherwise, you're good.
You can produce this functionality in a tied hash. I will show you an
example that uses Sunday-Saturday as its returned values. If you want it
to return Tuesday-Monday, that's an exercise to you. Also left to you is
the ability to get this information by giving a value from time(), rather
than a string like "2005-08-08".
package Tie::Week;
use Time::Local;
use warnings;
use strict;
sub TIEHASH {
my ($class) = @_;
bless {}, $class;
}
sub FETCH {
my ($self, $date) = @_;
# extract the year, month, and day
# and save the inter-number formatting
my ($y, $sep1, $m, $sep2, $d) =
$date =~ /^(\d{4})(\D*)(\d{2})(\D*)(\d{2})$/;
# store month and day as two digits
$_ = sprintf "%02d", $_ for $m, $d;
# return the cached value if we have it
return $self->{"$y-$m-$d"} if $self->{"$y-$m-$d"};
my $time = timelocal(0, 0, 12, $d, $m-1, $y-1900);
my $dow = (localtime $time)[6];
my $week_start = $time - $dow * 86400;
my $week_end = $time + (6 - $dow) * 86400;
my @s = (localtime $week_start)[3,4,5];
my @e = (localtime $week_end)[3,4,5];
# return [START, END] and store it in our cache
return $self->{$date} = [
sprintf("%04d%s%02d%s%02d", $s[2]+1900, $sep1, $s[1]+1, $sep2, $s[0]),
sprintf("%04d%s%02d%s%02d", $e[2]+1900, $sep1, $e[1]+1, $sep2, $e[0]),
];
}
package main;
tie my(%WEEK), 'Tie::Week';
my ($begin, $end) = @{ $WEEK{'2005/08/08'} };
print "$begin -> $end\n";
Another exercise left to the reader is the ability to cache the return
value for ALL days in the week provided. That is, I've cached the value
for August 8th, 2005, so if I ask for $WEEK{'2005-08-08'} or
$WEEK{'2005.08.08'} or $WEEK{'20050808'} again, I won't have to do any
calculations. (The cached value is stored as "YYYY-MM-DD", regardless of
what inter-number characters were used.)
What the module SHOULD do is also cache the return values for 2005-08-07,
2005-08-09, 2005-08-10, 2005-08-11, 2005-08-12, and 2005-08-13 at the same
time, so that I don't need to do calculations for THEM.
It's not a very difficult process at all, and a good exercise, I think.
I hope this module helps you complete your task.
--
Jeff "japhy" Pinyan % How can we ever be the sold short or
RPI Acacia Brother #734 % the cheated, we who for every service
http://japhy.perlmonk.org/ % have long ago been overpaid?
http://www.perlmonks.org/ % -- Meister Eckhart
--
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
<http://learn.perl.org/> <http://learn.perl.org/first-response>