Cron-Toolkit

 view release on metacpan or  search on metacpan

lib/Cron/Toolkit.pm  view on Meta::CPAN

   return $self->_is_match($tm);
}

sub _is_match {
   my ( $self, $tm ) = @_;

 NODE: for my $node ( @{ $self->{nodes} } ) {
      my $value = $self->_field_value( $tm, $node->field_type );
      if ( $node->type eq 'list' ) {
         for my $child ( @{ $node->children } ) {
            next NODE if $child->match( $value, $tm );
         }
         return 0;
      }
      return 0 unless $node->match( $value, $tm );
   }
   return 1;
}

sub next {
   my ( $self, $epoch_seconds ) = @_;
   $epoch_seconds //= time;

   my $clamped = max( $epoch_seconds, $self->{begin_epoch} );
   return if $clamped > $self->{end_epoch};

   my $tm = Time::Moment->from_epoch($clamped)
                        ->with_offset_same_instant( $self->{utc_offset} );
   $tm = $tm->plus_seconds(1);

   # HMS shortcut / odometer carry
   NODE: foreach my $i ( 0 .. 2 ) {
      my $node    = $self->{nodes}[$i];
      my $curval  = $self->_field_value( $tm, $node->field_type );
      my $lowval  = $node->lowest($tm);
      my $highval = $node->highest($tm);

      # Changed condition + explicit handling for exact highval
      if ($curval > $highval || ($curval == $highval && !$node->match($curval, $tm))) {
         $tm = $self->_set_date( $tm, $node->field_type, $lowval );
         $tm = $self->_plus_one( $tm, $self->{nodes}[ $i + 1 ]->field_type );
         next NODE;
      }

      for my $c ( $curval .. $highval ) {
         my $c_tm = $self->_set_date( $tm, $node->field_type, $c );
         if ( $self->_is_match($c_tm) ) {
            $tm = $c_tm;
            last NODE;
         }
      }

      # flip if no match found in this field
      $tm = $self->_set_date( $tm, $node->field_type, $lowval );
      $tm = $self->_plus_one( $tm, $self->{nodes}[ $i + 1 ]->field_type );
   }

   # the brute force approach for DMY is correct here because:
   # 1) the design is simple and easy to understand and debug
   # 2) solves all tricky end-of-month and leap year calculations
   # 3) 365 iterations per one-year time window is good enough

   my $max_tm = Time::Moment->new( year => 2099, month => 12, day => 31,
                                   hour => 23, minute => 59, second => 59 );
   my $max_iter = $tm->delta_days($max_tm) || 1;

   for my $day ( 1 .. $max_iter ) {
      return $tm->epoch if $self->_is_match($tm);
      $tm = $tm->plus_days(1);
   }
   return;
}

sub previous {
   my ( $self, $epoch_seconds ) = @_;
   $epoch_seconds //= time;

   my $clamped = min( $epoch_seconds, $self->{end_epoch} );
   return if $clamped < $self->{begin_epoch};

   my $tm = Time::Moment->from_epoch($clamped)
                        ->with_offset_same_instant( $self->{utc_offset} );
   $tm = $tm->minus_seconds(1);

   # HMS shortcut / odometer carry (backward)
   NODE: foreach my $i ( 0 .. 2 ) {
      my $node    = $self->{nodes}[$i];
      my $curval  = $self->_field_value( $tm, $node->field_type );
      my $lowval  = $node->lowest($tm);
      my $highval = $node->highest($tm);

      # Changed condition + explicit handling for exact lowval
      if ($curval < $lowval || ($curval == $lowval && !$node->match($curval, $tm))) {
         $tm = $self->_set_date( $tm, $node->field_type, $highval );
         $tm = $self->_minus_one( $tm, $self->{nodes}[ $i + 1 ]->field_type );
         next NODE;
      }

      for ( my $c = $curval ; $c >= $lowval ; $c-- ) {
         my $c_tm = $self->_set_date( $tm, $node->field_type, $c );
         if ( $self->_is_match($c_tm) ) {
            $tm = $c_tm;
            last NODE;
         }
      }

      # flip if no match found in this field
      $tm = $self->_set_date( $tm, $node->field_type, $highval );
      $tm = $self->_minus_one( $tm, $self->{nodes}[ $i + 1 ]->field_type );
   }

   # Brute force for DOM/DOW/year (no year adjustment block)
   my $min_tm = Time::Moment->new( year => 1970, month => 1, day => 1,
                                   hour => 0, minute => 0, second => 0 );
   my $min_iter = $min_tm->delta_days($tm) || 1;

   for my $day ( 0 .. $min_iter ) {
      return $tm->epoch if $self->_is_match($tm);
      $tm = $tm->minus_days(1);
   }
   return;



( run in 1.006 second using v1.01-cache-2.11-cpan-4ab04211f4c )