Skip to content

Instantly share code, notes, and snippets.

@devhammed
Created March 21, 2025 13:02
Show Gist options
  • Save devhammed/6f53ec01e6f596e1d94e0cb71e74a7c1 to your computer and use it in GitHub Desktop.
Save devhammed/6f53ec01e6f596e1d94e0cb71e74a7c1 to your computer and use it in GitHub Desktop.
<?php
/**
* Class Duration
* Represents a time span with microsecond precision, similar to Dart's Duration class.
*/
class Duration
{
/**
* @var int Total duration in microseconds.
*/
private int $microseconds;
/**
* Duration constructor.
*
* @param int $days
* @param int $hours
* @param int $minutes
* @param int $seconds
* @param int $milliseconds
* @param int $microseconds
*/
public function __construct(
int $days = 0,
int $hours = 0,
int $minutes = 0,
int $seconds = 0,
int $milliseconds = 0,
int $microseconds = 0
) {
$this->microseconds =
$days * 86_400_000_000 +
$hours * 3_600_000_000 +
$minutes * 60_000_000 +
$seconds * 1_000_000 +
$milliseconds * 1_000 +
$microseconds;
}
/**
* Create a Duration from a raw microsecond value.
*
* @param int $microseconds
* @return Duration
*/
public static function fromMicroseconds(int $microseconds): Duration
{
return new Duration(microseconds: $microseconds);
}
/**
* @return int Duration in whole days.
*/
public function inDays(): int
{
return intdiv($this->microseconds, 86_400_000_000);
}
/**
* @return int Duration in whole hours.
*/
public function inHours(): int
{
return intdiv($this->microseconds, 3_600_000_000);
}
/**
* @return int Duration in whole minutes.
*/
public function inMinutes(): int
{
return intdiv($this->microseconds, 60_000_000);
}
/**
* @return int Duration in whole seconds.
*/
public function inSeconds(): int
{
return intdiv($this->microseconds, 1_000_000);
}
/**
* @return int Duration in whole milliseconds.
*/
public function inMilliseconds(): int
{
return intdiv($this->microseconds, 1_000);
}
/**
* @return int Duration in microseconds.
*/
public function inMicroseconds(): int
{
return $this->microseconds;
}
/**
* @return bool True if duration is negative.
*/
public function isNegative(): bool
{
return $this->microseconds < 0;
}
/**
* Get the absolute (positive) value of this Duration.
*
* @return Duration
*/
public function abs(): Duration
{
return self::fromMicroseconds(abs($this->microseconds));
}
/**
* Compare this duration to another.
*
* @param Duration $other
* @return int Returns -1, 0, or 1
*/
public function compareTo(Duration $other): int
{
return $this->microseconds <=> $other->microseconds;
}
/**
* String representation of the duration.
*
* @return string
*/
public function toString(): string
{
return (string) $this;
}
/**
* Add another Duration to this one.
*
* @param Duration $other
* @return Duration
*/
public function add(Duration $other): Duration
{
return self::fromMicroseconds($this->microseconds + $other->microseconds);
}
/**
* Subtract another Duration from this one.
*
* @param Duration $other
* @return Duration
*/
public function subtract(Duration $other): Duration
{
return self::fromMicroseconds($this->microseconds - $other->microseconds);
}
/**
* Multiply this duration by a factor.
*
* @param int|float $factor
* @return Duration
*/
public function multiply(int|float $factor): Duration
{
return self::fromMicroseconds((int) round($this->microseconds * $factor));
}
/**
* Integer divide this duration by a divisor.
*
* @param int $divisor
* @return Duration
* @throws \DivisionByZeroError
*/
public function divide(int $divisor): Duration
{
if ($divisor === 0) {
throw new \DivisionByZeroError("Cannot divide by zero.");
}
return self::fromMicroseconds(intdiv($this->microseconds, $divisor));
}
/**
* Check equality with another Duration.
*
* @param Duration $other
* @return bool
*/
public function equals(Duration $other): bool
{
return $this->microseconds === $other->microseconds;
}
/**
* Check if this duration is less than another.
*
* @param Duration $other
* @return bool
*/
public function lessThan(Duration $other): bool
{
return $this->microseconds < $other->microseconds;
}
/**
* Check if this duration is less than or equal to another.
*
* @param Duration $other
* @return bool
*/
public function lessThanOrEqual(Duration $other): bool
{
return $this->microseconds <= $other->microseconds;
}
/**
* Check if this duration is greater than another.
*
* @param Duration $other
* @return bool
*/
public function greaterThan(Duration $other): bool
{
return $this->microseconds > $other->microseconds;
}
/**
* Check if this duration is greater than or equal to another.
*
* @param Duration $other
* @return bool
*/
public function greaterThanOrEqual(Duration $other): bool
{
return $this->microseconds >= $other->microseconds;
}
/**
* Negate the duration (unary -).
*
* @return Duration
*/
public function negate(): Duration
{
return self::fromMicroseconds(-$this->microseconds);
}
/**
* Format the duration as HH:MM:SS.mmmuuu.
*
* @return string
*/
public function __toString(): string
{
$micros = abs($this->microseconds);
$sign = $this->microseconds < 0 ? '-' : '';
$totalSeconds = intdiv($micros, 1_000_000);
$microRemainder = $micros % 1_000_000;
$hours = intdiv($totalSeconds, 3600);
$minutes = intdiv($totalSeconds % 3600, 60);
$seconds = $totalSeconds % 60;
$milliseconds = intdiv($microRemainder, 1_000);
$microseconds = $microRemainder % 1_000;
return sprintf(
"%s%02d:%02d:%02d.%03d%03d",
$sign,
$hours,
$minutes,
$seconds,
$milliseconds,
$microseconds
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment