Last active
December 7, 2016 03:18
-
-
Save paunin/04ba2a52c273f5312cce7f5c031d28b5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* 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; | |
/** | |
* 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 | |
) { | |
$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'); | |
} | |
$this->startWeekTimestamp = $startWeekTimestamp; | |
$this->endWeekTimestamp = $endWeekTimestamp; | |
} | |
/** | |
* @return int | |
*/ | |
public function getStartWeekTimestamp(): int | |
{ | |
return $this->startWeekTimestamp; | |
} | |
/** | |
* @return int | |
*/ | |
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() | |
); | |
} | |
} | |
/** | |
* Class Scheduler | |
*/ | |
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; | |
} | |
// 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; | |
} | |
} | |
/** | |
* @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 = WeekTimestamp::createFromDateTime($date); | |
$i = 1; | |
foreach ($this->intervals as $interval) { | |
if ( // We are in an interval | |
$interval->isWithin($weekTimestamp) | |
) { | |
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 WeekTimestamp::SECONDS_IN_WEEK - $weekTimestamp + $timeToFirstInterval; | |
} | |
$i++; | |
} | |
} | |
} | |
//===================================Tests===================================// | |
$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') | |
); | |
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 | |
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment