Shagshag
1/4/2013 - 11:16 PM

Check if a date is between two others with jockers

Check if a date is between two others with jockers

<?php
class checkRule
{
    /**
     * check if $datetime is between the start date and end date of a rule
     *
     * $rule has this format :
     * array(
     *     'date_start' => 'YYYY-MM-DD HH:MM:SS',
     *     'date_end' => 'YYYY-MM-DD HH:MM:SS',
     * );
     *
     * year and second of datetime are ignored
     * 00 for month or day means *
     *
     * @param  array  $rule
     * @param  string/boolean  $datetime when to valid the rule, if false current time is used
     * @return boolean
     */
    function isRuleValid($rule, $datetime = false)
    {
        $timetoCheck = $datetime ? strtotime($datetime) : time();
        list($date, $time) = explode(' ', $rule['date_start']);
        list($start['year'], $start['month'], $start['day']) = explode('-', $date);
        if ($start['month'] == 0) $start['month'] = null;
        if ($start['day'] == 0) $start['day'] = null;
        list($start['hour'], $start['minute'], $start['second']) = explode(':', $time);
        list($date, $time) = explode(' ', $rule['date_end']);
        list($end['year'], $end['month'], $end['day']) = explode('-', $date);
        if ($end['month'] == 0) $end['month'] = null;
        if ($end['day'] == 0) $end['day'] = null;
        list($end['hour'], $end['minute'], $end['second']) = explode(':', $time);
        $startPrev = $this->getPrevRun($timetoCheck, $start);
        $startNext = $this->getNextRun($timetoCheck, $start);
        $endPrev = $this->getPrevRun($timetoCheck, $end);
        $endNext = $this->getNextRun($timetoCheck, $end);
        return (($endPrev < $startPrev) && ($startNext > $endNext));
    }

    /**
     * Find the next time a job should be executed
     * http://stackoverflow.com/questions/321494/calculate-when-a-cron-job-will-be-executed-then-next-time/
     *
     * $job has this format :
     * array(
     *     'minute' => integer or null,
     *     'hour' => integer or null,
     *     'day' => integer or null,
     *     'month' => integer or null
     * );
     * null means '*'
     *
     * @param  array $job
     * @return int      timestamp of the next execution
     */
    function getNextRun($time, $job)
    {
        $job = $this->correctJob($job);
        $cron = new simpleTimestamp($time);
        $done = 0;
        while ($done < 100)
        {
            if (!is_null($job['minute']) && ($cron->minute != $job['minute']))
            {
                if ($cron->minute > $job['minute'])
                {
                    $cron->modify('+1 hour');
                }

                $cron->minute = $job['minute'];
            }

            if (!is_null($job['hour']) && ($cron->hour != $job['hour']))
            {
                if ($cron->hour > $job['hour'])
                {
                    $cron->modify('+1 day');
                }

                $cron->hour = $job['hour'];
                $cron->minute = 0;
            }

            if (!is_null($job['day']) && ($cron->day != $job['day']))
            {
                if ($cron->day > $job['day'])
                {
                    $cron->modify('+1 month');
                }

                $cron->day = $job['day'];
                $cron->hour = 0;
                $cron->minute = 0;
            }

            if (!is_null($job['month']) && ($cron->month != $job['month']))
            {
                if ($cron->month > $job['month'])
                {
                    $cron->modify('+1 year');
                }

                $cron->month = $job['month'];
                $cron->day = 1;
                $cron->hour = 0;
                $cron->minute = 0;
            }

            $done = (is_null($job['minute']) || $job['minute'] == $cron->minute) 
                    && (is_null($job['hour']) || $job['hour'] == $cron->hour) 
                    && (is_null($job['day']) || $job['day'] == $cron->day) 
                    && (is_null($job['month']) || $job['month'] == $cron->month) 
                    ? 100 : ($done + 1);
        }

        return $cron->timestamp;
    }

    /**
     * Find the previous time a job should have been executed
     * http://stackoverflow.com/questions/321494/calculate-when-a-cron-job-will-be-executed-then-next-time/
     *
     * $job has this format :
     * array(
     *     'minute' => integer or null,
     *     'hour' => integer or null,
     *     'day' => integer or null,
     *     'month' => integer or null
     * );
     * null means '*'
     *
     * @param  array $job
     * @return int      timestamp of the next execution
     */
    function getPrevRun($time, $job)
    {
        $job = $this->correctJob($job);
        $cron = new simpleTimestamp($time);
        $done = 0;
        while ($done < 100)
        {
            if (!is_null($job['month']) && ($cron->month != $job['month']))
            {
                if ($cron->month < $job['month'])
                {
                    $cron->modify('-1 year');
                }

                $cron->month = $job['month'];
                $cron->day = (int)date('d', mktime(0, 0, 0, $cron->month + 1, 0, $cron->year));
                $cron->hour = 23;
                $cron->minute = 59;
            }

            if (!is_null($job['day']) && ($cron->day != $job['day']))
            {
                if ($cron->day < $job['day'])
                {
                    $cron->modify('-1 month');
                }

                $cron->day = $job['day'];
                $cron->hour = 23;
                $cron->minute = 59;
            }

            if (!is_null($job['hour']) && ($cron->hour != $job['hour']))
            {
                if ($cron->hour < $job['hour'])
                {
                    $cron->modify('-1 day');
                }

                $cron->hour = $job['hour'];
                $cron->minute = 59;
            }

            if (!is_null($job['minute']) && ($cron->minute != $job['minute']))
            {
                if ($cron->minute < $job['minute'])
                {
                    $cron->modify('-1 hour');
                }

                $cron->minute = $job['minute'];
            }

            $done = (is_null($job['minute']) || $job['minute'] == $cron->minute) 
                    && (is_null($job['hour']) || $job['hour'] == $cron->hour) 
                    && (is_null($job['day']) || $job['day'] == $cron->day) 
                    && (is_null($job['month']) || $job['month'] == $cron->month) ? 100 : ($done + 1);
        }

        return $cron->timestamp;
    }

    /**
     * Correct invalid job
     * For example 31/02 25:63 become 03/03 02:03
     *
     * $job has this format :
     * array(
     *    'minute' => integer or null,
     *    'hour' => integer or null,
     *    'day' => integer or null,
     *    'month' => integer or null
     * );
     * null means '*'
     *
     * @param  array $job
     * @return array      job valid
     */
    function correctJob($job)
    {
        if ((int)$job['minute'] > 59)
        {
            if ($job['hour'] !== null) $job['hour']+= (int)($job['minute'] / 60);
            $job['minute']%= 60;
        }

        if ((int)$job['hour'] > 23)
        {
            if ($job['day'] !== null) $job['day']+= (int)($job['hour'] / 24);
            $job['hour']%= 24;
        }

        if ((int)$job['day'] > 29)
        {
            if ($job['month'] !== null)
            {
                $max = (int)date('d', mktime(0, 0, 0, $job['month'] + 1, 0));
                $job['month']+= (int)($job['day'] / $max);
                $job['day'] = 1 + (int)($job['day'] % ($max + 1));
            }
            else $job['hour']%= 31 + 1;
        }

        if ((int)$job['month'] > 11)
        {
            $job['month'] = ($job['month'] % 12);
        }

        return $job;
    }
}

/**
 * Allows to use timestamp easily
 *
 * properties are:
 *  - second
 *  - minute
 *  - hour
 *  - day
 *  - month
 *  - year
 *  - timestamp
 */
class simpleTimestamp
{
    private var $current_timestamp;
    private static $dateComponent = array(
        'second' => 's',
        'minute' => 'i',
        'hour' => 'G',
        'day' => 'j',
        'month' => 'n',
        'year' => 'Y',
        'timestamp' => 'U'
    );

    function __construct($timestamp = NULL)
    {
        $this->current_timestamp = is_null($timestamp) ? time() : $timestamp;
    }

    function __set($var, $value)
    {
      $components = array();
        list(   $components['second'], 
                $components['minute'], 
                $components['hour'], 
                $components['day'], 
                $components['month'], 
                $components['year']
            ) = explode(' ', date('s i G j n Y', $this->current_timestamp));
        switch ($var)
        {
            case 'timestamp':
                $this->current_timestamp = $value;
                break;
            default:
                $components[$var] = $value;
                $this->current_timestamp = mktime(
                    $components['hour'], 
                    $components['minute'], 
                    $components['second'], 
                    $components['month'], 
                    $components['day'], 
                    $components['year']
                );
        }
    }

    function __get($var)
    {
    	if (array_key_exists(self::$dateComponent, $var)) {
            $result = date(self::$dateComponent[$var], $this->current_timestamp);
        } else {
            $result = false;
	    }
        return $result;
    }

    /**
     * Modify the time using strtotime
     * http://php.net/strtotime
     * 
     * @param  string $how can by '+1 week 2 days 4 hours 2 seconds', '10 September 2000' or 'now' 
     */
    function modify($how)
    {
        return $this->current_timestamp = strtotime($how, $this->current_timestamp);
    }
}

$tmp = new checkRule();
$rule = array(
    'date_start' => '0000-00-00 19:00:00',
    'date_end' => '0000-00-00 18:00:00',
);
$tmp->isRuleValid($rule, '2012-05-04 15:00:00'); // true
$tmp->isRuleValid($rule, '2012-05-04 18:30:00'); // false
$rule = array(
    'date_start' => '0000-03-20 0:00:00',
    'date_end' => '0000-05-00 0:00:00',
);
$tmp->isRuleValid($rule, '2012-04-26 18:30:00'); // true
$tmp->isRuleValid($rule, '2012-05-04 18:30:00'); // false
$rule = array(
    'date_start' => '0000-11-20 0:00:00',
    'date_end' => '0000-02-00 0:00:00',
);
$tmp->isRuleValid($rule, '2012-01-01 00:00:00'); // true
$tmp->isRuleValid($rule, '2012-10-01 00:00:00'); // false

?>