- Add deployment/ansible/templates/.env.production.j2 used by secrets playbook - Enhance deploy-update.yml to read registry creds from vault or CI - Update production-deploy workflow to pass registry credentials to Ansible - Remove obsolete GitHub-style workflows under .gitea (conflicted naming) Why: make the production pipeline executable end-to-end with Ansible and consistent secrets handling; avoid legacy CI configs interfering.
232 lines
5.6 KiB
PHP
232 lines
5.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Core\ValueObjects;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
final readonly class Duration
|
|
{
|
|
private int $nanoseconds;
|
|
public function __construct(
|
|
float|int $value,
|
|
TimeUnit $unit = TimeUnit::NANOSECOND,
|
|
) {
|
|
if ($value < 0) {
|
|
throw new InvalidArgumentException('Duration cannot be negative');
|
|
}
|
|
|
|
$seconds = $value * $unit->getMultiplierToSeconds();
|
|
|
|
$this->nanoseconds = (int) round($seconds * 1_000_000_000);
|
|
}
|
|
|
|
/**
|
|
* Create Duration directly from nanoseconds for maximum precision
|
|
*/
|
|
public static function fromNanoseconds(int $nanoseconds): self
|
|
{
|
|
return new self($nanoseconds, TimeUnit::NANOSECOND);
|
|
}
|
|
|
|
// Factory Methods
|
|
public static function fromSeconds(float $seconds): self
|
|
{
|
|
return new self($seconds, TimeUnit::SECOND);
|
|
}
|
|
|
|
public static function fromUnit(float $value, TimeUnit $unit): self
|
|
{
|
|
return new self($value, $unit);
|
|
}
|
|
|
|
public static function fromMilliseconds(float $milliseconds): self
|
|
{
|
|
return self::fromUnit($milliseconds, TimeUnit::MILLISECOND);
|
|
}
|
|
|
|
public static function fromMicroseconds(float $microseconds): self
|
|
{
|
|
return self::fromUnit($microseconds, TimeUnit::MICROSECOND);
|
|
}
|
|
|
|
public static function fromMinutes(float $minutes): self
|
|
{
|
|
return self::fromUnit($minutes, TimeUnit::MINUTE);
|
|
}
|
|
|
|
public static function fromHours(float $hours): self
|
|
{
|
|
return self::fromUnit($hours, TimeUnit::HOUR);
|
|
}
|
|
|
|
public static function fromDays(float $days): self
|
|
{
|
|
return self::fromUnit($days, TimeUnit::DAY);
|
|
}
|
|
|
|
public static function between(Timestamp $timestamp, Timestamp $other): self
|
|
{
|
|
return $timestamp->diff($other);
|
|
}
|
|
|
|
// Parse from human-readable strings
|
|
public static function parse(string $value): self
|
|
{
|
|
$value = trim($value);
|
|
|
|
if (preg_match('/^(\d+(?:\.\d+)?)\s*([a-zA-Z]*)$/', $value, $matches)) {
|
|
$number = (float) $matches[1];
|
|
$unitString = $matches[2] ?: 's';
|
|
|
|
$unit = TimeUnit::fromString($unitString);
|
|
|
|
return self::fromUnit($number, $unit);
|
|
}
|
|
|
|
throw new InvalidArgumentException("Invalid duration format: $value");
|
|
}
|
|
|
|
// Conversion Methods
|
|
public function toSeconds(): float
|
|
{
|
|
return $this->nanoseconds / 1_000_000_000;
|
|
}
|
|
|
|
public function toNanoseconds(): int
|
|
{
|
|
return $this->nanoseconds;
|
|
}
|
|
|
|
public function toUnit(TimeUnit $unit, int $precision = 2): float
|
|
{
|
|
$seconds = $this->nanoseconds / 1_000_000_000;
|
|
|
|
return round($seconds / $unit->getMultiplierToSeconds(), $precision);
|
|
}
|
|
|
|
public function toMilliseconds(int $precision = 0): float
|
|
{
|
|
return $this->toUnit(TimeUnit::MILLISECOND, $precision);
|
|
}
|
|
|
|
public function toMicroseconds(int $precision = 0): float
|
|
{
|
|
return $this->toUnit(TimeUnit::MICROSECOND, $precision);
|
|
}
|
|
|
|
public function toMinutes(int $precision = 0): float
|
|
{
|
|
return $this->toUnit(TimeUnit::MINUTE, $precision);
|
|
}
|
|
|
|
public function toHours(int $precision = 0): float
|
|
{
|
|
return $this->toUnit(TimeUnit::HOUR, $precision);
|
|
}
|
|
|
|
// Human-readable format
|
|
public function toHumanReadable(): string
|
|
{
|
|
if ($this->nanoseconds === 0) {
|
|
return '0s';
|
|
}
|
|
|
|
$seconds = $this->nanoseconds / 1_000_000_000;
|
|
$unit = TimeUnit::bestUnitFor($seconds);
|
|
$value = $this->toUnit($unit);
|
|
|
|
return $value . ' ' . $unit->value;
|
|
}
|
|
|
|
// Arithmetic Operations
|
|
public function add(Duration $other): self
|
|
{
|
|
return new self($this->nanoseconds + $other->nanoseconds);
|
|
}
|
|
|
|
public function subtract(Duration $other): self
|
|
{
|
|
$result = $this->nanoseconds - $other->nanoseconds;
|
|
if ($result < 0) {
|
|
throw new InvalidArgumentException('Subtraction would result in negative duration');
|
|
}
|
|
|
|
return new self($result);
|
|
}
|
|
|
|
public function multiply(float $factor): self
|
|
{
|
|
if ($factor < 0) {
|
|
throw new InvalidArgumentException('Factor cannot be negative');
|
|
}
|
|
|
|
return new self((int) round($this->nanoseconds * $factor));
|
|
}
|
|
|
|
// Comparison Methods
|
|
public function equals(Duration $other): bool
|
|
{
|
|
return $this->nanoseconds === $other->nanoseconds;
|
|
}
|
|
|
|
public function greaterThan(Duration $other): bool
|
|
{
|
|
return $this->nanoseconds > $other->nanoseconds;
|
|
}
|
|
|
|
public function lessThan(Duration $other): bool
|
|
{
|
|
return $this->nanoseconds < $other->nanoseconds;
|
|
}
|
|
|
|
// Utility Methods
|
|
public function isZero(): bool
|
|
{
|
|
return $this->nanoseconds === 0;
|
|
}
|
|
|
|
public function isNotZero(): bool
|
|
{
|
|
return !$this->isZero();
|
|
}
|
|
|
|
// Framework Integration
|
|
public function toCacheSeconds(): int
|
|
{
|
|
return (int) ceil($this->nanoseconds / 1_000_000_000);
|
|
}
|
|
|
|
public function toTimeoutSeconds(): int
|
|
{
|
|
return (int) ceil($this->nanoseconds / 1_000_000_000);
|
|
}
|
|
|
|
public function __toString(): string
|
|
{
|
|
return $this->toHumanReadable();
|
|
}
|
|
|
|
// Common constants
|
|
public static function zero(): self
|
|
{
|
|
return new self(0);
|
|
}
|
|
|
|
public static function oneSecond(): self
|
|
{
|
|
return new self(1, TimeUnit::SECOND);
|
|
}
|
|
|
|
public static function oneMinute(): self
|
|
{
|
|
return self::fromMinutes(1);
|
|
}
|
|
|
|
public static function oneHour(): self
|
|
{
|
|
return self::fromHours(1);
|
|
}
|
|
}
|