Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

date ranges as days

by punkish (Priest)
on Feb 23, 2011 at 01:29 UTC ( [id://889705]=perlquestion: print w/replies, xml ) Need Help??

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

Its been a long day here in Wisconsin, so pardon me if the following be too basic a question --

The user provides a date, or start/end dates, or month/year or year (all of which could be resolved into start/end dates).

The program returns --

Option 1: an array of integers representing the ordinal day since Jan 1, 1980. For example, if the user enters "Feb 1, 1981" to "Feb 5, 1982", the array returned is (397 .. 766) as Feb 1, 1981 is the 397th day since Jan 1, 1980 (index starting at 0), and so on.

Potential solution: Use something like Time::JulianDay to convert start and end dates into integers, and just subtract "epoch" start day from each. Critique, por favor.

Option 2: a hash in which the keys are the years, and the values are the days for the period prescribed by the start/end dates in the input. So, something like so ( "1981" => [31 .. 365], "1982" => [0 .. 35] );. Suggestions, por favor.

--

when small people start casting long shadows, it is time to go to bed

Replies are listed 'Best First'.
Re: date ranges as days
by wind (Priest) on Feb 23, 2011 at 01:37 UTC

    For date calculations use the cpan module: Date::Calc and possibly Date::Parse

    Here's a quick and dirty script that takes the dates you specified and prints the difference in days between them. Note that there are differences between how Date::Parse treats month ranges and how Date::Calc does.

    use Date::Parse; use Date::Calc qw(Delta_Days); use strict; my $base = 'Jan 1, 1980'; my $start = 'Feb 1, 1981'; my $end = 'Feb 5, 1982'; print days_diff($start, $base) . "\n"; # 397 print days_diff($end, $base) . "\n"; # 766 sub days_diff { my $minuend = shift; my $subtrahend = shift; my (undef,undef,undef,$d2,$m2,$y2,undef) = strptime($minuend); my (undef,undef,undef,$d1,$m1,$y1,undef) = strptime($subtrahend); return Delta_Days($y1, $m1+1, $d1, $y2, $m2+1, $d2); } 1; __END__
Re: date ranges as days
by ikegami (Patriarch) on Feb 23, 2011 at 02:03 UTC
    Using DateTime:
    use strict; use warnings; use feature qw( say ); use DateTime qw( ); # Or using your favourite DateTime::Format parser. my $date1 = DateTime->new( year => 1981, month => 2, day => 1 ); my $date2 = DateTime->new( year => 1982, month => 2, day => 5 );

    Part 1:

    my $ref = DateTime->new( year => 1980, month => 1, day => 1 ); say $date1->delta_days($ref)->in_units('days'); say $date2->delta_days($ref)->in_units('days');

    Part 2:

    sub last_day_of_year { ( my $dt = DateTime->new( year => $_[0], month => 1, day => 1 ) ) ->add( years => 1, days => -1 ); return $dt->day_of_year(); } my %in_range; my $year1 = $date1->year(); my $year2 = $date2->year(); if ($year1 == $year2) { $in_range{$year1} = [ $date1->day_of_year()-1 .. $date2->day_of_yea +r()-1 ]; } else { $in_range{$year1} = [ $date1->day_of_year()-1 .. last_day_of_year($ +year1)-1 ]; for my $year ($year1+1 .. $year2-1) { $in_range{$year} = [ 0 .. last_day_of_year($year)-1 ]; } $in_range{$year2} = [ 0 .. $date2->day_of_year()-1 ]; } for my $year (sort { $a <=> $b } keys(%in_range)) { say "$year: @{ $in_range{$year} }"; }

    PS - You said 365 where you should have said 364.

    Update: Added part 2.

Re: date ranges as days
by punkish (Priest) on Feb 23, 2011 at 20:33 UTC
    Thanks wind and ikegami. Once again, my apologies for this very simple problem that I should have figured out myself. I am adding my own solution to your contributions for the benefit of the archives

    use Time::JulianDay; my @start = (1981, 2, 1); my @end = (1982, 2, 5); my $days = days('start' => \@start, 'end' => \@end, 'type' => 'yearly' +); my $days = days('start' => \@start, 'end' => \@end, 'type' => 'range') +; sub days { my (%a) = @_; my ($start, $end, $type) = ($a{'start'}, $a{'end'}, $a{'type'}); if ($type eq 'range') { my $j0 = julian_day(1980, 1, 1); my $j1 = julian_day(@$start); my $j2 = julian_day(@$end); return (($j1 - $j0), ($j2 - $j0)); } elsif ($type eq 'yearly') { my $start_year = $start->[0]; my $end_year = $end->[0]; my %days; for my $year ($start_year .. $end_year) { my $j0 = julian_day($year, 1, 1); my $j1; my $j2; if ($year == $start_year) { $j1 = julian_day($year, $start->[1], $start->[2]); $j2 = julian_day($year, 12, 31); } elsif ($year == $end_year) { $j1 = julian_day($year, 1, 1); $j2 = julian_day($year, $end->[1], $end->[2]); + } else { $j1 = julian_day($year, 1, 1); $j2 = julian_day($year, 12, 31); } $days{"$year"} = [($j1 - $j0) .. ($j2 - $j0)]; } return \%days; } }
    --

    when small people start casting long shadows, it is time to go to bed

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://889705]
Approved by ww
Front-paged by FalseVinylShrub
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (7)
As of 2024-04-16 08:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found