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.
<?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
}
/**
* @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
echo $scheduler->getDelay(new \DateTime('now') . "\n"; // ?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment