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

Looking for a method to convert the week of a year to Sunday's date. I have a data file that consists of issues experienced on a day to day basis. I convert these dates to "Week of Year" so I can report on the top 5 issues experienced per week. Once I tally up the data by week, I need to convert the week number back to a user readable date dd/mm/yy (sunday). Thanks to help in chat, I have this so far. This successfully prints out the top five issues by week, but, again, I need the Week Number converted back.
foreach my $week (sort keys %IssuesByWeek) { print "$week:\n\t",join ("\n\t", map{"$_ => $IssuesByWeek{$wee +k}{$_}" } (sort {$IssuesByWeek{$week}{$b} <=> $IssuesByWeek{$week}{$a +} } keys %{$IssuesB yWeek{$week}} )[0..4]),"\n" }
05.05:
        ISSUE ABC => 72
        ISSUE CBA => 39
        ISSUE BBA => 37
        ISSUE ABB => 24
        ISSUE BCA => 19
I want that instead to display:
01/23/05:
        ISSUE ABC => 72
        ISSUE CBA => 39
        ISSUE BBA => 37
        ISSUE ABB => 24
        ISSUE BCA => 19
I have Date::Manip installed already if that's needed. I see how to use it to get Week of Year, but not how to convert back. <update> Thanks all for the suggestions and code. This is how I ended up doing it and have NO idea why it works. I'll definetly be converting over to one of these.
foreach $date (reverse sort keys %IssuesByWeek) { $counter++; next if $counter>4; #Only want the most recent couple weeks ($year, $week) = split(/\./,$date); $time_t = POSIX::mktime(0,0,0,0,0,$year); #turn year to numbe +r of seconds since 1970 $secs = (604800*($week-1)+(2*24*60*60)) + $time_t; #add number + of seconds since year started some reason i kept ending up on Friday + when just multiplying the year by the week in seconds @date = POSIX::gmtime($secs); #turn seconds back to date $date[4]++; #because returned month is numbers of months since + jan ie jan is 0 $date[4] = "0".$date[4] if ($date[4]<10); $date[3] = "0".$date[3] if ($date[3]<10); $newdate = "$date[4]\/$date[3]\/$year";

Replies are listed 'Best First'.
Re: Convert Week Of Year to Date
by fglock (Vicar) on Feb 01, 2005 at 17:14 UTC

    Cut and paste from 425695...

    use Date::Tie; use strict; tie my %dt, 'Date::Tie', weekday => 1, week => 1, weekyear => 2005, hour => 0, minute => 0, second => 0; $dt{weekday} = 0; # sunday while ( $dt{weekyear} == 2005 ) { print "$dt{month}-$dt{day}-$dt{year}\n"; $dt{week}++; } 01-02-2005 01-09-2005 01-16-2005 01-23-2005 ...
Re: Convert Week Of Year to Date
by bgreenlee (Friar) on Feb 01, 2005 at 17:10 UTC
    Have a look at Date::Calc's 'Monday_of_Week' function.

    -b

Re: Convert Week Of Year to Date
by davido (Cardinal) on Feb 01, 2005 at 17:32 UTC

    There are so many ways this could be done using Date::Manip, all of them slightly complex... ;)

    But there is one other complexity to consider not related to manipulating the particular date module you use. And that is the concept of falling back to the starting date for a given week as the header for your week's summary.

    What happens if the first day of the year is Wednesday, and you summarize everything that occurred in the first week of the year, given that the weeks begins on Sunday? You fall back to a previous year. Is this desired behavior?

    What happens if the first day of the month is Wednesday, and you summarize everything that occurred in the first week of the month? ...same sort of problem. Things that happened on Wednesday April 1st will fall under the heading of the last Sunday in March. Again, is this desired behavior?

    Some companies will actually use what they call a 4-5-4 calendar, where years always start on Sunday, and months always start on Sunday. This simplifies such problems. But then your company months start and end a day or two or three differently from real-world months. It takes a little getting used to, but ensures that when you're looking at everything that happened in week 1 of the year, week 1 started on a Sunday, and the entire week falls into the same year. And you can look at weeks of months in the same way; weeks never span months under such a calendar.


    Dave

Re: Convert Week Of Year to Date
by demerphq (Chancellor) on Feb 01, 2005 at 18:16 UTC

    Now that there is some room, a minor rework of that code:

    foreach my $week (sort keys %IssuesByWeek) { my $issues=$IssuesByWeek{$week}; print "$week:\n\t", join ("\n\t", map{ "$_ => $issues->{$_}" } ( sort { $issues->{$b} <=> $issues->{$a} } keys %$issues )[0..4] ),"\n"; }

    Also using the temporary value makes things a little easier to read later on and also saves on the unecessary extra hash lookups.

    ---
    demerphq

      This is what I ended up with after your original suggestion. A hell of a line of code but it does make a pretty table:
      print OUTPUT "<tr bgcolor=#C0C0C0><td><b>$newdate</b></td><td align=ri +ght>#</td><td align=right>%</td></tr>\n<tr><td>",join ("</td></tr><tr +><td>", map {"<font size=-1>$_</td><td align=right><font size=-1>$IssuesByWeek{$da +te}{$_}</td><td align=right><font size=-1>".int(($IssuesByWeek{$date} +{$_}/$Weeks{$date}) *100)."\%</td>" } (sort {$IssuesByWeek{$date}{$b} <=> $IssuesByWeek{$d +ate}{$a} } keys %{$IssuesByWeek{$date}} )[0..4]),"</tr><tr><td><b><fo +nt size=-1>TOTAL TI CKETS</font></b></td><td align=right>$Weeks{$date}</td></td><tr><td><b +r><b>MACs</b></td><td align=right><br>$MACs{$date}</td><td> </td></tr +>\n";
Re: Convert Week Of Year to Date
by Limbic~Region (Chancellor) on Feb 01, 2005 at 19:17 UTC
    ear,
    Beyond the sagely advice already provided, I wanted to mention a few things for further consideration.

    First, your hash keys make it difficult to sort on them chronologically which presumably you would want to do:

    for my $week ( map { $_->[0] } sort { $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] } map { [$_, split /\./] } keys %IssuesByWeek ) { # ... }
    If you can, it would be better to use a ISO date string that can be sorted and will only require conversion one time and not every time you need to print results:
    for my $key ( keys %IssuesByWeek ) { # New key in format of YYYY-MM-DD $IssuesByWeek{ get_sunday( $key ) } = delete $IssuesByWeek{ $key } +; }
    If you can't do that, then perhaps you could change your data structure to something like $data{year}{week}{issue} = $count; then it will be much easier sorting.

    The second thing is that it is generally a waste to sort a list to find the top or bottom of the list. There are routines known as water-mark algorithms that do this much more efficiently. Instead of sorting the entire list and then throwing away most of the work, I would suggest using something similar to:

    #!/usr/bin/perl use strict; use warnings; my @list = map { int(rand 1000) + 1 } 1 .. 50; print "Top 5:\n"; print "\t$_\n" for top_x(\@list, 5); sub top_x { my ($list, $x) = @_; $x--; my @top; $#top = $x; for my $item ( @$list ) { next if defined $top[ -1 ] && $item <= $top[ -1 ]; for my $id ( 0 .. $#top ) { $top[ $id ] = $item and last if ! defined $top[ $id ]; if ( $item > $top[ $id ] ) { @top[ $id .. $#top ] = ($item, @top[ $id .. $#top - 1] +); #splice(@top, $id, 0, $item), pop @top; last; } } } return @top; }
    Note that the commented splice will effectively do the same thing as the array slice. Depending on the size of the array, it may or may not be more efficient.

    Cheers - L~R

Re: Convert Week Of Year to Date
by dragonchild (Archbishop) on Feb 01, 2005 at 18:56 UTC
    First, the 5th week of 2005 doesn't start on 1/23, it starts on 1/30. The first Sunday in 2005 is 1/2, so the first week is 1/2-1/8, second is 1/9-1/15, third is 1/16-1/22, fourth is 1/23-1/29. Unless, of course, the first week of 2005 begins on 12/26/2004 ...

    Second, there's only 53 Sundays in 2005, so it should be easy to calculate which ones go where.

    my @sundays = ([]); my @date = (2005, 1, 1); $date[2]++ while Day_Of_Week(@date) != 7; DATE: { push @sundays, \@date; @date = Add_Delta_Days( @date, 7 ); last if $date[0] == 2006; redo DATE; } # Now, $sundays[1] == ( 2005, 1, 2 ) # $sundays[2] == (2005, 1, 9 ) # etc.

    Alternately, you could just do:

    my @sunday_date = Monday_of_Week(Week_of_Year( @starting_date )); $sunday_date[2]--;

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.