Last active
July 12, 2018 16:49
-
-
Save Radiergummi/991c9629fde500d5006bd21eac455d09 to your computer and use it in GitHub Desktop.
TimeSpan class - a flexible PHP class to handle time calculations
This file contains 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 | |
/** | |
* TimeSpan class | |
* ============== | |
* | |
* Provides a convenience class to handle time amounts and time spans: Ever wondered how to easily figure out how many | |
* hours there are between 09:15:31 and 18:55:01? This class is for you. Going with the example, it's as easy as: | |
* | |
* `echo (new TimeSpan('18:55:01'))->sub('09:15:31')->format('h:m'); // '09:39'` | |
* | |
* The class provides several helper methods to work with time in different formats, as integers, strings or other TimeSpan | |
* instances. Internally, every value coming in is converted to seconds, calculated with the existing seconds and formatted | |
* on demand. Easy stuff, really, but it comes in handy at times (employee statistics for me). | |
* | |
* @author Moritz Friedrich <m@9dev.de> | |
* | |
* @example $timespan = new TimeSpan('13:35:05') // Creates a time span from a string (13:35:05) | |
* @example $timespan = new TimeSpan('13:35') // Creates a time span from a string (13:35:00) | |
* @example $timespan = new TimeSpan('13:1') // Creates a time span from a string, leading zeros or not (13:01:00) | |
* @example $timespan = new TimeSpan('150') // Creates a time span from a string, assumes seconds for single numbers (00:01:30) | |
* @example $timespan = new TimeSpan('02:190:376') // Creates a time span from a string, calculates correctly (05:16:16) | |
* @example $timespan = new TimeSpan(13); // Creates a time span from hours only (13:00:00) | |
* @example $timespan = new TimeSpan(13, 35); // Creates a time span from hours only (13:35:00) | |
* @example $timespan = new TimeSpan(13, 35, 05); // Creates a time span from hours, minutes and seconds (13:35:05) | |
* @example $timespan = new TimeSpan($otherTimeSpan) // Effectively clones a time span | |
*/ | |
class TimeSpan | |
{ | |
/** | |
* Holds the amount of time in seconds | |
* | |
* @var int | |
*/ | |
protected $time = 0; | |
/** | |
* Creates a new TimeSpan from any valid parse argument | |
* | |
* @param string|int|\TimeSpan $timeAmountOrHours | |
* @param int $minutes | |
* @param int $seconds | |
*/ | |
public function __construct($timeAmountOrHours = null, int $minutes = 0, int $seconds = 0) | |
{ | |
if ( ! is_null($timeAmountOrHours)) { | |
$this->time = static::parse($timeAmountOrHours, $minutes, $seconds); | |
} | |
} | |
/** | |
* Parses a time amount | |
* | |
* @param string|int|\TimeSpan $timeAmountOrHours time string, amount of seconds or another TimeSpan instance | |
* @param int $minutes | |
* @param int $seconds | |
* | |
* @return int | |
*/ | |
public static function parse($timeAmountOrHours, int $minutes = 0, int $seconds = 0): int | |
{ | |
if ($timeAmountOrHours instanceof TimeSpan) { | |
return $timeAmountOrHours->asSeconds(); | |
} | |
if (is_int($timeAmountOrHours)) { | |
return $timeAmountOrHours * 3600 + $minutes * 60 + $seconds; | |
} | |
if (is_string($timeAmountOrHours)) { | |
$hours = 0; | |
$minutes = 0; | |
$seconds = 0; | |
switch (substr_count($timeAmountOrHours, ':')) { | |
case 0: | |
$seconds = intval($timeAmountOrHours); | |
break; | |
case 1: | |
list ($hours, $minutes) = array_map('intval', explode(':', $timeAmountOrHours)); | |
break; | |
case 2: | |
list ($hours, $minutes, $seconds) = array_map('intval', explode(':', $timeAmountOrHours)); | |
break; | |
} | |
return $hours * 3600 + $minutes * 60 + $seconds; | |
} | |
} | |
/** | |
* Retrieves the total number of seconds for the time span | |
* | |
* @return int | |
*/ | |
public function asSeconds(): int | |
{ | |
return $this->time; | |
} | |
/** | |
* Calculates the distance between two time spans | |
* | |
* @param \TimeSpan $firstTimeSpan | |
* @param \TimeSpan $secondTimeSpan | |
* | |
* @return \TimeSpan | |
*/ | |
public static function distance(TimeSpan $firstTimeSpan, TimeSpan $secondTimeSpan): TimeSpan | |
{ | |
$firstSeconds = $firstTimeSpan->asSeconds(); | |
$secondSeconds = $secondTimeSpan->asSeconds(); | |
return new TimeSpan(0, 0, max($firstSeconds, $secondSeconds) - min($firstSeconds, $secondSeconds)); | |
} | |
/** | |
* Returns the difference between two time spans | |
* | |
* @param \TimeSpan $timeSpan | |
* | |
* @return \TimeSpan | |
*/ | |
public function diff(TimeSpan $timeSpan): TimeSpan | |
{ | |
return new TimeSpan(0, 0, $this->time - $timeSpan->asSeconds()); | |
} | |
/** | |
* Adds a time amount to the time span | |
* | |
* @param string|int|\TimeSpan $timeAmount | |
* | |
* @return $this | |
*/ | |
public function add($timeAmount): TimeSpan | |
{ | |
$this->time += self::parse($timeAmount); | |
return $this; | |
} | |
/** | |
* Adds seconds to the time span | |
* | |
* @param int $seconds | |
* | |
* @return $this | |
*/ | |
public function addSeconds($seconds): TimeSpan | |
{ | |
$this->time += (int)$seconds; | |
return $this; | |
} | |
/** | |
* Adds minutes to the time span | |
* | |
* @param int $minutes | |
* | |
* @return $this | |
*/ | |
public function addMinutes($minutes): TimeSpan | |
{ | |
$this->time += (int)$minutes * 60; | |
return $this; | |
} | |
/** | |
* Adds hours to the time span | |
* | |
* @param int $hours | |
* | |
* @return $this | |
*/ | |
public function addHours($hours): TimeSpan | |
{ | |
$this->time += (int)$hours * 3600; | |
return $this; | |
} | |
/** | |
* Subtracts a time amount from the time span | |
* | |
* @param string|int|\TimeSpan $timeAmount | |
* | |
* @return $this | |
*/ | |
public function sub($timeAmount): TimeSpan | |
{ | |
$this->time -= self::parse($timeAmount); | |
return $this; | |
} | |
/** | |
* Subtracts seconds from the time span | |
* | |
* @param $seconds | |
* | |
* @return $this | |
*/ | |
public function subSeconds($seconds): TimeSpan | |
{ | |
$this->time -= (int)$seconds; | |
return $this; | |
} | |
/** | |
* Subtracts minutes from the time span | |
* | |
* @param int $minutes | |
* | |
* @return $this | |
*/ | |
public function subMinutes($minutes): TimeSpan | |
{ | |
$this->time -= (int)$minutes * 60; | |
return $this; | |
} | |
/** | |
* Subtracts hours from the time span | |
* | |
* @param int $hours | |
* | |
* @return $this | |
*/ | |
public function subHours($hours): TimeSpan | |
{ | |
$this->time -= (int)$hours * 3600; | |
return $this; | |
} | |
/** | |
* Stringifies the time to the most common format | |
* | |
* @return string | |
*/ | |
public function __toString(): string | |
{ | |
return $this->format('h:m:s'); | |
} | |
/** | |
* Formats the time span according to a format. Available formatting tokens are: | |
* | |
* - h: Hours with leading zeros | |
* - H: Hours without leading zeros | |
* - m: Minutes with leading zeros | |
* - M: Minutes without leading zeros | |
* - s: Seconds with leading zeros | |
* - S: Seconds without leading zeros | |
* - B: Total time in seconds | |
* | |
* Token letters can be escaped using a backslash. | |
* | |
* @param string $formatStr | |
* | |
* @return string | |
* | |
* @example $t->format('h:m:s'); // 03:05:09 | |
* @example $t->format('H:M:S'); // 3:5:9 | |
* @example $t->format('B'); // 11109 | |
* @example $t->format('\H: H, \m: m'); // H: 3, m: 05 | |
*/ | |
public function format($formatStr): string | |
{ | |
$output = ''; | |
$skip = false; | |
if ($this->time < 0) { | |
$unsignedTime = 0 - $this->time; | |
$hours = floor($unsignedTime / 3600); | |
$minutes = floor(($unsignedTime / 60) % 60); | |
$seconds = $unsignedTime % 60; | |
} else { | |
$hours = floor($this->time / 3600); | |
$minutes = floor(($this->time / 60) % 60); | |
$seconds = $this->time % 60; | |
} | |
for ($i = 0; $i < strlen($formatStr); $i++) { | |
if ($skip) { | |
$skip = false; | |
continue; | |
} | |
switch ($formatStr[$i]) { | |
case '\\': | |
$skip = true; | |
break; | |
case 'h': | |
$output .= ($this->isNegative() ? '-' : '') . str_pad($hours, 2, '0', STR_PAD_LEFT); | |
break; | |
case 'H': | |
$output .= ($this->isNegative() ? '-' : '') . $hours; | |
break; | |
case 'm': | |
$output .= str_pad($minutes, 2, '0', STR_PAD_LEFT); | |
break; | |
case 'M': | |
$output .= $minutes; | |
break; | |
case 's': | |
$output .= str_pad($seconds, 2, '0', STR_PAD_LEFT); | |
break; | |
case 'S': | |
$output .= $seconds; | |
break; | |
case 'B': | |
$output .= $this->time; | |
break; | |
default: | |
$output .= $formatStr[$i]; | |
} | |
} | |
return $output; | |
} | |
/** | |
* Checks whether the time is a negative value | |
* | |
* @return bool | |
*/ | |
public function isNegative(): bool | |
{ | |
return $this->time < 0; | |
} | |
/** | |
* Checks if the time is a positive value | |
* | |
* @return bool | |
*/ | |
public function isPositive(): bool | |
{ | |
return $this->time >= 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment