Skip to content

Instantly share code, notes, and snippets.

@paunin
Last active December 7, 2016 03:18
Show Gist options
  • Select an option

  • Save paunin/04ba2a52c273f5312cce7f5c031d28b5 to your computer and use it in GitHub Desktop.

Select an option

Save paunin/04ba2a52c273f5312cce7f5c031d28b5 to your computer and use it in GitHub Desktop.

Revisions

  1. paunin revised this gist Dec 7, 2016. No changes.
  2. paunin revised this gist Dec 7, 2016. 1 changed file with 14 additions and 4 deletions.
    18 changes: 14 additions & 4 deletions WeekIntervalScheduler.php
    Original file line number Diff line number Diff line change
    @@ -105,6 +105,19 @@ public function getEndWeekTimestamp(): int
    {
    return $this->endWeekTimestamp;
    }

    /**
    * @param int $weekTimestamp
    *
    * @return bool
    */
    public function isWithin(int $weekTimestamp):bool
    {
    return (
    $weekTimestamp >= $this->getStartWeekTimestamp() &&
    $weekTimestamp < $this->getEndWeekTimestamp()
    );
    }
    }

    /**
    @@ -171,8 +184,7 @@ public function getDelay(\DateTime $date): int
    $i = 1;
    foreach ($this->intervals as $interval) {
    if ( // We are in an interval
    $weekTimestamp >= $interval->getStartWeekTimestamp() &&
    $weekTimestamp <= $interval->getEndWeekTimestamp()
    $interval->isWithin($weekTimestamp)
    ) {
    return ($interval->getEndWeekTimestamp() - $weekTimestamp) * (-1);
    } elseif ( // we by passed needed interval
    @@ -188,8 +200,6 @@ public function getDelay(\DateTime $date): int
    }
    $i++;
    }

    return 0;
    }
    }

  3. paunin revised this gist Dec 7, 2016. 1 changed file with 8 additions and 6 deletions.
    14 changes: 8 additions & 6 deletions WeekIntervalScheduler.php
    Original file line number Diff line number Diff line change
    @@ -202,12 +202,14 @@ public function getDelay(\DateTime $date): int
    new DateTimeZone('Asia/Ho_Chi_minh')
    );

    echo $scheduler->getDelay(new \DateTime('Sunday 09:00', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Monday 09:00', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Friday 17:59', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 min to the end -60
    echo $scheduler->getDelay(new \DateTime('Friday 17:59:59', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 sec to the end -1
    echo $scheduler->getDelay(new \DateTime('Friday 17:58', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 2 mins to the end -120
    echo $scheduler->getDelay(new \DateTime('Saturday 10:00', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 day to next week interval 24*60*60 = 86400
    date_default_timezone_set('Asia/Ho_Chi_minh');

    echo $scheduler->getDelay(new \DateTime('Sunday 09:00')).PHP_EOL; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Monday 09:00')).PHP_EOL; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Friday 17:59')).PHP_EOL; // 1 min to the end -60
    echo $scheduler->getDelay(new \DateTime('Friday 17:59:59')).PHP_EOL; // 1 sec to the end -1
    echo $scheduler->getDelay(new \DateTime('Friday 17:58')).PHP_EOL; // 2 mins to the end -120
    echo $scheduler->getDelay(new \DateTime('Saturday 10:00')).PHP_EOL; // 1 day to next week interval 24*60*60 = 86400
    echo $scheduler->getDelay(new \DateTime('now')).PHP_EOL; // ?
    // Check Exceptions::Overlaps in intervals are not allowed
  4. paunin revised this gist Dec 7, 2016. 1 changed file with 129 additions and 36 deletions.
    165 changes: 129 additions & 36 deletions WeekIntervalScheduler.php
    Original file line number Diff line number Diff line change
    @@ -1,13 +1,62 @@
    <?php

    class Interval
    /**
    * Class WeekTimestamp
    */
    final class WeekTimestamp
    {

    const SECONDS_IN_MINUTE = 60;
    const SECONDS_IN_HOUR = self::SECONDS_IN_MINUTE * 60;
    const SECONDS_IN_DAY = self::SECONDS_IN_HOUR * 24;
    const SECONDS_IN_WEEK = self::SECONDS_IN_DAY * 7;

    /**
    * @param DateTime $dateTime
    *
    * @return int amount of seconds since the beginning of the week
    */
    public static function createFromDateTime(\DateTime $dateTime): int
    {
    return self::create(
    intval($dateTime->format('w')),
    intval($dateTime->format('H')),
    intval($dateTime->format('i')),
    intval($dateTime->format('s'))
    );
    }

    /**
    * @param int $weekDay Day of the week 0 - Sunday, 6 - Saturday
    * @param int $hour 0-23
    * @param int $minute 0-59
    * @param int $second 0-59
    *
    * @return int amount of seconds since the beginning of the week
    */
    public static function create(
    int $weekDay,
    int $hour = 0,
    int $minute = 0,
    int $second = 0
    ): int {
    $timestamp =
    $weekDay * self::SECONDS_IN_DAY +
    $hour * self::SECONDS_IN_HOUR +
    $minute * self::SECONDS_IN_MINUTE +
    $second;
    if($timestamp >= self::SECONDS_IN_WEEK){
    throw new \RuntimeException('Can\'t create WeekTimestamp');
    }

    return $timestamp;
    }
    }

    /**
    * Class Interval
    */
    class Interval
    {
    private $startWeekTimestamp;
    private $endWeekTimestamp;

    @@ -30,20 +79,8 @@ public function __construct(
    int $endMinute
    ) {

    if ($startDay < 0 || $startDay > 6) {
    throw new \RuntimeException('Start day should be in interval between 0 and 6');
    }
    if ($endDay < 0 || $endDay > 6) {
    throw new \RuntimeException('End day should be in interval between 0 and 6');
    }
    $startWeekTimestamp =
    $startDay * self::SECONDS_IN_DAY +
    $startHour * self::SECONDS_IN_HOUR +
    $startMinute * self::SECONDS_IN_MINUTE;
    $endWeekTimestamp =
    $endDay * self::SECONDS_IN_DAY +
    $endHour * self::SECONDS_IN_HOUR +
    $endMinute * self::SECONDS_IN_MINUTE;
    $startWeekTimestamp = WeekTimestamp::create($startDay, $startHour, $startMinute);
    $endWeekTimestamp = WeekTimestamp::create($endDay, $endHour, $endMinute);

    if ($startWeekTimestamp >= $endWeekTimestamp) {
    throw new \RuntimeException('Start of interval should be before end of it');
    @@ -68,10 +105,11 @@ public function getEndWeekTimestamp(): int
    {
    return $this->endWeekTimestamp;
    }


    }

    /**
    * Class Scheduler
    */
    class Scheduler
    {
    /**
    @@ -87,7 +125,7 @@ class Scheduler
    /**
    * Scheduler constructor.
    *
    * @param Interval[] $intervals
    * @param Interval[] $intervals
    * @param DateTimeZone $timezone
    */
    public function __construct(array $intervals = [], \DateTimeZone $timezone)
    @@ -100,10 +138,22 @@ public function __construct(array $intervals = [], \DateTimeZone $timezone)
    foreach ($intervals as $interval) {
    $this->intervals[$interval->getStartWeekTimestamp()] = $interval;
    }
    ksort(
    $this->intervals
    ); // we want to make sure intervals are ordered to be able iterate it from the firs to last
    // @todo: Need to check that we don't have overlaps
    // we want to make sure intervals are ordered to be able iterate it from the firs to last
    ksort($this->intervals);

    // we need to check overlaps
    /** @var Interval $previousInterval */
    $previousInterval = null;
    foreach ($this->intervals as $interval) {
    if (
    $previousInterval &&
    $previousInterval->getEndWeekTimestamp() > $interval->getStartWeekTimestamp()
    ) {
    throw new \RuntimeException('Overlaps in intervals are not allowed');
    }

    $previousInterval = $interval;
    }
    }

    /**
    @@ -116,11 +166,7 @@ public function getDelay(\DateTime $date): int
    {
    $date->setTimezone($this->timezone);

    $weekTimestamp =
    intval($date->format('w')) * Interval::SECONDS_IN_DAY +
    intval($date->format('H')) * Interval::SECONDS_IN_HOUR +
    intval($date->format('i')) * Interval::SECONDS_IN_MINUTE +
    intval($date->format('s'));
    $weekTimestamp = WeekTimestamp::createFromDateTime($date);

    $i = 1;
    foreach ($this->intervals as $interval) {
    @@ -138,7 +184,7 @@ public function getDelay(\DateTime $date): int
    ) {
    $timeToFirstInterval = array_values($this->intervals)[0]->getStartWeekTimestamp();

    return Interval::SECONDS_IN_WEEK - $weekTimestamp + $timeToFirstInterval;
    return WeekTimestamp::SECONDS_IN_WEEK - $weekTimestamp + $timeToFirstInterval;
    }
    $i++;
    }
    @@ -147,6 +193,7 @@ public function getDelay(\DateTime $date): int
    }
    }

    //===================================Tests===================================//
    $scheduler = new Scheduler(
    [
    new Interval(0, 10, 0, 0, 18, 0), // Sunday 10:00 - Sunday 18:00 - normal interval
    @@ -155,10 +202,56 @@ public function getDelay(\DateTime $date): int
    new DateTimeZone('Asia/Ho_Chi_minh')
    );

    echo $scheduler->getDelay(new \DateTime('Sunday 09:00', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Monday 09:00', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Friday 17:59', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 min to the end -60
    echo $scheduler->getDelay(new \DateTime('Friday 17:59:59', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 sec to the end -1
    echo $scheduler->getDelay(new \DateTime('Friday 17:58', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 2 mins to the end -120
    echo $scheduler->getDelay(new \DateTime('Saturday 10:00', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 day to next week interval 24*60*60 = 86400
    echo $scheduler->getDelay(new \DateTime('now') . "\n"; // ?
    echo $scheduler->getDelay(new \DateTime('Sunday 09:00', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Monday 09:00', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Friday 17:59', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 min to the end -60
    echo $scheduler->getDelay(new \DateTime('Friday 17:59:59', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 sec to the end -1
    echo $scheduler->getDelay(new \DateTime('Friday 17:58', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 2 mins to the end -120
    echo $scheduler->getDelay(new \DateTime('Saturday 10:00', new DateTimeZone('Asia/Ho_Chi_minh'))).PHP_EOL; // 1 day to next week interval 24*60*60 = 86400
    echo $scheduler->getDelay(new \DateTime('now')).PHP_EOL; // ?
    // Check Exceptions::Overlaps in intervals are not allowed
    try {

    $scheduler = new Scheduler(
    [
    new Interval(0, 10, 0, 0, 18, 0), // Sunday 10:00 - Sunday 18:00 - normal interval
    new Interval(0, 17, 0, 5, 18, 0), // Sunday 17:00 - Friday 18:00 - over midnight
    ],
    new DateTimeZone('Asia/Ho_Chi_minh')
    );
    } catch (\Exception $exception) {
    echo $exception->getMessage().PHP_EOL;
    }

    // Check Exceptions::Overlaps in intervals are not allowed
    try {
    $scheduler = new Scheduler(
    [

    ],
    new DateTimeZone('Asia/Ho_Chi_minh')
    );
    } catch (\Exception $exception) {
    echo $exception->getMessage().PHP_EOL;
    }


    // Check Exceptions::Start of interval should be before end of it
    try {
    $scheduler = new Scheduler(
    [
    new Interval(1, 10, 0, 0, 18, 0), // Sunday 10:00 - Sunday 18:00 - normal interval
    ],
    new DateTimeZone('Asia/Ho_Chi_minh')
    );
    } catch (\Exception $exception) {
    echo $exception->getMessage().PHP_EOL;
    }

    // Check Exceptions::Can't create WeekTimestamp
    try {
    WeekTimestamp::create(8,99,99);
    } catch (\Exception $exception) {
    echo $exception->getMessage().PHP_EOL;
    }
  5. paunin revised this gist Dec 6, 2016. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions WeekIntervalScheduler.php
    Original file line number Diff line number Diff line change
    @@ -128,12 +128,10 @@ public function getDelay(\DateTime $date): int
    $weekTimestamp >= $interval->getStartWeekTimestamp() &&
    $weekTimestamp <= $interval->getEndWeekTimestamp()
    ) {

    return ($interval->getEndWeekTimestamp() - $weekTimestamp) * (-1);
    } elseif ( // we by passed needed interval
    $weekTimestamp < $interval->getStartWeekTimestamp()
    ) {

    return $interval->getStartWeekTimestamp() - $weekTimestamp;
    } elseif ( // Last Interval in the collection, need to get time to the first interval
    count($this->intervals) == $i
  6. paunin revised this gist Dec 6, 2016. 1 changed file with 0 additions and 3 deletions.
    3 changes: 0 additions & 3 deletions WeekIntervalScheduler.php
    Original file line number Diff line number Diff line change
    @@ -104,8 +104,6 @@ public function __construct(array $intervals = [], \DateTimeZone $timezone)
    $this->intervals
    ); // we want to make sure intervals are ordered to be able iterate it from the firs to last
    // @todo: Need to check that we don't have overlaps

    var_dump($this->intervals);
    }

    /**
    @@ -143,7 +141,6 @@ public function getDelay(\DateTime $date): int
    $timeToFirstInterval = array_values($this->intervals)[0]->getStartWeekTimestamp();

    return Interval::SECONDS_IN_WEEK - $weekTimestamp + $timeToFirstInterval;

    }
    $i++;
    }
  7. paunin revised this gist Dec 6, 2016. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion WeekIntervalScheduler.php
    Original file line number Diff line number Diff line change
    @@ -165,4 +165,5 @@ public function getDelay(\DateTime $date): int
    echo $scheduler->getDelay(new \DateTime('Friday 17:59', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 min to the end -60
    echo $scheduler->getDelay(new \DateTime('Friday 17:59:59', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 sec to the end -1
    echo $scheduler->getDelay(new \DateTime('Friday 17:58', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 2 mins to the end -120
    echo $scheduler->getDelay(new \DateTime('Saturday 10:00', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 day to next week interval 24*60*60 = 86400
    echo $scheduler->getDelay(new \DateTime('Saturday 10:00', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 day to next week interval 24*60*60 = 86400
    echo $scheduler->getDelay(new \DateTime('now') . "\n"; // ?
  8. paunin created this gist Dec 6, 2016.
    168 changes: 168 additions & 0 deletions WeekIntervalScheduler.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,168 @@
    <?php

    class Interval
    {

    const SECONDS_IN_MINUTE = 60;
    const SECONDS_IN_HOUR = self::SECONDS_IN_MINUTE * 60;
    const SECONDS_IN_DAY = self::SECONDS_IN_HOUR * 24;
    const SECONDS_IN_WEEK = self::SECONDS_IN_DAY * 7;

    private $startWeekTimestamp;
    private $endWeekTimestamp;

    /**
    * Interval constructor.
    *
    * @param int $startDay of the week 0 - Sunday, 6 - Saturday
    * @param int $startHour 0-23
    * @param int $startMinute 0-59
    * @param int $endDay of the week 0 - Sunday, 6 - Saturday
    * @param int $endHour 0-23
    * @param int $endMinute 0-59
    */
    public function __construct(
    int $startDay,
    int $startHour,
    int $startMinute,
    int $endDay,
    int $endHour,
    int $endMinute
    ) {

    if ($startDay < 0 || $startDay > 6) {
    throw new \RuntimeException('Start day should be in interval between 0 and 6');
    }
    if ($endDay < 0 || $endDay > 6) {
    throw new \RuntimeException('End day should be in interval between 0 and 6');
    }
    $startWeekTimestamp =
    $startDay * self::SECONDS_IN_DAY +
    $startHour * self::SECONDS_IN_HOUR +
    $startMinute * self::SECONDS_IN_MINUTE;
    $endWeekTimestamp =
    $endDay * self::SECONDS_IN_DAY +
    $endHour * self::SECONDS_IN_HOUR +
    $endMinute * self::SECONDS_IN_MINUTE;

    if ($startWeekTimestamp >= $endWeekTimestamp) {
    throw new \RuntimeException('Start of interval should be before end of it');
    }

    $this->startWeekTimestamp = $startWeekTimestamp;
    $this->endWeekTimestamp = $endWeekTimestamp;
    }

    /**
    * @return int
    */
    public function getStartWeekTimestamp(): int
    {
    return $this->startWeekTimestamp;
    }

    /**
    * @return int
    */
    public function getEndWeekTimestamp(): int
    {
    return $this->endWeekTimestamp;
    }


    }

    class Scheduler
    {
    /**
    * @var Interval[]
    */
    private $intervals = [];

    /**
    * @var \DateTimeZone
    */
    private $timezone;

    /**
    * Scheduler constructor.
    *
    * @param Interval[] $intervals
    * @param DateTimeZone $timezone
    */
    public function __construct(array $intervals = [], \DateTimeZone $timezone)
    {
    $this->timezone = $timezone;
    if (!count($intervals)) {
    throw new \RuntimeException('You should have intervals');
    }

    foreach ($intervals as $interval) {
    $this->intervals[$interval->getStartWeekTimestamp()] = $interval;
    }
    ksort(
    $this->intervals
    ); // we want to make sure intervals are ordered to be able iterate it from the firs to last
    // @todo: Need to check that we don't have overlaps

    var_dump($this->intervals);
    }

    /**
    * @param DateTime $date
    *
    * @return int <=0 amount of seconds to the end of current interval,
    * >0 - number of seconds to the next interval
    */
    public function getDelay(\DateTime $date): int
    {
    $date->setTimezone($this->timezone);

    $weekTimestamp =
    intval($date->format('w')) * Interval::SECONDS_IN_DAY +
    intval($date->format('H')) * Interval::SECONDS_IN_HOUR +
    intval($date->format('i')) * Interval::SECONDS_IN_MINUTE +
    intval($date->format('s'));

    $i = 1;
    foreach ($this->intervals as $interval) {
    if ( // We are in an interval
    $weekTimestamp >= $interval->getStartWeekTimestamp() &&
    $weekTimestamp <= $interval->getEndWeekTimestamp()
    ) {

    return ($interval->getEndWeekTimestamp() - $weekTimestamp) * (-1);
    } elseif ( // we by passed needed interval
    $weekTimestamp < $interval->getStartWeekTimestamp()
    ) {

    return $interval->getStartWeekTimestamp() - $weekTimestamp;
    } elseif ( // Last Interval in the collection, need to get time to the first interval
    count($this->intervals) == $i
    ) {
    $timeToFirstInterval = array_values($this->intervals)[0]->getStartWeekTimestamp();

    return Interval::SECONDS_IN_WEEK - $weekTimestamp + $timeToFirstInterval;

    }
    $i++;
    }

    return 0;
    }
    }

    $scheduler = new Scheduler(
    [
    new Interval(0, 10, 0, 0, 18, 0), // Sunday 10:00 - Sunday 18:00 - normal interval
    new Interval(1, 10, 0, 5, 18, 0), // Monday 10:00 - Friday 18:00 - over midnight
    ],
    new DateTimeZone('Asia/Ho_Chi_minh')
    );

    echo $scheduler->getDelay(new \DateTime('Sunday 09:00', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Monday 09:00', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 hour to time 1*60*60 = 3600
    echo $scheduler->getDelay(new \DateTime('Friday 17:59', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 min to the end -60
    echo $scheduler->getDelay(new \DateTime('Friday 17:59:59', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 sec to the end -1
    echo $scheduler->getDelay(new \DateTime('Friday 17:58', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 2 mins to the end -120
    echo $scheduler->getDelay(new \DateTime('Saturday 10:00', new DateTimeZone('Asia/Ho_Chi_minh'))) . "\n"; // 1 day to next week interval 24*60*60 = 86400