requestedAt = $requestedAt; $this->respondedAt = $respondedAt; } public static function start(\DateTimeImmutable $requestedAt): self { return new self($requestedAt, null); } public static function completed(\DateTimeImmutable $requestedAt, \DateTimeImmutable $respondedAt): self { return new self($requestedAt, $respondedAt); } public static function now(): self { return new self(new \DateTimeImmutable(), null); } public function complete(\DateTimeImmutable $respondedAt): self { return new self($this->requestedAt, $respondedAt); } public function completeNow(): self { return new self($this->requestedAt, new \DateTimeImmutable()); } public function requestedAt(): \DateTimeImmutable { return $this->requestedAt; } public function respondedAt(): ?\DateTimeImmutable { return $this->respondedAt; } public function durationMs(): ?int { if ($this->respondedAt === null) { return null; } $diff = $this->respondedAt->getTimestamp() - $this->requestedAt->getTimestamp(); return $diff * 1000; } public function isCompleted(): bool { return $this->respondedAt !== null; } public function format(): string { $duration = $this->durationMs(); if ($duration === null) { return 'In progress'; } if ($duration < 1000) { return "{$duration}ms"; } return sprintf('%.2fs', $duration / 1000); } public function toArray(): array { return [ 'requested_at' => $this->requestedAt->format('Y-m-d H:i:s.u'), 'responded_at' => $this->respondedAt?->format('Y-m-d H:i:s.u'), 'duration_ms' => $this->durationMs(), ]; } }