For Programmers: Free Programming Magazines  


Home > Archive > PERL Beginners > August 2005 > Help with hash of dates









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Help with hash of dates
Kevin Old

2005-08-08, 10:00 pm

Hello everyone,

I'm trying to come up with a way to automatically create a hash like
the following:

use Tie::IxHash;
tie my %pairs, "Tie::IxHash";

%pairs =3D (
'1' =3D> {
'start' =3D> '04/24/05',
'end' =3D> '04/30/05'
},
'2' =3D> {
'start' =3D> '05/01/05',
'end' =3D> '05/07/05'
},
'3' =3D> {
'start' =3D> '05/08/05',
'end' =3D> '05/14/05'
},
);

It's a hash with the start and end date for each w with the start
date being Sunday and the end date being the following Saturday.

I know all about Data::Calc, Date::Manip and have scoured them for
every possible way of coming up with something like this, but I can't
seem to get it.

Ultimately, if I could find a solution that would give me dates like
these for any day range (for example Tues - Mon) and a start and end
date and it fill in the blanks that would be great.

I've even tried working with the Date::Manip ParseRecur function and
can't seem to get anything to work.

Any help is greatly appreciated!

Kevin
--=20
Kevin Old
kevinold@gmail.com
Jeff 'japhy' Pinyan

2005-08-08, 10:00 pm

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 w 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 w 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::W;

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 $w_start = $time - $dow * 86400;
my $w_end = $time + (6 - $dow) * 86400;

my @s = (localtime $w_start)[3,4,5];
my @e = (localtime $w_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::W';

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 w 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
Kevin Old

2005-08-08, 10:00 pm

On 8/8/05, Jeff 'japhy' Pinyan <japhy@perlmonk.org> wrote:
> On Aug 8, Kevin Old said:
>=20
>=20
> You can use localtime() and the Time::Local module (which comes with Perl=

)
> to get this information rather easily.

<snip>
> It's not a very difficult process at all, and a good exercise, I think.
>=20
> I hope this module helps you complete your task.


Hi Jeff,

Yes, this is the kind of example I needed. I've never taken the time
to fully investigate the Time::Local module, but can see from what
you've provided exactly how powerful it is.

I appreciate the exercises and will definitely complete them.

Thanks for your help!
Kevin
--=20
Kevin Old
kevinold@gmail.com
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2009 codecomments.com