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>


Reply via email to