#!/usr/bin/perl
use strict;
use warnings;
use feature qw( :5.10 );
use DateTime;
use DateTime::Duration;
use DateTime::Set;
use DateTime::Span;

my $starting_date = { day => 5, hour => 0, minute => 0, second => 0 };
my $range_ref =     { days => 5, hours => 20, minutes => 0, seconds => 0 };
my ($dt, $rv);

# Example 1: Date being tested is explicitly set

$dt = DateTime->new(
   year   => 2009,
   month  => 12,
   day    => 31,
   hour   => 12,
   minute => 36,
   second => 47,
);
$rv = validate( {
    starting_date => $starting_date,
    range        => $range_ref,
    target       => $dt,
} );
$rv ? say "$dt is in range"
    : say "$dt is not in range";

# Example 2: Date being tested is explicitly set

$dt = DateTime->new(
   year   => 2010,
   month  => 1,
   day    => 1,
   hour   => 12,
   minute => 36,
   second => 47,
);
$rv = validate( {
    starting_date => $starting_date,
    range        => $range_ref,
    target       => $dt,
} );
$rv ? say "$dt is in range"
    : say "$dt is not in range";

# Example 3: Test 'now'

$dt = DateTime->now();
$rv = validate( {
    starting_date   => $starting_date,
    range           => $range_ref,
    target          => $dt,
} );
$rv ? say "$dt is in range"
    : say "$dt is not in range";

##### SUBROUTINES #####

sub validate {
    my $args = shift;
    my $sixdays = DateTime::Duration->new( days  => 6 );
    my $oneweek = DateTime::Duration->new( weeks => 1 );
    
    my $dt1 = $args->{target}->clone();
    $dt1->set(
        hour    => $args->{starting_date}->{hour},
        minute  => $args->{starting_date}->{minute},
        second  => $args->{starting_date}->{second},
    );
    
    my $starting_date_minus_6 = $dt1 - $sixdays;
    
    my $spanweek = DateTime::Span->from_datetime_and_duration( 
        start => $starting_date_minus_6, duration => $oneweek );
    
    my $set = DateTime::Set->from_recurrence( 
        recurrence => sub {
            return $_[0] if $_[0]->is_infinite;
            return $_[0]->add( days => 1 )
        },
        span => $spanweek,
    );

    my $start_of_span = _get_start_of_span( $args->{starting_date}, $set );

    my $allowable_range = DateTime::Duration->new( %{ $args->{range} } );
    
    my $span_allow = DateTime::Span->from_datetime_and_duration( 
        start       => $start_of_span,
        duration    => $allowable_range
    );
    
    _date_is_in_span( $args->{target}, $span_allow );
}

sub _get_start_of_span {
    my ($starting_date, $set) = @_;
    my $iter = $set->iterator;
    my $starting_dt;
    while ( my $x = $iter->next ) {
        if ( $x->dow eq $starting_date->{day} ) {
            $starting_dt = $x->clone();
            last;
        }
    };
    return $starting_dt;
}

sub _date_is_in_span {
    my ( $date, $span ) = @_;
    $span->contains( $date ) ? 1 : 0;
}

The attached file, zinspan.pl, is my first attempt at using DateTime to solve a practical problem. I am looking for feedback both on the program's validity and on whether there is the making of a new DateTime module in it.

Problem: Suppose that a 'week' can begin at an arbitrarily chosen day of the week and time of day. Suppose further that that week can be divided into two subspans: one in which an event is permitted to occur, and the other in which the event is not permitted to occur. Write a function that returns true if the event's date is in the permitted subspan and false if it is in the forbidden subspan.

Example: A file is to be processed if it arrives on the system anytime between Friday midnight and Wednesday 8:00 pm. The file has arrived. Is it okay to process it?

Thank you very much.
Jim Keenan 

Reply via email to