Skip to content

Instantly share code, notes, and snippets.

@d4rkne55
Last active July 16, 2020 10:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d4rkne55/9890a7fdcd0ab21dcae306d66fd062bf to your computer and use it in GitHub Desktop.
Save d4rkne55/9890a7fdcd0ab21dcae306d66fd062bf to your computer and use it in GitHub Desktop.
A DateInterval extension class
<?php
/**
* This class brings support for milliseconds, explicit recalculation of carry-over points
* and human-friendly formatting
*/
class DateIntervalEnhanced extends DateInterval
{
public $ms = 0;
private $unitData = array(
'y' => [
'fullUnit' => 'year(s)'
],
'm' => [
'fullUnit' => 'month(s)'
],
'd' => [
'fullUnit' => 'day(s)'
],
'h' => [
'fullUnit' => 'hour(s)',
'max' => 24,
'parent' => 'd'
],
'i' => [
'shortUnit' => 'm',
'fullUnit' => 'minute(s)',
'max' => 60,
'parent' => 'h'
],
's' => [
'fullUnit' => 'second(s)',
'max' => 60,
'parent' => 'i'
],
'ms' => [
'max' => 1000,
'parent' => 's'
]
);
public function __construct($interval_spec) {
if (preg_match('/\.(\d+)S$/', $interval_spec, $matches)) {
$this->ms = (int) str_pad($matches[1], 3, '0');
$interval_spec = str_replace($matches[0], 'S', $interval_spec);
}
parent::__construct($interval_spec);
}
/**
* Recalculates carry-over points for time data (only)
*/
public function recalculateTime() {
$vars = array_slice($this->unitData, 3);
foreach (array_reverse($vars) as $unit => $unitData) {
if ($this->$unit >= $unitData['max']) {
$this->{$unitData['parent']} += floor($this->$unit / $unitData['max']);
$this->$unit %= $unitData['max'];
}
}
}
/**
* Recalculates all carry-over points relative to a given date
*
* @param \DateTime|string $date
*/
public function recalculateForDate($date) {
if (is_string($date)) {
$date = new DateTimeImmutable($date);
}
$dateAfter = $date->add($this);
$date = $date->diff($dateAfter);
$standardUnits = array_slice(array_keys($this->unitData), 0, -1);
foreach ($standardUnits as $unit) {
$this->$unit = $date->$unit;
}
}
/**
* This method formats the data in a human-friendly way
*
* @param int $maxUnits specifies the maximum units it should output, -1 for unlimited
* @param bool $fullUnits whether to display full unit names or abbreviations
* @return string
*/
public function humanFormat($maxUnits = 2, $fullUnits = true) {
$units = array();
foreach ($this->unitData as $unit => $unitData) {
if ($this->$unit > 0) {
$fullUnitName = isset($unitData['fullUnit']) ? $unitData['fullUnit'] : $unit;
$shortUnitName = isset($unitData['shortUnit']) ? $unitData['shortUnit'] : $unit;
if ($fullUnits) {
if ($this->$unit > 1) {
$unitName = preg_replace('/\((\w+)\)$/', '$1', $fullUnitName);
} else {
$unitName = preg_replace('/\(\w+\)$/', '', $fullUnitName);
}
} else {
$unitName = $shortUnitName;
}
$units[] = array(
$this->$unit,
$unitName
);
}
if ($maxUnits != -1 && count($units) >= $maxUnits) {
break;
}
}
$unitNameSeparator = ($fullUnits) ? ' ' : '';
$str = '';
foreach ($units as $i => $unit) {
if ($fullUnits) {
if ($i == count($units) - 1 && $i > 0) {
$str .= ' and ';
} elseif ($i > 0) {
$str .= ', ';
}
} else {
$str .= ' ';
}
$str .= implode($unitNameSeparator, $unit);
}
return trim($str);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment