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

Hello Everyone, I have this code misbehaving in one of my scripts, I have a var containing the sequential number of the day for the year and I am suppose to get the regular date for that day and its weekday. If I set the day to 273 I get back 2008/09/31 which is not a proper date. can you help me understand why? .................
syy=2008 newstart=272 perl -e 'use Time::Local; $yr = $ARGV[0] - 1900; $yday = $ARGV[1]; $my +time = timelocal(1,0,0,1,1,$yr); $mytime += ( 86400 * $yday ); ($sec, +$min,$hour,$day,$mon,$yr,$wday,$yday,$whocares) = localtime($mytime); $yr += 1900; printf("%d/%02d/%02d%s", $yr, $mon, $day, " " );use POSIX + qw(strftime);$fmt = "%A"; # %a = abbreviated weekday %u = weekday nu +mber; $weekday = strftime($fmt, 0, 0, 0, $day , $mon - 1, $yr - 1900, -1, -1 +, -1);printf("%s%s%s", "$weekday"," ",$ARG[2]);' $syy $newstart >> pp +pp newstart=273 perl -e 'use Time::Local; $yr = $ARGV[0] - 1900; $yday = $ARGV[1]; $my +time = timelocal(1,0,0,1,1,$yr); $mytime += ( 86400 * $yday ); ($sec, +$min,$hour,$day,$mon,$yr,$wday,$yday,$whocares) = localtime($mytime); $yr += 1900; printf("%d/%02d/%02d%s", $yr, $mon, $day, " " );use POSIX + qw(strftime);$fmt = "%A"; # %a = abbreviated weekday %u = weekday nu +mber; $weekday = strftime($fmt, 0, 0, 0, $day , $mon - 1, $yr - 1900, -1, -1 +, -1);printf("%s%s%s", "$weekday"," ",$ARG[2]);' $syy $newstart >> pp +pp newstart=274 perl -e 'use Time::Local; $yr = $ARGV[0] - 1900; $yday = $ARGV[1]; $my +time = timelocal(1,0,0,1,1,$yr); $mytime += ( 86400 * $yday ); ($sec, +$min,$hour,$day,$mon,$yr,$wday,$yday,$whocares) = localtime($mytime); $yr += 1900; printf("%d/%02d/%02d%s", $yr, $mon, $day, " " );use POSIX + qw(strftime);$fmt = "%A"; # %a = abbreviated weekday %u = weekday nu +mber; $weekday = strftime($fmt, 0, 0, 0, $day , $mon - 1, $yr - 1900, -1, -1 +, -1);printf("%s%s%s", "$weekday"," ",$ARG[2]);' $syy $newstart >> pp +pp the contents of pppp: 2008/09/30 Tuesday 2008/09/31 Wednesday 2008/10/01 Wednesday
Thanks for your help.

Replies are listed 'Best First'.
Re: Number of day in the year to proper date
by almut (Canon) on Oct 02, 2008 at 23:13 UTC

    Don't reinvent the wheel :)

    use Date::Manip; print UnixDate( ParseDate("2008-273"), "%Y/%m/%d %A\n");
Re: Number of day in the year to proper date
by Animator (Hermit) on Oct 02, 2008 at 23:21 UTC

    Your code, slightly rewritten to make it more readable: (reading a one-liner on a site is not easy.)

    #!/usr/bin/perl -l use strict; use warnings; use Time::Local; use POSIX qw(strftime); my $fmt = "%A"; # %a = abbreviated weekday %u = weekday number; my $yr1 = 2008 - 1900; for my $yday1 (272, 273, 274) { my $mytime = timelocal(1,0,0,1,1,$yr1); $mytime += ( 86400 * $yday1); my ($sec,$min,$hour,$day,$mon,$yr,$wday,$yday,$whocares) = localtime +($mytime); $yr += 1900; printf("%d/%02d/%02d%s", $yr, $mon, $day, " " ); my $weekday = strftime($fmt, 0, 0, 0, $day , $mon - 1, $yr - 1900, - +1, -1, -1); printf("%s\n", "$weekday"); }

    The output of the code:

    2008/09/30 Tuesday
    2008/09/31 Wednesday
    2008/10/01 Wednesday
    

    Why does it produce different dates? You got the monts wrong. January is month 0, february is month 1, ....

    In the code: my $mytime = timelocal(1,0,0,1,1,$yr1); => Should be: my $mytime = timelocal(1,0,0,1,0,$yr1); and
    $yr += 1900; should be $yr += 1900;$mon += 1;

    If you make the changes then the output will be:

    2008/09/29 Monday
    2008/09/30 Tuesday
    2008/10/01 Wednesday
    

    The meaning of your output was:

    2008/09/30 Tuesday   => This really was 2008/10/30 with the weekday of 2008/09/30
    2008/09/31 Wednesday => This really was 2008/10/31 with the weekday of 2008/10/01
    2008/10/01 Wednesday => This really was 2008/11/01 with the weekday of 2008/10/01
    

    An easy way to confirm the date is to add print scalar localtime($mytime); (and to change the $fmt = "%x";). Adding it in the original code will give you the following output:

    2008/09/30 2008/30/09 Tuesday
    Thu Oct 30 00:00:01 2008
    
    2008/09/31 2008/10/01 Wednesday
    Fri Oct 31 00:00:01 2008
    
    2008/10/01 2008/10/01 Wednesday
    Sat Nov  1 00:00:01 2008
    

    Now: on to what is wrong with the code?

    • The use of localtime. Do you really want to worry about Daylight Saving Time?
    • Using POSIX::strftime to get the weekday. localtime() and gmtime() return the weekday: ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime....;. In that list $wday is the day of the week. Where 0 is Sunday, 1 is Monday and 6 is Saterday. Why use POSIX::strftime if you already have the weekday?

    The solution to all your problems is not to write the date calculation routines yourself. There are several modules out there that do an excellent job!

    For example: Date::Calc.
    This would turn your code into:

    #!/usr/bin/perl -l use strict; use warnings; use Date::Calc qw/Add_Delta_Days Date_to_Text/; my $yr1 = 2008; for my $yday1 (272, 273, 274) { print Date_to_Text(Add_Delta_Days($yr1, 1, 1, $yday1)); }

    Benefits?

    • More readable;
    • Shoter;
    • Easier;
    • Correcter (Daylight Saving Time);
    • Faster;

    Update: Added: The meaning of your output was
    Update2: changed some dates from dd/mm/yyyy into yyyy/mm/dd.

Re: Number of day in the year to proper date
by moritz (Cardinal) on Oct 02, 2008 at 23:03 UTC
Re: Number of day in the year to proper date
by JavaFan (Canon) on Oct 03, 2008 at 00:07 UTC
    You seem to be counting from Feb 1, one second after midnight. Which not only throws you a month off, but can also give you problems with daylight savings time.
Re: Number of day in the year to proper date
by ikegami (Patriarch) on Oct 03, 2008 at 01:19 UTC

    Not all days have 86400 seconds. Follow almut's advice.