Location: PHPKode > scripts > pop3ml > includes/class.scheduledate.php
<?php 
/*
 * @$Header: /var/cvsroot/scheduledate/class.scheduledate.php,v 1.6 2010/02/06 07:47:56 cvs Exp $
 */
/*
    A simple class to schedule a command by date

    Copyright (C) 2009- Giuseppe Lucarelli <hide@address.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of version 2 of the GNU General Public License as
    published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
error_reporting(E_ALL);

/**
 *
    VALID PATTERNS COULD BE (for example):
    "2010/1/6 12:00 "                    // fixed date "2010/01/06 12:00"
    "* * * * * "                         // every year, month, day, hour, minute
    "* * * * 0 "                         // every year, month, day, hour, at '0' minute
    "2009  1-7     *       *       0\n"; // every day and hour of 2009 from january to july; have a little patience, please :)
    "2009  *       *       *       0\n"; // every month, day and hour of 2009; have a little more patience, please :)
    "2009-2010 1 mon,thu,fri 15 51 ";
    "2009-2010 1 2*sun-tue,thu-sat 15 51 ";
    "2009-2010 * *mon,thu 15 51 ";
    "2009 2 3 0 51 ";
    "2009-2010 01,03 3*sun,thu * 51 ";
    "2009-2010 01,03 3*sun 0 51 ";
    "2009-2010 01,03 * 11 51 ";
    "2009-2010 3,12 1,4-7 15 51 ";
    "2009-2010 01-02 10 0-23 51 ";
    "2009-2010 01-02 10 11,23 51 ";
    "2009 3,12 wed-fri 11 51 ";
    "2009 * 1,4-7,9-12 11 51 ";
  *
  * @return true                          existing scheduling pattern and date is greater then now
  * @return false but date is not false   existing scheduling pattern and date is less then now
  * @return false and date is false too   not existing scheduling
  */

class ScheduleDate
{
/* public */
    //var $week = array('dom','lun','mar','mer','gio','ven','sab');
    var $week = array('sun','mon','tue','wed','thu','fri','sat');
    var $dateArray = array('year'=>0,'month'=>0,'day'=>0,'hour'=>0,'minute'=>0);
    var $date = null;
    var $time = null;
    var $firstRun = null;
    var $lastRun = null;
    var $pattern = null;
    var $dayPattern = null;
    var $matches = null;
    var $now = null;

    function BuildDateArray($date) {
        $t = explode(',',strftime("%Y,%m,%d,%H,%M", strtotime($date)));
        $this->dateArray['year'] = $t[0];
        $this->dateArray['month'] = $t[1];
        $this->dateArray['day'] = $t[2];
        $this->dateArray['hour'] = $t[3];
        $this->dateArray['minute'] = $t[4];
    }

    function BuildDate() {
        $this->date = $this->dateArray['year'].'-'.$this->dateArray['month'].'-'.$this->dateArray['day'].' '.
                      $this->dateArray['hour'].':'.$this->dateArray['minute'];
        $this->time = strtotime($this->date);
        return $this->time;
    }

    function Parse(& $pattern, & $matches) {
        preg_match("/([\*0-9]{1,4})(|[\-,].*)[[:blank:]\/\-]+([\*0-9]{1,2})(|[\-,].*)[[:blank:]\/\-]([\*0-9a-z]{1,3}|)(|[\*\-,].*)[[:blank:]\/]([\*0-9]{1,2})(|[\-,].*)[[:blank:]:\-]([\*0-9]{1,2})(|[\-,].*)[[:blank:]:\-\r\n\t$]/i", $pattern."\n", $matches, PREG_OFFSET_CAPTURE);
        if(!$matches || !$matches[0][0]) {
            return false;
        }
        $pattern = $matches[0][0];
        for($i = 2; $i <= 10; $i+=2) {
            if(strlen($matches[$i][0]) > 0) {
                if($matches[$i][0][0] == '-' || $matches[$i][0][0] == ',') {
                    $matches[$i][0] = $matches[$i-1][0].$matches[$i][0];
                }
            } else if(!strcmp($matches[$i-1][0],'*')) {
                $matches[$i][0] = $matches[$i-1][0];
            }
        }
        if(preg_match("/[a-z]/i",$matches[5][0])) {
            if(strlen($matches[6][0]) == 0) {
                $matches[6][0] = $matches[5][0];
            }
            $matches[5][0] = 5;
        } else if(preg_match("/[a-z]/i",$matches[6][0])) {
            if(strlen($matches[5][0]) <= 0) {
                $matches[5][0] = 5;   // if max week days is not specified, set it to max (maybe 6?)
            }
        }
        return true;
    }

    function Explode(& $pattern, $rif, & $field, $needle = ',') {
        $retval = false;

        $list = explode($needle,$pattern);
        $field = false;
        $first = false;
        for($i=0; $i < sizeof($list); $i++) {
             if($pos=strpos($list[$i],'-')) {
                 $from=substr($list[$i],0,$pos);                     // $from=trim(substr($list[$i],0,$pos),"()");
                 if($first === false) $first = $from;
                 $to=substr($list[$i],$pos+1);                       // $to=trim(substr($list[$i],$pos+1),"()");
                 if($rif <= $from) {
                     $field = $from;
                     $retval = true;
                     break;
                 } else if($rif <= $to) {
                     $field = $rif;
                     $retval = true;
                     break;
                 }
             } else if($rif <= $list[$i]) {                          // } else if($rif <= trim($list[$i],"()")) {
                 $field = $list[$i];                                 //     $field = trim($list[$i],"()");
                 $retval = true;
                 break;
             }
             if($first === false) $first = $list[$i];                // if($first === false) $first = trim($list[$i],"()");
        }
        // if no matches, start from first value
        if($field === false)
            $field = $first;
//echo "Exiting [$field]...................\n";
        return $retval;
    }
    
    function GetFirstWeekDay($day) {
        $retval = false;
        $daycounter = array();

        for($i=1; $i <= 31; $i++) {
            $this->dateArray['day'] = $i;
            $d=date("w",strtotime(
                            $this->dateArray['year'].'-'.$this->dateArray['month'].'-'.$i.' '.
                            $this->dateArray['hour'].':'.$this->dateArray['minute']));
            if(!preg_match("/\b".$d."\b/",$this->dayPattern)) {
                continue;
            }
            if(!$daycounter[$d]) {
                $daycounter[$d] = 1;
            } else {
                $daycounter[$d]++;
            }
            if($daycounter[$d] > $this->matches[5][0]) {
                break;
            }
            if($i >= $day) {
                $retval = $i;
                break;
            }
        }
        return $retval;
    }

    function GetLastWeekDay($year,$month,$day,$hour,$minute) {
        $retval = strtotime($year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':00');
        $daycounter = array();

        for($i=31; $i >= 28; $i--) {
            if(checkdate(trim($month,' *,-'),$i,trim($year,' *,-'))) {
                break;
            }
        }
        if($i <= $day) {
            $retval=strtotime("$year-$month-$i $hour:$minute:00");
        }
        if($this->dayPattern === null) {
            return $retval;
        }
        $day = $i;    // max month's day
        for($i=1; $i <= $day; $i++) {
            $d=date("w",strtotime("$year-$month-$i $hour:$minute:00"));
            if(preg_match("/\b".$d."\b/",$this->dayPattern)) {
                if(!$daycounter[$d]) {
                    $daycounter[$d] = 1;
                } else {
                    $daycounter[$d]++;
                }
                if($daycounter[$d] > $this->matches[5][0]) {
                    continue;
                }
                $retval=strtotime("$year-$month-$i $hour:$minute:00");
            }
        }
        return $retval;
    }

    function GetLastToken($field, $pattern, $max) {
        if(preg_match("/[a-z]/i",$pattern)) {
            $this->TransformWeek($pattern);
            $pattern = $this->dayPattern;
        }
        if(!strcmp($field,'*')) {
            $pattern = $max;
        }
        //if($pattern && !strcmp($pattern,'*')) {
            //$pattern = $max;
        //} else {
            //$pattern = $field.$pattern;
        //}
        $token = preg_split('/[,-]/',(strlen($pattern) > 0 ? $pattern : $field));
        for($i=sizeof($token)-1; $i >= 0; $i--) {
            if(strlen($token[$i]) > 0) {
                return $token[$i];
            }
        }
    }

    function TransformWeek($pattern) {
        if($this->dayPattern !== null)
            return;
        $token = trim($pattern,'*');
        for($i=0; $i < sizeof($this->week); $i++) {
            $token = str_replace($this->week[$i],$i,$token);
        }
        $token = preg_split('/([,-])/',$token,-1,PREG_SPLIT_DELIM_CAPTURE);
        for($i=0; $i < sizeof($token); $i++) {
            if(strcmp($token[$i],'-')) {
                $this->dayPattern .= $token[$i];
                continue;
            }
            for($x=$token[$i-1]+1; $x < $token[$i+1]; $x++) {
                $this->dayPattern .= ','.$x;
            }
            $this->dayPattern .= ',';
        }
    }

    function CheckPattern (& $pattern, $rif, & $field, $debug = false) {
        if($debug) {
            echo "\n";
            var_dump($pattern,$rif,$field);
        }
        if(!strcmp($pattern,'*')) {
            $field = $rif;
        } else if(strlen($pattern) == 0) {
            return false;
        } else {
            if($this->Explode($pattern,$rif,$field,',') === false) {
                return false;
            }
        }
        return true;
    }

    // minute
    function BuildMinute() {
        if($this->BuildDate() < $this->now) {
            $d=date("i",strtotime($this->date)+60);
            $this->CheckPattern($this->matches[10][0], $d, $this->dateArray['minute']);
//echo " < min(".date("Y-m-d H:i:s",$this->BuildDate())."); ";
        }
    }
    // hour
    function BuildHour() {
        if($this->BuildDate() < $this->now) {
            $d=date("H",strtotime($this->date)+60*60);
            $this->CheckPattern($this->matches[8][0], $d, $this->dateArray['hour']);
//echo " < hour(".date("Y-m-d H:i:s",$this->BuildDate())."); ";
        }
    }
    // day
    function BuildDay() {
        if($this->BuildDate() < $this->now) {
            if(!preg_match("/[a-z]/i",$this->matches[6][0])) {
                $d=date("d",strtotime($this->date)+24*60*60);
                $this->CheckPattern($this->matches[6][0], $d, $this->dateArray['day']);
                if(checkdate(trim($this->dateArray['month'],' *,-'),
                             trim($this->dateArray['day'],' *,-'),
                             trim($this->dateArray['year'],' *,-')) != true) {
                    $this->dateArray['day'] = $d - 1;
                }
            } else {
                $this->TransformWeek($this->matches[6][0]);
                $d=date("w",strtotime($this->date)) + 1;
                if($d > 6)
                    $d = 0;
                $this->CheckPattern($this->dayPattern, $d, $this->dateArray['day']);
                $this->dateArray['day'] = $this->dateArray['day'] - ($d - 1);
                if($this->dateArray['day'] <= 0) {
                    $this->dateArray['day'] = 7 - ($this->dateArray['day'] * -1);
                }
                $newtime = strtotime($this->date)+$this->dateArray['day']*24*60*60;
                $this->dateArray['day']=date("d",$newtime);
                // check if new week day doesn't exceed limit
                if(($this->dateArray['day']=$this->GetFirstWeekDay($this->dateArray['day'])) === false) {
                    $this->dateArray['day'] = 1;
                } else {
                    // if month changes, day is invalid so reset day to '1' and check for first week day after 'year' building
                    if(date('m',$newtime) != $this->dateArray['month']) {
                        $this->dateArray['day'] = 1;
                    }
                }
            }
//echo " < day(".date("Y-m-d H:i:s",$this->BuildDate())."); ";
        }
    }
    // month
    function BuildMonth() {
        if($this->BuildDate() < $this->now) {
            $d=date('m',$this->now)+1;
            if($d > 12) $d = 1;
            $this->CheckPattern($this->matches[4][0], $d, $this->dateArray['month']);
            if(checkdate(trim($this->dateArray['month'],' *,-'),
                         trim($this->dateArray['day'],' *,-'),
                         trim($this->dateArray['year'],' *,-')) != true) {
                $this->dateArray['month'] = '1';
            }
//echo " < mon(".date("Y-m-d H:i:s",$this->BuildDate())."); ";
        }
    }
    // year
    function BuildYear() {
        if($this->BuildDate() < $this->now) {
            $d = date('Y',$this->now)+1;
            if($this->CheckPattern($this->matches[2][0], $d, $this->dateArray['year']) === false) {
                $this->date = false;
            }
//echo " < year(".date("Y-m-d H:i:s",$this->BuildDate())."); ";
        }
    }

    function GetFirstRun($pattern,$now) {
        $this->pattern = $pattern;
        if(!$this->matches) {
            $this->Parse($pattern,$this->matches);
        }
        if($this->CheckPattern($this->matches[2][0], date('Y',$now), $year) !== true) {
            $year = ($this->matches[1][0] ? $this->matches[1][0] : date('Y',$now));
        }
        if($this->CheckPattern($this->matches[4][0], 1, $month) !== true) {
            $month = ($this->matches[3][0] ? $this->matches[3][0] : 1);
        }
        if(!preg_match("/[a-z]/i",$this->matches[6][0])) {
            if($this->CheckPattern($this->matches[6][0], 1, $day) !== true) {
                $day = ($this->matches[5][0] ? $this->matches[5][0] : 1);
            }
        } else {
            $this->dateArray['year'] = $year;
            $this->dateArray['month'] = $month;
            $this->TransformWeek($this->matches[6][0]);
            $day=$this->GetFirstWeekDay(1);
        }
        if($this->CheckPattern($this->matches[8][0], 0, $hour) !== true) {
            $hour = ($this->matches[7][0] ? $this->matches[7][0] : 0);
        }
        if($this->CheckPattern($this->matches[10][0], 0, $minute) !== true) {
            $minute = ($this->matches[9][0] ? $this->matches[9][0] : 0);
        }
        $this->firstRun = strtotime($year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':00');
        return $this->firstRun;
    }

    function GetLastRun() {
        return $this->GetLastWeekDay(
                   $this->GetLastToken($this->matches[1][0], $this->matches[2][0], 2019),
                   $this->GetLastToken($this->matches[3][0], $this->matches[4][0], 12),
                   $this->GetLastToken($this->matches[5][0], $this->matches[6][0], 31),
                   $this->GetLastToken($this->matches[7][0], $this->matches[8][0], 23),
                   $this->GetLastToken($this->matches[9][0], $this->matches[10][0], 59));
    }

    function Renew($pattern, $date, $timecheck) {
        $retval = false;
        $loop = 365;

        $this->pattern = $pattern;
        //$this->now = $now;
        if(!$this->matches) {
            $this->Parse($this->pattern,$this->matches);
        }
        $this->date = $date;
        $this->BuildDateArray($this->date);
        $this->BuildDate();
        if($this->lastRun === null) {
            $this->lastRun = $this->GetLastRun();
        }
        if($this->lastRun <= $timecheck) {
            $this->date = false;
            $retval = false;
            return $retval;
        }
        while(1) {
            $this->now = strtotime($this->date) + 1; // sum 1 so now is greater then original date
            $this->BuildMinute();
            $this->BuildHour();
            $this->BuildDay();
            $this->BuildMonth();
            $this->BuildYear();
            // check for week day
            if(preg_match("/[a-z]/i",$this->matches[6][0]) && $this->dateArray['day'] == 1) {
                $this->dateArray['day'] = $this->GetFirstWeekDay($this->dateArray['day']);
                $this->BuildDate();
            }
            if($loop-- <= 0) {
                echo nl2br("\nOops! there is an internal error; please send me your pattern at: hide@address.com\n\n");
                $this->date = false;
                $retval = false;
                break;
            }
            if(date('Y',$this->time) == 1970) {
                echo "TIME ERROR ".date("Y-m-d D H:i:s",$this->time);
                break;
            }
            if($this->time > $timecheck) {
                $retval = true;
                break;
            }
        }
        if($retval !== false) {
            $retval = $this->time;
        }
        return $retval;
    }
};
Return current item: pop3ml