DarkBlue has asked for the wisdom of the Perl Monks concerning the following question:

I am in the process of writing a "Press Release" CGI application in Perl for a client's website.

The database is of a high volume with new press-releases being issued on a daily basis (Mon - Fri). As such, they have requested that the script offer a means by which the user can select a time frame and press releases issued within that time frame are displayed.

I have dealt with "Today's", "This Month's", "Most Recent" (displayed 20 to a page) and all seem to be working fine.

My client also wants a "This Week's" option - but, it is to be based on a calendar working week, not "display anything published within the last seven days" (which would have been easy). That is, if today is Monday, display only those articles published today, if Tuesday - display today's and yesterday's, if Wednesday - display today's, Monday's and Tuesday's... etc. If today is Sunday, display everything published between the Monday and Friday immediately prior to today.

My solution to this can be described as follows:

If today is Monday then display only those documents published on a Monday with an epoch time less than 604,800 seconds (one day) difference from the current epoch time;
If today is Tuesday then display only those documents published on either a Monday or a Tuesday with an epoch time less than (604,800 * 2) difference from the current epoch time; and so on.

Notes pertaining to the code segment over the page:

  • "$this_day" is the current day in "human" form based on the system clock (i.e. "Monday", "Tuesday"...);
  • "$time" is the current system time in epoch seconds (from Perl's "time" function);
  • "$this_record2" contains a time-stamp that is the epoch time of the record's creation;
  • "$record_day" contains the day in "human" form on which the record was created;
  • A hash called "display_index" is being populated with the reference key and creation time (in epoch seconds) of each matching record.

    This whole chunk of code is being executed recursively within a while loop ("while (($key, $value) = each %index) {") as the program evaluates each record in the database (a tied hash ("%index") to a GDBM database).

    if ($sort_method eq "week") { $record_day = $this_record[0]; $record_day =~ s/(\w*?) .*/$1/so; if ($this_day eq "Monday") { if (($time - $this_record[2]) <= 604800) and ($record_day eq $this_day +)) { $display_index{$key} = $this_record[2]; } } if ($this_day eq "Tuesday") { if (($time - $this_record[2]) <= (604800 * 2)) { if (($record_day eq "Monday") or ($record_day eq "Tuesday")) { $display_index{$key} = $this_record[2]; } } } if ($this_day eq "Wednesday") { if (($time - $this_record[2]) <= (604800 * 3)) { if (($record_day eq "Monday") or ($record_day eq "Tuesday") or ($recor +d_day eq "Wednesday")) { $display_index{$key} = $this_record[2]; } } } if ($this_day eq "Thursday") { if (($time - $this_record[2]) <= (604800 * 4)) { if (($record_day eq "Monday") or ($record_day eq "Tuesday") or ($recor +d_day eq "Wednesday") or ($record_day eq "Thursday")) { $display_index{$key} = $this_record[2]; } } } if ($this_day eq "Friday") { if (($time - $this_record[2]) <= (604800 * 5)) { if (($record_day eq "Monday") or ($record_day eq "Tuesday") or ($recor +d_day eq "Wednesday") or ($record_day eq "Thursday") or ($record_day eq "Fri +day")) { $display_index{$key} = $this_record[2]; } } } if ($this_day eq "Saturday") { if (($time - $this_record[2]) <= (604800 * 6)) { if (($record_day eq "Monday") or ($record_day eq "Tuesday") or ($recor +d_day eq "Wednesday") or ($record_day eq "Thursday") or ($record_day eq "Fri +day") or ($record_day eq "Saturday")) { $display_index{$key} = $this_record[2]; } } } if ($this_day eq "Sunday") { if (($time - $this_record[2]) <= (604800 * 7)) { $display_index{$key} = $this_record[2]; } } }

     

    In theory, there is no difference between theory and practise.  But in practise, there is.
     
    Jonathan M. Hollin
    Digital-Word.com

    Replies are listed 'Best First'.
    Re: There's More Than One Way To Do It
    by merlyn (Sage) on Apr 04, 2001 at 19:13 UTC
      That seems like wayyyy too much work.
      my $now = time; my @now = localtime $now; my $start_of_today = $now - $now[0] - 60 * $now[1] - 3600 * $now[2]; my $days_since_monday = ($now[6]+6) % 7; my $start_of_monday = $start_of_today - 86400 * $days_since_monday;
      Now $start_of_monday is the epoch time for the nearest monday morning before now. Just show the ones since then.

      This fails around a DST switchover, but only by being an hour too much or too little. No biggy if you aren't publishing press releases sunday night at midnight. {grin}

      -- Randal L. Schwartz, Perl hacker

    Re: There's More Than One Way To Do It
    by davorg (Chancellor) on Apr 04, 2001 at 19:22 UTC

      I think your algorithm is a little more complex than necessary. I'd do something like this:

      #!/usr/bin/perl -w use strict; use Time::Local; # Get the epoch time and the day of the week my $now = time; my $dow = (localtime($now))[6]; # Get the various elements of the current date and time, # subtracting the correct number of seconds to move to # the previous Sunday. # N.B. 86_400 is the number of seconds in a day my @time = localtime($now - ($dow * 86_400)); # Move to the previous midnight @time[0 .. 2] = (0, 0, 0); # Get the epoch seconds. This is now the # start of the previous Sunday my $sunday = timelocal(@time[0 .. 5]); # Add a day to get to the start of the previous # Monday my $monday = $sunday + 86_400; # At this point you can do what you want with this # number - maybe use it in the where clause of # a database query. print scalar localtime $monday;
      --
      <http://www.dave.org.uk>

      "Perl makes the fun jobs fun
      and the boring jobs bearable" - me

    (boo) TIMTOWDI - munging dates and times
    by boo_radley (Parson) on Apr 04, 2001 at 19:16 UTC
      you probably want to check out date::manip.
      Failing that, I'd try mucking with the following POSIX compliant time formats.
      %U week of year, Sunday as first day of week - 01 to 53 %W week of year, Monday as first day of week - 01 to 53
      and do something like :
      get today's date. get today's week of year. (WOY) while the current day's WOY = today's WOY retrieve and print articles
      yeah.
      why bother with epoch time when POSIX and Date::Manip are there to let you work on what you need to do, rather than figuring out mundane and error prone epoch time calculation?

      Oh, a code sample? OK :
      use POSIX; use strict; # # get epoch time and format to Week of Year. # my $today = time; my $current = strftime("%W",localtime($today)); # # Is today a sunday? # if (strftime("%w",localtime ($today))==0) { # # Yes, retrieve last week's news. # print "getting last week's news.\n"; } else { # # Nope, look for each day this week. # my $check = $current; while ($check==$current) { # # while WOYs are equal, retrieve that day's news. # print scalar(localtime($today)) , " is this week. Retrieving a +rticles.\n"; # # back a day. # $today -=86400; $check = strftime("%W",localtime($today)); } }
    Re: There's More Than One Way To Do It
    by arturo (Vicar) on Apr 04, 2001 at 19:29 UTC

      Since Sunday (or, if you like, Monday) is the beginning of the week, the "this week" asks for everything from that day to the present. So, just have some code figure out when the most recent $FirstDayOfWeek was. Date::Manip makes this easy, I bet it's easy with Date::Calc too. The former is kind of heavyweight for this task; the latter is definitely recommended for any serious mangling dates.

      Just for fun, and much later than merlyn and davorg presented their (more elegant) versions, here's something from off the top of my head that uses no modules, is only barely tested, and is decidedly ugly (assumes Sunday is the date to fix on):

      # returns the DD-MM-YYYY of the most recent Sunday sub last_sunday { my $from = shift; my $time = localtime($from); my $day_of_week = (split /\s/, $time)[0]; # if today is Sunday, then we just want stuff # since midnight return date_format($from) if $day_of_week eq 'Sun'; foreach my $i (1..7) { my $day_of_week_then = (split /\s/, localtime($from - $i * 86400))[0]; return date_format($from - $i *86400) if $day_of_week_then eq 'Sun'; } } sub date_format { my $timestamp = shift; my @times = localtime $timestamp; return sprintf("%02d-%02d-%4d", $times[3], $times[4]+1, $times +[5]+1900); }

      HTH

      Philosophy can be made out of anything. Or less -- Jerry A. Fodor

    Re: There's More Than One Way To Do It
    by DarkBlue (Sexton) on Apr 04, 2001 at 19:30 UTC

      Yeah, yeah... I knew there was a better way, I just couldn't think of it. Thanks a lot guys, especially to Randal who offered what I thought was the most elegant solution - your code is now embedded in my application and working fine... cheers.

       

      In theory, there is no difference between theory and practise.  But in practise, there is.
       
      Jonathan M. Hollin
      Digital-Word.com